From 86e5caee1b619752789ba93e22af5314784da785 Mon Sep 17 00:00:00 2001 From: tilakjain619 Date: Wed, 23 Jul 2025 19:08:36 +0530 Subject: [PATCH 01/50] Enhance Crisis Support with GeoIP-based helpline detection and IASP fallback --- .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 145 bytes .../chat_interface.cpython-312.pyc | Bin 0 -> 6174 bytes components/__pycache__/header.cpython-312.pyc | Bin 0 -> 1718 bytes .../__pycache__/sidebar.cpython-312.pyc | Bin 0 -> 16930 bytes components/sidebar.py | 53 +++++++++++++----- core/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 139 bytes core/__pycache__/config.cpython-312.pyc | Bin 0 -> 1393 bytes core/__pycache__/utils.cpython-312.pyc | Bin 0 -> 4232 bytes css/__pycache__/styles.cpython-312.pyc | Bin 0 -> 16574 bytes requirements.txt | 3 +- 10 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 components/__pycache__/__init__.cpython-312.pyc create mode 100644 components/__pycache__/chat_interface.cpython-312.pyc create mode 100644 components/__pycache__/header.cpython-312.pyc create mode 100644 components/__pycache__/sidebar.cpython-312.pyc create mode 100644 core/__pycache__/__init__.cpython-312.pyc create mode 100644 core/__pycache__/config.cpython-312.pyc create mode 100644 core/__pycache__/utils.cpython-312.pyc create mode 100644 css/__pycache__/styles.cpython-312.pyc diff --git a/components/__pycache__/__init__.cpython-312.pyc b/components/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcd4eed1fea040415bedca772d2af12f0d07c176 GIT binary patch literal 145 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 G!~X!7YO8+$ literal 0 HcmV?d00001 diff --git a/components/__pycache__/sidebar.cpython-312.pyc b/components/__pycache__/sidebar.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e428d2aa73f6620fdf294f1ec31e2cc950986f08 GIT binary patch literal 16930 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 SVP=L`ABmPEA`QI)6#IVxhmAe} literal 0 HcmV?d00001 diff --git a/components/sidebar.py b/components/sidebar.py index 6d9aeee..8d911bd 100644 --- a/components/sidebar.py +++ b/components/sidebar.py @@ -2,20 +2,38 @@ import webbrowser from datetime import datetime from core.utils import create_new_conversation, get_current_time - -# Emergency contacts and resources -emergency_resources = { - "Crisis Hotlines": [ +import requests + +# Get the user's country based on their IP address +# This is used to provide localized resources and emergency contacts. +def get_user_country(): + try: + resp = requests.get("https://ipinfo.io/json", timeout=2) + if resp.status_code == 200: + data = resp.json() + return data.get("country", None) + except Exception: + pass + return None + +country_helplines = { + "US": [ "National Suicide Prevention Lifeline: 988", "Crisis Text Line: Text HOME to 741741", "SAMHSA National Helpline: 1-800-662-4357" ], - "International": [ - "India: 9152987821 (AASRA)", - "UK: 116 123 (Samaritans)", - "Australia: 13 11 14 (Lifeline)" + "IN": [ + "AASRA: 9152987821", + "Sneha Foundation: 044-24640050" + ], + "GB": [ + "Samaritans: 116 123" + ], + "AU": [ + "Lifeline: 13 11 14" ] } +IASP_LINK = "https://findahelpline.com/" mental_health_resources_full = { "Depression & Mood Disorders": { @@ -282,11 +300,20 @@ def render_sidebar(): st.markdown("---") with st.expander("☎️ Crisis Support"): - st.markdown("**24/7 Crisis Hotlines:**") - for category, numbers in emergency_resources.items(): - st.markdown(f"**{category}:**") - for number in numbers: - st.markdown(f"• {number}") + # Provide localized helplines based on user's country + user_country = get_user_country() + country_label = user_country if user_country else "your country" + st.markdown("### 🚨 Emergency Help") + if user_country and user_country in country_helplines: + st.markdown(f"**Helplines for {country_label}:**") + for line in country_helplines[user_country]: + st.markdown(f"• {line}") + else: + st.markdown( + f"Couldn't detect a local helpline for {country_label}. [Find help worldwide via IASP]({IASP_LINK})" + ) + + st.markdown("---") with st.expander("ℹ️ About TalkHeal"): st.markdown(""" diff --git a/core/__pycache__/__init__.cpython-312.pyc b/core/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71f37cdfc85fc2b3f2fdb2c352ac7a8fdd6737f8 GIT binary patch literal 139 zcmX@j%ge<81fGu?GC=fW5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!(sH$m2}w=Ph;a`N z&UaQYGK~pI%*pmhP0Wc&&M!)hiI30B%PfhH*DI*}#bJ}1pHiBWYFESx)XE6N#URE< MW=2NFB4!{90KBpu;s5{u literal 0 HcmV?d00001 diff --git a/core/__pycache__/config.cpython-312.pyc b/core/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cbdb0670bd6518363608088a5f727f8e24b2bb4 GIT binary patch literal 1393 zcmah|&u<$=6rS1juGhcfO`KE#Db=VYp^g-{v}#Ez1aczRSZP8e=RmATtF3opue;v0 zX2(rzHON97(o?B8Uo zNyq|QIxRQVZQZ*6G1jfSPRU8t>{1i?9<+ouuuXLfd2w1s`FgG95Xu7krh%D)4{Ew? zU?VB9a7`~^jhfWLEHtpqqGL1J(i={lvKiAhsi|9<%YpZFqPbM3n1!p@u4^X6Rkt|) zn-V{8mciLY&d@Ok0Xm9SgpoNi4EPqe4*~UXgH`c+;ybuD;17b&4;;@`#*&7nA^Mqz z@FC2D%YY90?}gt1WR~NHy#t^U{TF<|$3w6K8h}ptIUIfIQ1~CT^icR3d^>jA9X>&a z!e>HbsQ2MMI1qQhK1|A+Y$BW4$Zh7d^j1z=&+PEl2KAb$-N%ilTEbP%{nGW+wWU2v zcgxLGcHlug8SxBcI~46Xb=yFujq;RWy=s{hxwuF$byH1rM>j2OAnG9A(LJ+NCm8)x z<2FhvOmHa+4iQW$VOuv@Xq3(3S!`pXQ}Y4da13lQWgRy%ggAsrc%VX=gCed`)3K8x ziE*x&R2^hO?0SQM5IVn-xxKrMi{;&HKJTod>o<4(Io&Nf1asltE>&wVb@XZN&F>R$ zKH_}4EiPK%R8XXq{?zpG;*-Vp+)`&MbrkF?vB&X8@iw~lLV2&R&UDrJjym67SUyo# zj-+0E`Y8HmKa%(zKxqy3mGEQbkWkU(_C3$FOF#Eoa^XE)lD?{}5u zwzAxd#Q!>#c>LU*ey}V$JKWfXoM?Bh?$g40#)Ao1^HH`%cb{*>hW^zp@{AII{#UH)q zL0Dufoy}-#pKRXAWe0qH56*ZAKI~kaDQ)+_T6nRPDKde&)~vKaQP*e z{UsWI68Q4duO_s~z=fSH0CyZ?#v}JL>vL^lmG}8B5&kO}^cmnC{KJ W+nc)ZIwHq~R^&_#LzT?)!hZu65LSKw literal 0 HcmV?d00001 diff --git a/core/__pycache__/utils.cpython-312.pyc b/core/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8f04da0c904d78782856403cfe411b4fb89ad0b GIT binary patch literal 4232 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 Sk7uTH-@Je9_FV31Uhm%vax`TC literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt index 067175a..be2430e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ streamlit streamlit_modal google-generativeai -Pillow \ No newline at end of file +Pillow +requests \ No newline at end of file From c7eb9a82b5197d72345ef6e7923b1b2c6998ff01 Mon Sep 17 00:00:00 2001 From: Neermita18 Date: Sat, 26 Jul 2025 20:55:45 +0530 Subject: [PATCH 02/50] UI-Enhancements #39 --- TalkHeal.py | 6 +++++- components/sidebar.py | 16 +++++++++++++--- css/styles.py | 2 +- streamlit.toml | 1 - 4 files changed, 19 insertions(+), 6 deletions(-) delete mode 100644 streamlit.toml diff --git a/TalkHeal.py b/TalkHeal.py index f99cfb6..aa4fdd2 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -93,7 +93,11 @@ def get_tone_prompt(): else: with main_area: render_header() - st.subheader(f"🗣️ Current Chatbot Tone: **{st.session_state['selected_tone']}**") + st.markdown(f""" +
+

🗣️ Current Chatbot Tone: {st.session_state['selected_tone']}

+
+""", unsafe_allow_html=True) render_chat_interface() handle_chat_input(model, system_prompt=get_tone_prompt()) diff --git a/components/sidebar.py b/components/sidebar.py index 208d066..f90f7a7 100644 --- a/components/sidebar.py +++ b/components/sidebar.py @@ -125,9 +125,19 @@ def render_sidebar(): st.session_state.active_conversation = i st.rerun() with col2: - if st.button("🗑️", key=f"delete_{i}", type="primary"): - st.session_state.delete_candidate = i - st.rerun() + if convo["messages"]: + if st.button("🗑️", key=f"delete_{i}", type="primary", use_container_width=True): + st.session_state.delete_candidate = i + st.rerun() + else: + st.button( + "🗑️", + key=f"delete_{i}", + type="primary", + use_container_width=True, + disabled=not convo["messages"] # Disable if it's a new/empty conversation + ) + else: st.warning( diff --git a/css/styles.py b/css/styles.py index b4ee017..525f831 100644 --- a/css/styles.py +++ b/css/styles.py @@ -498,7 +498,7 @@ def apply_custom_css(): .stTextInput > div > div > input, .stTextArea > div > div > textarea {{ background: var(--glass-effect) !important; - background-color: #FFFFD0 !important; + background-color: #FFDDEE !important; border: 2px solid var(--border) !important; border-radius: var(--radius) !important; font-size: 1em !important; diff --git a/streamlit.toml b/streamlit.toml deleted file mode 100644 index 0a23a05..0000000 --- a/streamlit.toml +++ /dev/null @@ -1 +0,0 @@ -GEMINI_API_KEY = "AIzaSyDsLJgA58LvgFtnUdVBLFb08GZQV0wXYjQ" From a15c7ea2173b82748530510b52c03b3ee8989695 Mon Sep 17 00:00:00 2001 From: LayaYeldandi3 <23wh1a12b3@bvrithyderabad.edu.in> Date: Sun, 27 Jul 2025 12:27:11 +0530 Subject: [PATCH 03/50] Added Yoga for Mental Health feature based on mood (GSSoC'25 Level 2) --- core/config.py | 2 +- pages/Yoga.py | 145 +++++++++++++++++++++++++++++++++++++++++++++++++ streamlit.toml | 2 +- 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 pages/Yoga.py diff --git a/core/config.py b/core/config.py index c14aeb9..f92bba2 100644 --- a/core/config.py +++ b/core/config.py @@ -13,7 +13,7 @@ "menu_items": None } -st.set_page_config(**PAGE_CONFIG) +#st.set_page_config(**PAGE_CONFIG) # ---------- Custom Dropdown Style ---------- st.markdown(""" diff --git a/pages/Yoga.py b/pages/Yoga.py new file mode 100644 index 0000000..8d7befb --- /dev/null +++ b/pages/Yoga.py @@ -0,0 +1,145 @@ +import streamlit as st +import json +import os +from streamlit_lottie import st_lottie + +st.set_page_config(page_title="🧘 Yoga for Mental Health", layout="centered") + +def load_lottiefile(filepath: str): + try: + with open(filepath, "r") as f: + return json.load(f) + except FileNotFoundError: + return None + +lottie_yoga = load_lottiefile("assets/yoga_animation.json") + +# --- Load Yoga Data --- +try: + with open(os.path.join("data", "yoga.json"), "r") as f: + yoga_data = json.load(f) +except FileNotFoundError: + yoga_data = {} + +#--CSS-- +st.markdown(""" + +""", unsafe_allow_html=True) + +# --- Animation --- +st.markdown('
', unsafe_allow_html=True) +if lottie_yoga: + st_lottie(lottie_yoga, height=220, key="yoga") +st.markdown('
', unsafe_allow_html=True) + +# --- Title & Description --- +st.markdown("

🧘‍♀️ Yoga for Mental Wellness

", unsafe_allow_html=True) +st.markdown("

Choose your mood and explore a calming yoga asana to support your mind and body.

", unsafe_allow_html=True) + +# --- Dropdown -- +def format_mood(option): + return "Select your mood" if option == "Select your mood" else option + +mood_options = ["Select your mood"] + list(yoga_data.keys()) +selected_mood = st.selectbox( + "🌸 How are you feeling today?", + options=mood_options, + index=0, + format_func=format_mood, + key="mood_selector" +) + +# --- Asana Section --- +if selected_mood != "Select your mood": + asana = yoga_data.get(selected_mood) + if asana: + st.markdown("
", unsafe_allow_html=True) + st.markdown(f"
🧘 {asana.get('sanskrit_name')} ({asana.get('english_name')})
", unsafe_allow_html=True) + st.markdown(f"

💖 {asana.get('benefit')}

", unsafe_allow_html=True) + + with st.expander("📋 Steps to Perform"): + steps = asana.get("steps", []) + if steps: + for i, step in enumerate(steps, 1): + fixed_step = step.replace("–", "–").replace("​", "") + st.markdown(f"
{i}. {fixed_step}
", unsafe_allow_html=True) + else: + st.markdown("
No steps available for this asana.
", unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) diff --git a/streamlit.toml b/streamlit.toml index 0a23a05..a897b4b 100644 --- a/streamlit.toml +++ b/streamlit.toml @@ -1 +1 @@ -GEMINI_API_KEY = "AIzaSyDsLJgA58LvgFtnUdVBLFb08GZQV0wXYjQ" +GEMINI_API_KEY = "AIzaSyDDybpwfASDDntMkTh4qOhjjLxwbC6-8Ww" From 3035d3726a7aa73d1074ea3b691b8782cedf8570 Mon Sep 17 00:00:00 2001 From: LayaYeldandi3 <23wh1a12b3@bvrithyderabad.edu.in> Date: Sun, 27 Jul 2025 13:03:57 +0530 Subject: [PATCH 04/50] Add streamlit-lottie to requirements --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c6beb4f..7a85788 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ streamlit_modal google-generativeai geopy requests -Pillow \ No newline at end of file +Pillow +streamlit-lottie \ No newline at end of file From 5836fb61ab77c1a37be809b55f49af28819c4e3a Mon Sep 17 00:00:00 2001 From: LayaYeldandi3 <23wh1a12b3@bvrithyderabad.edu.in> Date: Sun, 27 Jul 2025 13:11:14 +0530 Subject: [PATCH 05/50] Force added yoga_animation and yoga.json files --- .streamlit/secrets.toml | 1 + assets/yoga_animation.json | 1 + 2 files changed, 2 insertions(+) create mode 100644 .streamlit/secrets.toml create mode 100644 assets/yoga_animation.json diff --git a/.streamlit/secrets.toml b/.streamlit/secrets.toml new file mode 100644 index 0000000..9aa7542 --- /dev/null +++ b/.streamlit/secrets.toml @@ -0,0 +1 @@ +GEMINI_API_KEY = "AIzaSyDsLJgA58LvgFtnUdVBLFb08GZQV0wXYjQ" \ No newline at end of file diff --git a/assets/yoga_animation.json b/assets/yoga_animation.json new file mode 100644 index 0000000..31a96aa --- /dev/null +++ b/assets/yoga_animation.json @@ -0,0 +1 @@ +{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.20","a":"","k":"","d":"","tc":""},"fr":24,"ip":0,"op":168,"w":500,"h":500,"nm":"Relax","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Main_Null","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[250,229.99999999999997,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":84,"s":[250,190,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":168,"s":[250,229.99999999999997,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":169,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Hair_Front","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-0.687,-1.629],[1.152,0.839]],"o":[[0,0],[5.37,12.739],[-20.221,-14.712]],"v":[[-21.906,-85.944],[-23.528,-86.59],[0.666,-64.276]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.176470592618,0.301960796118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0.25,-4],[0,0],[-2.25,3.25],[-2.75,6.5]],"o":[[0,0],[-0.25,4],[0,0],[2.25,-3.25],[2.75,-6.5]],"v":[[-26.5,-88.5],[-40.75,-52.75],[-37.5,-42.75],[-30.292,-54.26],[-22.288,-62.747]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.176470592618,0.301960796118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.25,-0.25],[-7.558,-9.448],[0.394,-21.172],[0,0],[1,1]],"o":[[-2.25,0.25],[30.882,38.618],[-0.312,17.851],[0,0],[-1,-1]],"v":[[-7,-100.75],[-24.75,-90],[22.25,-43.25],[26.75,-48.75],[19.25,-93]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.176470592618,0.301960796118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":-74,"op":170,"st":-74,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Top","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-2,40,0],"ix":2},"a":{"a":0,"k":[133.596,126.196,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-16.736,5.563],[-2.848,-4.49],[1.915,6.169],[2.102,16.813],[2.029,-11.435],[15.736,-10.247],[0,0],[2.53,-7.764],[-0.694,10.927],[-7.901,-6.186],[-10.216,-0.238]],"o":[[10.001,-3.324],[0.652,5.76],[-2.414,-7.778],[0,0],[-19.781,-13.547],[-2.458,-10.963],[-2.314,17.284],[-1.878,5.764],[2.556,-1.073],[11.553,9.046],[10.035,0.234]],"v":[[163.595,143.52],[173.444,119.186],[179.774,167.313],[172.487,128.016],[168.628,155.743],[100.11,154.943],[95.02,130.329],[88.202,165.23],[93.04,118.519],[101.247,141.132],[132.561,142.462]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.9019607843137255,1,0.7803921568627451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.481,-12.015],[-1.24,-4.105],[1.915,6.169],[2.102,16.813],[2.692,-8.613],[2.616,8.637],[0,0],[2.53,-7.764],[-0.83,2.661],[-0.904,4.192],[-3.599,9.582],[-7.764,9.41],[6.69,25.848],[-2.146,-1.931],[-5.384,-6.998]],"o":[[4.522,11.403],[0.867,4.199],[0.806,2.669],[-2.414,-7.778],[0,0],[-2.691,8.614],[-2.616,-8.638],[-2.314,17.284],[-1.878,5.764],[1.276,-4.094],[2.586,-11.992],[5.858,-15.593],[-5.134,19.39],[1.337,1.675],[10.768,9.691],[5.384,6.999]],"v":[[189.923,109.627],[196.845,145.77],[199.944,158.186],[179.774,167.313],[172.487,128.016],[165.623,166.663],[102.809,165.801],[95.02,130.329],[88.202,165.23],[68.29,159.519],[71.497,147.132],[77.311,108.462],[113.23,80.306],[151.406,78.847],[156.586,84.235],[184.581,98.771]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8745098039215686,0.9686274509803922,0.7568627450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":169,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Pant","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-2,40,0],"ix":2},"a":{"a":0,"k":[133.596,126.196,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.742,18.113],[0,0],[-1.615,-5.384],[-2.154,-2.155],[-23.687,-8.614],[27.261,-6.586]],"o":[[0,0],[0,0],[1.616,5.383],[0,0],[0,0],[9.886,-6.086]],"v":[[167.354,183.833],[168.43,187.064],[172.16,194.601],[177.006,208.637],[226.573,211.078],[166.585,213.407]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.384313732386,0.670588254929,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 5","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-8.176,-5.895],[0,0],[0,0],[-1.662,5.37],[0,0],[0,0]],"o":[[-32.426,-7.395],[23.763,-8.406],[2.172,-2.135],[1.662,-5.369],[0,0],[-5.059,23.63]],"v":[[101.272,214.341],[39.442,212.667],[90.282,210.663],[95.754,196.42],[99.05,188.912],[99.405,185.066]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.384313732386,0.670588254929,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.063,-5.663],[0,0],[0,0],[8.595,2.23],[0,0],[-15.074,-0.73],[0,0],[8.032,2.311],[11.778,2.748],[0,0],[-10.899,-3.607]],"o":[[6.312,5.462],[0,0],[0,0],[-8.594,-2.228],[-6.255,-7.775],[15.074,0.73],[-2.846,-5.596],[-29.505,-8.488],[-12.966,-3.026],[0.746,-0.733],[44.531,14.736]],"v":[[116.784,238.484],[123.746,255.436],[82.854,256.53],[36.556,255.875],[23.226,249.596],[40.522,251.591],[119.067,250.042],[111.726,235.309],[69.068,222.447],[47.157,227.163],[70.066,221.584]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.384313732386,0.670588254929,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[13.936,-0.155],[-11.91,-9.191],[-30.977,2.618],[-1.013,16.756],[5.384,-3.229],[8.614,-2.154],[0,0],[0,0],[2.171,-0.158],[9.436,0.885],[5.636,1.039],[5.921,0],[-2.55,-1.37],[-20.169,2.148],[-0.494,-0.369],[14.229,-2.47]],"o":[[6.686,4.345],[0,0],[0,0],[2.691,7.538],[0,0],[-8.613,2.154],[0,0],[0,0],[0,0],[-4.689,-4.24],[-2.117,-0.39],[3.959,-1.032],[10.977,5.897],[14.134,-1.505],[0.718,0.535],[-17.826,3.095]],"v":[[138.66,235.601],[173.006,248.137],[236.073,247.578],[248.609,226.19],[243.975,247.898],[231.342,254.321],[184.043,254.571],[176.756,254.571],[171.939,254.245],[162.66,246.561],[141.335,241.407],[127.608,230.946],[135.62,232.549],[185.675,221.688],[208.137,226.578],[183.89,223.197]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921574354,0.384313732386,0.670588254929,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.383,5.613],[0,0],[-1.615,-5.384],[-2.154,-2.155],[-23.687,-8.614],[-2.692,-7.537],[5.384,-3.229],[8.614,-2.154],[0,0],[0,0],[2.171,-0.158],[9.436,0.885],[5.636,1.039],[5.921,0],[6.562,-5.788],[0,0],[0,0],[8.595,2.23],[0,0],[-2.758,7.514],[0,0],[0,0],[-1.662,5.37],[0,0],[0,0]],"o":[[0,0],[0,0],[1.616,5.383],[0,0],[0,0],[2.691,7.538],[0,0],[-8.613,2.154],[0,0],[0,0],[0,0],[-4.689,-4.24],[-2.117,-0.39],[-5.923,0],[6.312,5.462],[0,0],[0,0],[-8.594,-2.228],[-5.355,-3.278],[2.757,-7.513],[23.763,-8.406],[2.172,-2.135],[1.662,-5.369],[0,0],[22.191,3.63]],"v":[[167.354,183.833],[168.43,187.064],[172.16,194.601],[177.006,208.637],[226.573,211.078],[248.609,226.19],[243.975,247.898],[231.342,254.321],[184.043,254.571],[176.756,254.571],[171.939,254.245],[162.66,246.561],[141.335,241.407],[127.608,230.946],[116.034,239.859],[123.746,255.436],[82.854,256.53],[36.556,255.875],[23.226,249.596],[17.772,228.591],[39.442,212.667],[90.282,210.663],[95.754,196.42],[99.05,188.912],[99.405,185.066]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.498039215803,0.470588237047,0.823529422283,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":169,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Body","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-2,40,0],"ix":2},"a":{"a":0,"k":[133.596,126.196,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-3.25,4.25]],"o":[[0,0],[3.25,-4.25]],"v":[[125.971,51.946],[134.846,50.071]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.666666686535,0.43935328722,0.358169943094,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 8","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.188,0.5],[0,0],[-1.5,0]],"o":[[-2.375,2],[0,0],[1.5,0]],"v":[[134.221,43.196],[126.346,44.633],[129.971,46.133]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.078431375325,0.160784319043,0.215686276555,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.78823530674,0.524025976658,0.429665505886,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 7","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[8,-0.25]],"o":[[0,0],[-8,0.25]],"v":[[148.096,27.571],[137.221,23.071]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.078431375325,0.160784319043,0.215686276555,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 6","np":3,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-7.875,-0.375]],"o":[[0,0],[7.875,0.375]],"v":[[112.346,28.196],[123.471,23.571]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.078431375325,0.160784319043,0.215686276555,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 5","np":3,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.125,0.125],[3.5,0.035],[0.031,-0.031],[-4.188,-0.063]],"o":[[0.125,-0.125],[-3.094,-0.031],[0.438,0.781],[3.812,0.057]],"v":[[148.096,31.008],[142.69,33.227],[137.221,31.258],[142.971,33.946]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078431375325,0.160784319043,0.215686276555,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 4","np":3,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.125,0.125],[3.5,0.035],[0.031,-0.031],[-4.188,-0.063]],"o":[[0.125,-0.125],[-3.094,-0.031],[0.438,0.781],[3.812,0.057]],"v":[[123.596,31.008],[118.19,33.227],[112.721,31.258],[118.471,33.946]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078431375325,0.160784319043,0.215686276555,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 3","np":3,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.938,8],[0,0],[0.438,-5.25],[0,0]],"o":[[-15.688,19.625],[0,0],[9.5,-7.375],[0,0]],"v":[[147.409,56.571],[114.409,56.571],[116.159,73.196],[147.096,71.071]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.827450990677,0.545314967632,0.444552093744,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.481,-12.015],[-1.24,-4.105],[-1.379,-2.422],[-1.58,-1.829],[-6.216,-7.195],[-2.386,-2.355],[-2.153,-1.077],[-2.693,0.538],[-3.768,-1.076],[-1.076,-0.538],[3.769,-1.077],[0,0],[-3.229,0.538],[0,5.384],[-1.986,0.993],[0,0],[-2.153,-0.539],[0,0],[0.538,-1.616],[0,0],[1.616,0],[0,0],[2.153,1.615],[3.768,3.769],[7.537,7.538],[2.154,5.384],[3.769,30.149],[2.692,-8.613],[-1.077,-3.768],[0,0],[-1.615,-5.384],[-2.154,-2.155],[-23.687,-8.614],[-2.692,-7.537],[5.384,-3.229],[8.614,-2.154],[0,0],[0,0],[2.171,-0.158],[0,0],[0.573,0],[5.921,0],[8.076,-1.615],[0,0],[0,0],[8.595,2.23],[0,0],[-2.758,7.514],[0,0],[0,0],[-1.662,5.37],[0,0],[0,0],[2.616,8.637],[0,0],[2.201,-5.364],[7.603,-7.472],[0,0],[2.168,-1.596],[0,0],[1.615,0.014],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.072,-0.546],[0,0],[0,0],[0,0],[3.759,1.11],[-1.081,0.53],[-3.778,1.043],[-2.687,-0.561],[0,0],[-2.244,2.175],[-2.214,2.517],[-6.279,7.14],[-1.214,2.089],[-0.83,2.661],[-0.904,4.192],[-3.599,9.582],[-0.921,16],[6.461,5.384],[-1.077,7.537],[-0.538,6.998],[-10.767,4.307],[-13.948,-10.438],[0.268,-0.433],[0,0],[3.348,-4.872],[0,0],[0.538,-7.537],[-10.767,-9.691],[-5.384,-6.998]],"o":[[4.522,11.403],[0.867,4.199],[0.806,2.669],[1.197,2.099],[6.215,7.196],[2.191,2.536],[2.224,2.195],[0,0],[2.691,-0.538],[3.769,1.078],[1.077,0.539],[-3.768,1.076],[0,0],[0,0],[0,0],[1.076,-0.538],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.615,0],[0,0],[-2.155,-1.616],[0,0],[-7.538,-7.537],[-2.153,-5.383],[0,0],[-2.691,8.614],[0,0],[0,0],[1.616,5.383],[0,0],[0,0],[2.691,7.538],[0,0],[-8.613,2.154],[0,0],[0,0],[0,0],[-0.813,0.056],[-2.153,0],[-5.923,0],[0,0],[0,0],[0,0],[-8.594,-2.228],[-5.355,-3.278],[2.757,-7.513],[23.763,-8.406],[2.172,-2.135],[1.662,-5.369],[0,0],[1.109,-3.759],[-2.616,-8.638],[-4.032,30.114],[-2.2,5.364],[-7.603,7.471],[-3.801,3.735],[-2.167,1.596],[0,0],[-1.615,-0.014],[0,0],[-0.524,-1.62],[0,0],[2.158,-0.519],[0,0],[1.976,1.011],[-0.047,5.383],[3.225,0.567],[0,0],[-3.76,-1.109],[1.082,-0.529],[3.778,-1.044],[2.687,0.563],[2.163,-1.058],[2.406,-2.334],[6.278,-7.14],[1.596,-1.815],[1.401,-2.41],[1.276,-4.094],[2.586,-11.992],[7.155,-19.047],[0,0],[-6.46,-5.383],[1.077,-7.537],[0.539,-6.999],[0,0],[10.397,7.781],[1.768,1.942],[0,0],[-1.152,2.128],[0,0],[-0.538,7.538],[10.768,9.691],[5.384,6.999]],"v":[[189.173,109.627],[195.595,145.77],[198.944,158.186],[201.947,165.971],[206.315,171.743],[224.963,193.328],[231.735,200.758],[238.418,205.906],[241.649,205.906],[252.954,206.982],[263.721,208.598],[260.491,212.905],[254.03,213.443],[259.952,219.365],[260.491,213.981],[262.645,210.213],[263.721,212.905],[266.413,211.29],[265.336,218.827],[266.413,220.981],[256.723,224.749],[254.03,225.287],[245.417,223.134],[241.649,221.519],[229.266,213.443],[200.733,193.523],[184.581,175.219],[172.737,126.766],[164.123,163.913],[167.354,183.833],[168.43,187.064],[171.66,194.601],[176.506,209.137],[226.573,211.828],[248.109,227.44],[242.725,247.898],[230.342,253.821],[184.043,253.821],[176.506,253.821],[171.939,254.245],[171.66,255.436],[163.585,253.282],[146.358,253.821],[124.284,254.359],[123.746,255.436],[82.854,255.53],[36.556,255.125],[24.226,249.096],[19.022,228.591],[40.692,213.167],[90.782,210.913],[95.754,196.42],[99.05,188.912],[100.155,185.691],[103.559,165.801],[95.27,128.579],[83.003,176.927],[66.693,195.09],[37.986,214.759],[25.533,222.726],[21.751,224.308],[13.119,226.386],[10.432,225.824],[0.774,221.971],[1.87,219.827],[0.859,212.28],[3.537,213.919],[4.637,211.236],[6.757,215.024],[7.249,220.412],[13.222,214.542],[6.767,213.947],[3.574,209.612],[14.355,208.092],[25.67,207.113],[28.9,207.142],[35.629,202.053],[42.465,194.682],[61.301,173.261],[65.718,167.527],[68.79,159.769],[72.247,147.382],[79.061,108.462],[116.209,72.929],[110.286,50.856],[102.211,35.782],[104.364,26.63],[114.593,0.25],[147.497,1],[156.781,27.745],[159.364,30.788],[158.201,44.934],[151.24,50.683],[146.896,61.085],[156.586,84.235],[184.581,98.771]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.960784316063,0.629434883595,0.51372551918,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":169,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Hair_Back","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[8,1.25],[0.25,-27.5],[1.25,-16.75],[-35.378,24.973],[7.5,12.25]],"o":[[-8,-1.25],[-0.25,27.5],[-1.157,15.507],[34,-24],[-10.256,-16.752]],"v":[[13.5,-69],[-30.5,-45.5],[-38.25,-1],[47.5,45.5],[29.5,-20.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.015732409433,0.071019984782,0.121568627656,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[12.5,23],[32.452,4.275],[2.156,-12.245],[-1.177,-7.706],[0,-34],[-48.303,31.422],[18.981,16.04]],"o":[[-3.784,-6.962],[-15.097,-1.989],[-10.844,3.505],[5.5,36],[0,15.55],[46.5,-30.25],[-17.75,-15]],"v":[[31.5,-77.25],[-5.452,-105.775],[-38.406,-84.005],[-49,-63.5],[-53.5,1],[47.5,45.5],[46.5,-43.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.745097994804,0.141176000237,0.129411995411,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.039215687662,0.176470592618,0.301960796118,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-66,"op":170,"st":-66,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Halo 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[270,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[75,75,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":-78,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":6,"s":[60]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":90,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":174,"s":[60]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":258,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":342,"s":[60]},{"t":426,"s":[0]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":26,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-113,"op":170,"st":-65,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Halo","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[229.99999999999997,250,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[75,75,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":-2,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":82,"s":[60]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":166,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":250,"s":[60]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":334,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":418,"s":[60]},{"t":502,"s":[0]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":26,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-37,"op":191,"st":11,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":20,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,450,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[243,8]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":84,"s":[186,6]},{"t":168,"s":[243,8]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.282352954149,0.282352954149,0.282352954149,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":169,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Circles - 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":71,"ix":10},"p":{"a":0,"k":[376.898,209.72,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[-80,80,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-167,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-145.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-122.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-87.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-67.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":-39.445,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":-21.189,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":1,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":22.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":45.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":80.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":100.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":128.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":146.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":169,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-167,"op":191,"st":-167,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Circles - 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":71,"ix":10},"p":{"a":0,"k":[376.898,209.72,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[-80,80,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-122,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-100.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-77.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-42.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-22.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":5.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":23.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":46,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":67.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":90.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":125.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":145.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":173.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":191.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":214,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-122,"op":183,"st":-122,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Circles - 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":71,"ix":10},"p":{"a":0,"k":[376.898,209.72,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[-80,80,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-68,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-46.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-23.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":11.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":31.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":59.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":77.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":100,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":121.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":144.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":179.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":199.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":227.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":245.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":268,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-68,"op":179,"st":-68,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Circles - 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":129,"ix":10},"p":{"a":0,"k":[173.913,303.783,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[39,39,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-167,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-145.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-122.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-87.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-67.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":-39.445,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":-21.189,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":1,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":22.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":45.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":80.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":100.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":128.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":146.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":169,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-167,"op":191,"st":-167,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Circles - 8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":129,"ix":10},"p":{"a":0,"k":[173.913,303.783,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[39,39,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-122,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-100.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-77.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-42.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-22.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":5.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":23.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":46,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":67.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":90.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":125.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":145.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":173.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":191.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":214,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-122,"op":183,"st":-122,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Circles - 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":129,"ix":10},"p":{"a":0,"k":[173.913,303.783,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[39,39,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-68,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-46.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-23.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":11.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":31.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":59.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":77.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":100,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":121.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":144.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":179.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":199.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":227.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":245.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":268,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-68,"op":179,"st":-68,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Circles - 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":66,"ix":10},"p":{"a":0,"k":[370.6,138.448,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[-28.000000000000004,28.000000000000004,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-167,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-145.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-122.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-87.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-67.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":-39.445,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":-21.189,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":1,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":22.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":45.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":80.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":100.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":128.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":146.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":169,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-167,"op":191,"st":-167,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Circles - 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":66,"ix":10},"p":{"a":0,"k":[370.6,138.448,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[-28.000000000000004,28.000000000000004,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-122,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-100.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-77.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-42.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-22.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":5.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":23.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":46,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":67.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":90.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":125.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":145.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":173.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":191.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":214,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-122,"op":183,"st":-122,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Circles - 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":66,"ix":10},"p":{"a":0,"k":[370.6,138.448,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[-28.000000000000004,28.000000000000004,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-68,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-46.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-23.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":11.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":31.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":59.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":77.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":100,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":121.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":144.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":179.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":199.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":227.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":245.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":268,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-68,"op":179,"st":-68,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Circles - 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[164.187,108.128,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-167,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-145.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-122.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-87.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-67.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":-39.445,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":-21.189,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":1,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":22.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":45.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":80.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":100.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":128.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":146.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":169,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-167,"op":191,"st":-167,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Circles - 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[164.187,108.128,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-122,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-100.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-77.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":-42.196,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":-22.154,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":5.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":23.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":46,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":67.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":90.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":125.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":145.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":173.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":191.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":214,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-122,"op":183,"st":-122,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"Circles - 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[164.187,108.128,0],"ix":2},"a":{"a":0,"k":[194.187,88.128,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":-68,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":-46.421,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":-23.066,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":11.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":31.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":59.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":77.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"i":{"x":0.575,"y":0.575},"o":{"x":0.167,"y":0.167},"t":100,"s":[232,72],"to":[-49.639,1.418],"ti":[15,-43]},{"i":{"x":0.637,"y":0.638},"o":{"x":0.299,"y":0.299},"t":121.579,"s":[159,168],"to":[-15,43],"ti":[-10,-61]},{"i":{"x":0.656,"y":0.656},"o":{"x":0.315,"y":0.315},"t":144.934,"s":[73,268],"to":[10,61],"ti":[-36,37]},{"i":{"x":0.669,"y":0.669},"o":{"x":0.333,"y":0.333},"t":179.804,"s":[253,316],"to":[36,-37],"ti":[-54.512,7.9]},{"i":{"x":0.685,"y":0.685},"o":{"x":0.345,"y":0.345},"t":199.846,"s":[364,332],"to":[69,-10],"ti":[38,35]},{"i":{"x":0.693,"y":0.693},"o":{"x":0.354,"y":0.354},"t":227.555,"s":[420,208],"to":[-38,-35],"ti":[46,48]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.445,"y":0.445},"t":245.811,"s":[327,152],"to":[-38.548,-40.225],"ti":[70,-2]},{"t":268,"s":[232,72]}],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.5607843137254902,0.7176470588235294,0.3686274509803922,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[233,73],"ix":2},"a":{"a":0,"k":[233,73],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-68,"op":179,"st":-68,"bm":0}],"markers":[{"tm":168,"cm":"1","dr":0}]} \ No newline at end of file From dcf39376ea10a2bdae346f7adb470ad0feb338d8 Mon Sep 17 00:00:00 2001 From: LayaYeldandi3 <23wh1a12b3@bvrithyderabad.edu.in> Date: Sun, 27 Jul 2025 13:16:38 +0530 Subject: [PATCH 06/50] Force added Yoga.json data file --- data/Yoga.json | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 data/Yoga.json diff --git a/data/Yoga.json b/data/Yoga.json new file mode 100644 index 0000000..765d13f --- /dev/null +++ b/data/Yoga.json @@ -0,0 +1,91 @@ +{ + "Anxious": { + "sanskrit_name": "Viparita Karani", + "english_name": "Legs-Up-the-Wall Pose", + "benefit": "Relieves anxiety and calms the nervous system.", + "steps": [ + "Lie on your back and extend your legs up against a wall.", + "Keep your arms relaxed at your sides.", + "Breathe deeply and relax for 5-15 minutes." + ] + }, + "Sad": { + "sanskrit_name": "Balasana", + "english_name": "Child's Pose", + "benefit": "Soothes sadness and encourages inner reflection.", + "steps": [ + "Kneel down and sit back on your heels.", + "Bend forward and stretch your arms in front of you.", + "Rest your forehead on the mat and take deep breaths." + ] + }, + "Stressed": { + "sanskrit_name": "Bhramari Pranayama", + "english_name": "Bee Breath", + "benefit": "Reduces stress and relaxes the mind through calming vibrations.", + "steps": [ + "Sit comfortably with your spine straight.", + "Close your eyes and take a deep breath in.", + "Exhale slowly while making a humming sound like a bee.", + "Repeat this for 5-10 rounds." + ] + }, + "Motivated": { + "sanskrit_name": "Surya Namaskar", + "english_name": "Sun Salutation", + "benefit": "Boosts energy, motivation, and positivity through a full-body flow.", + "steps": [ + "Stand tall in Mountain Pose with hands in prayer.", + "Inhale, raise your arms overhead and arch back slightly.", + "Exhale, fold forward and place your hands on the ground.", + "Inhale, lift your head and chest halfway up.", + "Exhale, step back into a plank and lower to the floor.", + "Inhale into Cobra pose.", + "Exhale into Downward Dog and hold.", + "Inhale, step forward, and repeat the flow for 3-5 rounds." + ] + }, + "Tired": { + "sanskrit_name": "Savasana", + "english_name": "Corpse Pose", + "benefit": "Promotes deep rest and rejuvenation.", + "steps": [ + "Lie flat on your back with legs extended and arms by your sides.", + "Close your eyes and let your entire body relax.", + "Focus on your breath and stay in this pose for 5-10 minutes." + ] + }, + "Angry": { + "sanskrit_name": "Simhasana", + "english_name": "Lion's Breath", + "benefit": "Releases anger and tension through expressive breath.", + "steps": [ + "Sit in a comfortable kneeling position.", + "Inhale deeply through the nose.", + "Exhale forcefully through the mouth while sticking out your tongue and roaring like a lion.", + "Repeat for 5 rounds." + ] + }, + "Lonely": { + "sanskrit_name": "Matsyasana", + "english_name": "Fish Pose", + "benefit": "Opens the heart center and promotes emotional release.", + "steps": [ + "Lie on your back and slide your hands under your hips.", + "Press your forearms and elbows into the ground.", + "Lift your chest and gently drop your head back.", + "Hold for 5 breaths, then relax." + ] + }, + "Joyful": { + "sanskrit_name": "Natarajasana", + "english_name": "Dancer's Pose", + "benefit": "Celebrates balance, grace, and self-expression.", + "steps": [ + "Stand tall and shift your weight to your left foot.", + "Bend your right knee and hold your ankle from behind.", + "Extend your left arm forward and lift your right leg up.", + "Hold for 5-7 breaths, then switch sides." + ] + } +} From eb75a4142b8befd8350a6724164e310facaf325c Mon Sep 17 00:00:00 2001 From: LayaYeldandi3 <23wh1a12b3@bvrithyderabad.edu.in> Date: Sun, 27 Jul 2025 13:30:50 +0530 Subject: [PATCH 07/50] Fixed Yoga.py to load updated Yoga.json correctly --- pages/Yoga.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/Yoga.py b/pages/Yoga.py index 8d7befb..a3d3c0a 100644 --- a/pages/Yoga.py +++ b/pages/Yoga.py @@ -16,7 +16,7 @@ def load_lottiefile(filepath: str): # --- Load Yoga Data --- try: - with open(os.path.join("data", "yoga.json"), "r") as f: + with open(os.path.join("data", "Yoga.json"), "r") as f: yoga_data = json.load(f) except FileNotFoundError: yoga_data = {} From f8e2c90d7e8cfaf259893c6db647dd99bf4c6ddc Mon Sep 17 00:00:00 2001 From: Neermita18 Date: Sun, 27 Jul 2025 14:02:44 +0530 Subject: [PATCH 08/50] add streamlit.toml --- streamlit.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 streamlit.toml diff --git a/streamlit.toml b/streamlit.toml new file mode 100644 index 0000000..9aa7542 --- /dev/null +++ b/streamlit.toml @@ -0,0 +1 @@ +GEMINI_API_KEY = "AIzaSyDsLJgA58LvgFtnUdVBLFb08GZQV0wXYjQ" \ No newline at end of file From ea7b371377d2d9708ee90bce51be382cf35ff202 Mon Sep 17 00:00:00 2001 From: pvedha24 Date: Sun, 27 Jul 2025 19:33:46 +0530 Subject: [PATCH 09/50] Add user profile management module with avatar support - Add profile.py module with complete profile functionality - Integrate profile component in sidebar.py - Update main TalkHeal.py to support profile features - Implement profile picture upload and management - Add user preferences (name, font size) functionality - Fix text input color visibility issue --- TalkHeal.py | 2 + components/profile.py | 353 ++++++++++++++++++++++++++++++++++++++++++ components/sidebar.py | 7 +- 3 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 components/profile.py diff --git a/TalkHeal.py b/TalkHeal.py index f99cfb6..2876716 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -17,6 +17,7 @@ from components.sidebar import render_sidebar from components.chat_interface import render_chat_interface, handle_chat_input from components.emergency_page import render_emergency_page +from components.profile import apply_global_font_size # --- 1. INITIALIZE SESSION STATE --- @@ -41,6 +42,7 @@ st.session_state.selected_tone = "Compassionate Listener" # --- 2. SET PAGE CONFIG --- +apply_global_font_size() # --- 3. APPLY STYLES & CONFIGURATIONS --- diff --git a/components/profile.py b/components/profile.py new file mode 100644 index 0000000..90d8943 --- /dev/null +++ b/components/profile.py @@ -0,0 +1,353 @@ +""" +TalkHeal Profile Management Module + +This module handles user profile functionality including: +- Profile creation and editing +- Profile picture upload and management +- User preferences (name, font size) +- Profile display in sidebar + +Author: TalkHeal Team +Version: 1.0 +""" + +import streamlit as st +import base64 +from datetime import datetime +from io import BytesIO +from PIL import Image + + +def initialize_profile_state(): + """Initialize profile data in session state if not exists""" + if "user_profile" not in st.session_state: + st.session_state.user_profile = { + "name": "", + "profile_picture": None, + "join_date": datetime.now().strftime("%B %Y"), + "font_size": "Medium" + } + + +def get_greeting(): + """Get appropriate greeting based on current time""" + current_hour = datetime.now().hour + if current_hour < 12: + return "Good morning" + elif current_hour < 17: + return "Good afternoon" + else: + return "Good evening" + + +def get_user_initials(name): + """Generate user initials from name""" + if name: + return ''.join([word[0].upper() for word in name.split()[:2]]) + return "TH" + + +def create_default_avatar(initials, size=80): + """Create a default avatar with user initials""" + return st.markdown(f""" +
+ {initials} +
+ """, unsafe_allow_html=True) + + +def handle_profile_picture_upload(uploaded_file): + """Handle profile picture upload and processing""" + if uploaded_file is not None: + try: + # Process and resize image to medium size + image = Image.open(uploaded_file) + # Resize to medium size (200x200) while maintaining aspect ratio + image.thumbnail((200, 200), Image.Resampling.LANCZOS) + + # Convert to base64 for storage + buffered = BytesIO() + image.save(buffered, format="PNG") + img_str = base64.b64encode(buffered.getvalue()).decode() + + # Save to session state + st.session_state.user_profile["profile_picture"] = f"data:image/png;base64,{img_str}" + + st.success("✅ Profile picture uploaded successfully!") + return True + + except Exception as e: + st.error("❌ Error uploading image. Please try a different file.") + return False + + return False + + +def render_profile_header(): + """Render the profile header with picture and greeting""" + profile_data = st.session_state.user_profile + greeting = get_greeting() + + # Profile header section + st.markdown("### 👤 Profile") + + # Profile picture and greeting + col1, col2 = st.columns([1, 2]) + + with col1: + if profile_data["profile_picture"]: + # Display uploaded profile picture with circular shape and medium size + st.markdown(f""" +
+ Profile Picture +
+ """, unsafe_allow_html=True) + else: + # Default avatar with initials + initials = get_user_initials(profile_data["name"]) + create_default_avatar(initials, 80) + + with col2: + if profile_data["name"]: + display_name = profile_data["name"].split()[0] + st.markdown(f"**{greeting}, {display_name}!** 👋") + else: + st.markdown(f"**Welcome to TalkHeal!** 🌟") + st.caption(f"Member since {profile_data['join_date']}") + + +def render_profile_settings(): + """Render the profile settings form""" + profile_data = st.session_state.user_profile + + # Add CSS to fix the text input color issue + st.markdown(""" + + """, unsafe_allow_html=True) + + with st.expander("⚙️ Profile Settings"): + + # Name input + st.markdown("**Your Name**") + new_name = st.text_input( + "Enter your name", + value=profile_data["name"], + key="profile_name_input", + placeholder="Enter your name", + help="Enter your preferred name for personalized interactions" + ) + + # Profile picture upload + st.markdown("**Profile Picture**") + uploaded_file = st.file_uploader( + "Upload a profile picture (Optional)", + type=['png', 'jpg', 'jpeg'], + key="profile_pic_upload", + help="Drag and drop or click to upload. Supported formats: PNG, JPG, JPEG" + ) + + # Handle file upload + handle_profile_picture_upload(uploaded_file) + + # Font size preference + st.markdown("**Font Size**") + font_size = st.selectbox( + "Choose your preferred text size", + ["Small", "Medium", "Large"], + index=["Small", "Medium", "Large"].index(profile_data["font_size"]), + key="font_size_selector", + help="This will change the font size throughout the entire application" + ) + + # Action buttons row + col_save, col_reset = st.columns(2) + + with col_save: + # Save profile button + if st.button("💾 Save Profile", key="save_profile", use_container_width=True, type="primary"): + # Update profile data + profile_data["name"] = new_name.strip() + profile_data["font_size"] = font_size + + # Save to session state + st.session_state.user_profile = profile_data + + # Apply font size globally (you can implement this in your main app) + st.session_state.global_font_size = font_size + + # Success message + st.success("🎉 Profile saved successfully!") + st.balloons() + + # Rerun to update the display + st.rerun() + + with col_reset: + # Reset profile button + if st.button("🔄 Reset All", key="reset_profile", use_container_width=True, type="secondary"): + # Show confirmation dialog + st.session_state.show_reset_confirmation = True + st.rerun() + + # Reset confirmation dialog + if st.session_state.get("show_reset_confirmation", False): + st.warning("⚠️ Are you sure you want to reset all profile settings?") + col_confirm, col_cancel = st.columns(2) + + with col_confirm: + if st.button("✅ Yes, Reset", key="confirm_reset", use_container_width=True, type="primary"): + # Reset to default values + st.session_state.user_profile = { + "name": "", + "profile_picture": None, + "join_date": datetime.now().strftime("%B %Y"), + "font_size": "Medium" + } + # Reset global font size + st.session_state.global_font_size = "Medium" + st.session_state.show_reset_confirmation = False + st.success("🔄 Profile reset successfully!") + st.rerun() + + with col_cancel: + if st.button("❌ Cancel", key="cancel_reset", use_container_width=True): + st.session_state.show_reset_confirmation = False + st.rerun() + + +def render_profile_stats(): + """Render simplified profile statistics""" + profile_data = st.session_state.user_profile + + if profile_data["name"]: + with st.expander("📊 Your TalkHeal Journey"): + col1, col2 = st.columns(2) + + with col1: + st.metric("Conversations", "0", help="Total chat sessions") + + with col2: + st.metric("Days Active", "1", help="Days you've used TalkHeal") + + +def render_profile_section(): + """ + Main function to render the complete profile section + This is the function that should be imported and called in sidebar + """ + # Initialize profile state + initialize_profile_state() + + # Render profile components + render_profile_header() + render_profile_settings() + render_profile_stats() + + # Add separator + st.markdown("---") + + +# Optional: Helper functions for other parts of the app +def get_user_name(): + """Get the current user's name""" + if "user_profile" in st.session_state: + return st.session_state.user_profile.get("name", "") + return "" + + +def get_user_font_size(): + """Get the current user's preferred font size""" + if "global_font_size" in st.session_state: + return st.session_state.global_font_size + elif "user_profile" in st.session_state: + return st.session_state.user_profile.get("font_size", "Medium") + return "Medium" + + +def apply_global_font_size(): + """ + Apply the user's font size preference globally across the application. + Call this function in your main app to apply font size changes. + """ + font_size = get_user_font_size() + + # Font size mappings + font_sizes = { + "Small": "14px", + "Medium": "16px", + "Large": "18px" + } + + selected_size = font_sizes.get(font_size, "16px") + + # Apply CSS to change font size globally + st.markdown(f""" + + """, unsafe_allow_html=True) + + +def get_user_profile_picture(): + """Get the current user's profile picture""" + if "user_profile" in st.session_state: + return st.session_state.user_profile.get("profile_picture", None) + return None \ No newline at end of file diff --git a/components/sidebar.py b/components/sidebar.py index 208d066..bf79dd8 100644 --- a/components/sidebar.py +++ b/components/sidebar.py @@ -3,6 +3,8 @@ from datetime import datetime from core.utils import create_new_conversation, get_current_time from core.theme import get_current_theme, toggle_theme, set_palette, PALETTES +from components.profile import initialize_profile_state, render_profile_section + # --- Structured Emergency Resources --- GLOBAL_RESOURCES = [ @@ -74,7 +76,10 @@ def render_sidebar(): """Renders the left and right sidebars.""" - + + with st.sidebar: + # === PROFILE SECTION (Now imported from profile.py) === + render_profile_section() with st.sidebar: st.markdown("### 💬 Conversations") if "show_quick_start_prompts" not in st.session_state: From 50863e1cb497af2309b46ad12751bb6fad6674e2 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Mon, 28 Jul 2025 00:10:15 +0530 Subject: [PATCH 10/50] Update sidebar.py --- components/sidebar.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/sidebar.py b/components/sidebar.py index e66e115..5c0bafe 100644 --- a/components/sidebar.py +++ b/components/sidebar.py @@ -78,9 +78,7 @@ def render_sidebar(): """Renders the left and right sidebars.""" with st.sidebar: - # === PROFILE SECTION (Now imported from profile.py) === render_profile_section() - with st.sidebar: st.markdown("### 💬 Conversations") if "show_quick_start_prompts" not in st.session_state: st.session_state.show_quick_start_prompts = False @@ -430,4 +428,4 @@ def render_sidebar(): *"It's absolutely okay not to be okay :)"* 📅 Enhanced Version - May 2025 - """) \ No newline at end of file + """) From 4bc3f06f172ee73d94dcaafc50f4d4cf0ad5ab7c Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Mon, 28 Jul 2025 11:35:37 +0530 Subject: [PATCH 11/50] Update Yoga.py --- pages/Yoga.py | 241 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 181 insertions(+), 60 deletions(-) diff --git a/pages/Yoga.py b/pages/Yoga.py index a3d3c0a..697d9a7 100644 --- a/pages/Yoga.py +++ b/pages/Yoga.py @@ -1,6 +1,7 @@ import streamlit as st import json import os +import base64 from streamlit_lottie import st_lottie st.set_page_config(page_title="🧘 Yoga for Mental Health", layout="centered") @@ -12,6 +13,16 @@ def load_lottiefile(filepath: str): except FileNotFoundError: return None +# Function to encode image to base64 +def get_base64_of_bin_file(bin_file): + try: + with open(bin_file, 'rb') as f: + data = f.read() + return base64.b64encode(data).decode() + except FileNotFoundError: + st.error(f"Background image not found at {bin_file}. Please check the path.") + return "" + lottie_yoga = load_lottiefile("assets/yoga_animation.json") # --- Load Yoga Data --- @@ -21,39 +32,82 @@ def load_lottiefile(filepath: str): except FileNotFoundError: yoga_data = {} -#--CSS-- -st.markdown(""" +background_image_path = "blue.png" +base64_background_image = get_base64_of_bin_file(background_image_path) + +# --- Custom CSS --- +st.markdown(f""" """, unsafe_allow_html=True) @@ -127,19 +250,17 @@ def format_mood(option): # --- Asana Section --- if selected_mood != "Select your mood": - asana = yoga_data.get(selected_mood) - if asana: - st.markdown("
", unsafe_allow_html=True) - st.markdown(f"
🧘 {asana.get('sanskrit_name')} ({asana.get('english_name')})
", unsafe_allow_html=True) - st.markdown(f"

💖 {asana.get('benefit')}

", unsafe_allow_html=True) - - with st.expander("📋 Steps to Perform"): - steps = asana.get("steps", []) - if steps: - for i, step in enumerate(steps, 1): - fixed_step = step.replace("–", "–").replace("​", "") - st.markdown(f"
{i}. {fixed_step}
", unsafe_allow_html=True) - else: - st.markdown("
No steps available for this asana.
", unsafe_allow_html=True) - - st.markdown("
", unsafe_allow_html=True) + st.markdown("
", unsafe_allow_html=True) + st.markdown(f"
🧘 {asana.get('sanskrit_name')} ({asana.get('english_name')})
", unsafe_allow_html=True) + st.markdown(f"

💖 {asana.get('benefit')}

", unsafe_allow_html=True) + + with st.expander("📋 Steps to Perform"): + steps = asana.get("steps", []) + if steps: + for i, step in enumerate(steps, 1): + fixed_step = step.replace("–", "–").replace("​", "") + st.markdown(f"
{i}. {fixed_step}
", unsafe_allow_html=True) + else: + st.markdown("
No steps available for this asana.
", unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) From 4a5ded9874e7a5af520ef7c9fd110a5bcbfd0eac Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Mon, 28 Jul 2025 11:41:28 +0530 Subject: [PATCH 12/50] Update Yoga.py --- pages/Yoga.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/pages/Yoga.py b/pages/Yoga.py index 697d9a7..f26e38f 100644 --- a/pages/Yoga.py +++ b/pages/Yoga.py @@ -32,7 +32,7 @@ def get_base64_of_bin_file(bin_file): except FileNotFoundError: yoga_data = {} -background_image_path = "blue.png" +background_image_path = "lavender.png" base64_background_image = get_base64_of_bin_file(background_image_path) # --- Custom CSS --- @@ -250,17 +250,21 @@ def format_mood(option): # --- Asana Section --- if selected_mood != "Select your mood": - st.markdown("
", unsafe_allow_html=True) - st.markdown(f"
🧘 {asana.get('sanskrit_name')} ({asana.get('english_name')})
", unsafe_allow_html=True) - st.markdown(f"

💖 {asana.get('benefit')}

", unsafe_allow_html=True) - - with st.expander("📋 Steps to Perform"): - steps = asana.get("steps", []) - if steps: - for i, step in enumerate(steps, 1): - fixed_step = step.replace("–", "–").replace("​", "") - st.markdown(f"
{i}. {fixed_step}
", unsafe_allow_html=True) - else: - st.markdown("
No steps available for this asana.
", unsafe_allow_html=True) - - st.markdown("
", unsafe_allow_html=True) + asana = yoga_data.get(selected_mood) + if asana: + st.markdown("
", unsafe_allow_html=True) + st.markdown(f"
🧘 {asana.get('sanskrit_name')} ({asana.get('english_name')})
", unsafe_allow_html=True) + st.markdown(f"

💖 {asana.get('benefit')}

", unsafe_allow_html=True) + + with st.expander("📋 Steps to Perform"): + steps = asana.get("steps", []) + if steps: + for i, step in enumerate(steps, 1): + fixed_step = step.replace("–", "–").replace("​", "") + st.markdown(f"
{i}. {fixed_step}
", unsafe_allow_html=True) + else: + st.markdown("
No steps available for this asana.
", unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) + else: + st.warning(f"No yoga asana found for '{selected_mood}'. Please select another mood.") From dd12b6fee7e2a0c278ed4a5b1cccf189bf18041f Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Mon, 28 Jul 2025 11:48:54 +0530 Subject: [PATCH 13/50] Update Yoga.py --- pages/Yoga.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pages/Yoga.py b/pages/Yoga.py index f26e38f..e6fd06f 100644 --- a/pages/Yoga.py +++ b/pages/Yoga.py @@ -96,14 +96,16 @@ def get_base64_of_bin_file(bin_file): }} [data-testid="stSidebar"] {{ - background-color: rgba(253, 208, 232, 0.7) !important; - border-right: 2px solid rgba(245, 167, 208, 0.8) !important; - backdrop-filter: blur(10px); + background-color: rgba(253, 208, 232, 0.4) !important; + border-right: 2px solid rgba(245, 167, 208, 0.6) !important; + backdrop-filter: blur(12px) brightness(1.1) !important; + box-shadow: 4px 0 24px rgba(0,0,0,0.15) !important; }} header[data-testid="stHeader"] {{ - background-color: rgba(255, 230, 242, 0.7) !important; - backdrop-filter: blur(5px); + background-color: rgba(255, 230, 242, 0.4) !important; + backdrop-filter: blur(8px) brightness(1.1) !important; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); }} hr, div[role="separator"], [data-testid="stHorizontalBlock"], @@ -145,10 +147,12 @@ def get_base64_of_bin_file(bin_file): div[data-testid="stSelectbox"] > div:first-child > div {{ color: #4a148c !important; font-style: italic !important; - background-color: rgba(255, 240, 246, 0.7) !important; - border: 1px solid rgba(245, 167, 208, 0.8) !important; + background-color: rgba(255, 255, 255, 0.2) !important; + border: 1px solid rgba(255, 255, 255, 0.4) !important; border-radius: 12px; padding: 0.75rem 1rem; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + backdrop-filter: blur(5px) brightness(1.05); }} .stSelectbox input {{ From 9c5b7ff9ba52773eae98b21e8be8680aa2a4150c Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Mon, 28 Jul 2025 12:09:32 +0530 Subject: [PATCH 14/50] Update Yoga.py --- pages/Yoga.py | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/pages/Yoga.py b/pages/Yoga.py index e6fd06f..270aca2 100644 --- a/pages/Yoga.py +++ b/pages/Yoga.py @@ -145,13 +145,13 @@ def get_base64_of_bin_file(bin_file): }} div[data-testid="stSelectbox"] > div:first-child > div {{ - color: #4a148c !important; + color: #4a148c !important; font-style: italic !important; background-color: rgba(255, 255, 255, 0.2) !important; border: 1px solid rgba(255, 255, 255, 0.4) !important; border-radius: 12px; padding: 0.75rem 1rem; - box-shadow: 0 2px 5px rgba(0,0,0,0.1); + box-shadow: 0 2px 5px rgba(0,0,0,0.1); backdrop-filter: blur(5px) brightness(1.05); }} @@ -159,11 +159,36 @@ def get_base64_of_bin_file(bin_file): pointer-events: none !important; caret-color: transparent !important; user-select: none !important; - background-color: rgba(255, 240, 246, 0.7) !important; + background-color: transparent !important; + color: #4a148c !important; + font-weight: 500; +}} + +div[data-baseweb="popover"] > div > ul {{ + background-color: rgba(255, 255, 255, 0.6) !important; + border: 1px solid rgba(255, 255, 255, 0.8) !important; + border-radius: 12px; + backdrop-filter: blur(10px) brightness(1.05) !important; + box-shadow: 0 8px 30px rgba(0,0,0,0.2) !important; +}} + +div[data-baseweb="popover"] li {{ color: #4a148c !important; + font-weight: 500; + transition: background-color 0.2s ease; +}} + +div[data-baseweb="popover"] li:hover {{ + background-color: rgba(255, 240, 246, 0.8) !important; + color: #4a148c !important; +}} + +div[data-baseweb="popover"] li[aria-selected="true"] {{ + background-color: rgba(184, 51, 162, 0.8) !important; + color: white !important; + font-weight: bold !important; }} -/* Asana Info Box */ div[style*="background-color: #fff0f6"] {{ background-color: rgba(255, 240, 246, 0.7) !important; padding: 1.2rem; @@ -174,7 +199,6 @@ def get_base64_of_bin_file(bin_file): backdrop-filter: blur(8px); }} -/* Asana Names */ div[style*="font-size: 24px"] {{ color: #4a148c !important; font-weight: bold; From f3aea35599af4d1d075a0f75df3a0b7a3e8341c1 Mon Sep 17 00:00:00 2001 From: Sharanya Date: Mon, 28 Jul 2025 20:29:05 +0530 Subject: [PATCH 15/50] Added breathing exercise to components folder --- components/Breathing_Exercise.py | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 components/Breathing_Exercise.py diff --git a/components/Breathing_Exercise.py b/components/Breathing_Exercise.py new file mode 100644 index 0000000..d9f9db6 --- /dev/null +++ b/components/Breathing_Exercise.py @@ -0,0 +1,60 @@ +import streamlit as st +import time + +def breathing_exercise(): + st.markdown("

🧘 Breathing Exercise

", unsafe_allow_html=True) + + st.markdown("### 👇 Follow the animation to breathe in and out") + st.write("Use this simple breathing exercise to relax. Follow the circle expanding and contracting.") + + circle_animation = """ + + +
+ """ + st.markdown(circle_animation, unsafe_allow_html=True) + + breath_text = st.empty() + + if st.button("🌀 Start Breathing"): + for _ in range(3): + breath_text.markdown("## 🌬️ Breathe In...") + time.sleep(4) + breath_text.markdown("## ✋ Hold...") + time.sleep(2) + breath_text.markdown("## 😮‍💨 Breathe Out...") + time.sleep(4) + breath_text.markdown("### ✅ Done! Feel better?") + + with st.expander("🕒 Need a Timer?"): + minutes = st.slider("How many minutes do you want to do this?", 1, 10, 2) + if st.button("Start Timer"): + st.success("Relax and follow the animation...") + timer_placeholder = st.empty() + for i in range(minutes * 60, 0, -1): + mins, secs = divmod(i, 60) + timer_text = f"{mins:02d}:{secs:02d}" + timer_placeholder.markdown(f"## ⏳ {timer_text}") + time.sleep(1) + timer_placeholder.markdown("### ✅ Timer complete!") + +if __name__ == "__main__": + breathing_exercise() From 90969898b38f01bb393bdb5d327e47d00b14dd1e Mon Sep 17 00:00:00 2001 From: srishti1837 Date: Tue, 29 Jul 2025 11:58:33 +0530 Subject: [PATCH 16/50] Add journaling feature to TalkHeal --- components/sidebar.py | 11 ++++++- pages/Journaling.py | 70 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 pages/Journaling.py diff --git a/components/sidebar.py b/components/sidebar.py index 5c0bafe..e684be2 100644 --- a/components/sidebar.py +++ b/components/sidebar.py @@ -76,10 +76,17 @@ def render_sidebar(): """Renders the left and right sidebars.""" - + with st.sidebar: render_profile_section() + + st.markdown("### 📂 Explore") + st.page_link("pages/Journaling.py", label="📝 Journaling", use_container_width=True) + st.page_link("pages/Yoga.py", label="🧘 Yoga", use_container_width=True) + st.markdown("---") + st.markdown("### 💬 Conversations") + if "show_quick_start_prompts" not in st.session_state: st.session_state.show_quick_start_prompts = False if "pre_filled_chat_input" not in st.session_state: @@ -91,6 +98,7 @@ def render_sidebar(): create_new_conversation() st.session_state.show_quick_start_prompts = True st.rerun() + if st.session_state.show_quick_start_prompts: st.markdown("---") st.markdown("**Start with a common topic:**") @@ -111,6 +119,7 @@ def render_sidebar(): st.markdown("---") + if st.session_state.conversations: if "delete_candidate" not in st.session_state: for i, convo in enumerate(st.session_state.conversations): diff --git a/pages/Journaling.py b/pages/Journaling.py new file mode 100644 index 0000000..be5b2ff --- /dev/null +++ b/pages/Journaling.py @@ -0,0 +1,70 @@ +import streamlit as st +from datetime import datetime +import os +import json +import google.generativeai as genai + +# ---- Gemini API Setup ---- +genai.configure(api_key=st.secrets["GEMINI_API_KEY"]) +model = genai.GenerativeModel("models/gemini-1.5-flash") + +# ---- File Setup ---- +JOURNAL_DIR = "journal_entries" +os.makedirs(JOURNAL_DIR, exist_ok=True) + +# Get user ID (customize this logic for actual user login) +user_id = st.session_state.get("user_id", "default_user") +user_file = os.path.join(JOURNAL_DIR, f"{user_id}_journal.json") + +# Load past entries +if os.path.exists(user_file): + with open(user_file, "r") as f: + journal_entries = json.load(f) +else: + journal_entries = [] + +# ---- UI ---- +st.title("📝 Journaling") +st.markdown("Reflect on your thoughts. Gemini will help you analyze your emotional tone.") + +# New entry input +st.markdown("### ✍️ New Entry") +entry = st.text_area("What's on your mind today?", height=200) + +if st.button("Save Entry", type="primary"): + if entry.strip(): + # Add entry + journal_entries.append({ + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "content": entry.strip() + }) + with open(user_file, "w") as f: + json.dump(journal_entries, f, indent=2) + st.success("Journal entry saved.") + st.experimental_rerun() + else: + st.warning("Please write something before saving.") + +# ---- View Past Entries ---- +if journal_entries: + st.markdown("### 📖 Your Journal History") + for i, item in enumerate(reversed(journal_entries)): + with st.expander(item["timestamp"]): + st.markdown(item["content"]) + + if st.button(f"Analyze Emotional Tone #{i}", key=f"analyze_{i}"): + with st.spinner("Analyzing with Gemini..."): + prompt = f""" +You are a helpful emotional assistant. Analyze the emotional tone of the following journal entry. +Provide a 1-line mood summary and then a short encouraging message. + +Journal Entry: {item['content']} + """ + try: + response = model.generate_content(prompt) + st.markdown("🧠 **Gemini Insight:**") + st.markdown(response.text) + except Exception as e: + st.error(f"Error: {e}") +else: + st.info("No journal entries yet.") From 5b6be88e2b76a76899799effd121c028f19f0f8e Mon Sep 17 00:00:00 2001 From: eccentriccoder01 Date: Tue, 29 Jul 2025 12:34:47 +0530 Subject: [PATCH 17/50] Update --- README.md | 54 ++++++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index a6989d3..1bfe1f9 100644 --- a/README.md +++ b/README.md @@ -56,29 +56,26 @@ ## 📊 Project Stats -
+

+ +[![Open Source Love svg1](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/) +![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-brightgreen.svg?style=flat) +![Visitors](https://api.visitorbadge.io/api/Visitors?path=eccentriccoder01%2FTalkHeal%20&countColor=%23263759&style=flat) +![GitHub Forks](https://img.shields.io/github/forks/eccentriccoder01/TalkHeal) +![GitHub Repo Stars](https://img.shields.io/github/stars/eccentriccoder01/TalkHeal) +![GitHub Contributors](https://img.shields.io/github/contributors/eccentriccoder01/TalkHeal) +![GitHub Last Commit](https://img.shields.io/github/last-commit/eccentriccoder01/TalkHeal) +![GitHub Repo Size](https://img.shields.io/github/repo-size/eccentriccoder01/TalkHeal) +![GitHub Total Lines](https://sloc.xyz/github/eccentriccoder01/TalkHeal) +![Github](https://img.shields.io/github/license/eccentriccoder01/TalkHeal) +![GitHub Issues](https://img.shields.io/github/issues/eccentriccoder01/TalkHeal) +![GitHub Closed Issues](https://img.shields.io/github/issues-closed-raw/eccentriccoder01/TalkHeal) +![GitHub Pull Requests](https://img.shields.io/github/issues-pr/eccentriccoder01/TalkHeal) +![GitHub Closed Pull Requests](https://img.shields.io/github/issues-pr-closed/eccentriccoder01/TalkHeal) +

+
--- @@ -104,14 +101,11 @@ For a detailed walkthrough of TalkHeal's features and how to use them, check out ## 🛠️ Technologies Used -| Tech | Purpose | -| --------------------- | --------------------------------- | -| **Python** | Core backend and AI logic | -| **Streamlit** | UI and frontend integration | -| **Google Gemini API** | Generative AI Conversations | -| **Session State** | Manage multi-threaded chat memory | -| **CSS Variables** | Theming and modern styling | -| **Streamlit Secrets** | Secure API key management | +![Python](https://img.shields.io/badge/Python-20232A?style=for-the-badge&logo=python&logoColor=61DAFB) +![CSS](https://img.shields.io/badge/CSS-38B2AC?style=for-the-badge&logo=css&logoColor=white) +![SQLite](https://img.shields.io/badge/SQLite-4EA94B?style=for-the-badge&logo=sqlite&logoColor=white) +![Gemini](https://img.shields.io/badge/Gemini-3448C5?style=for-the-badge&logo=google&logoColor=white) +![Streamlit](https://img.shields.io/badge/Streamlit-0099FF?style=for-the-badge&logo=streamlit&logoColor=white) --- From bdc26f8b55a2d1305a4549eed0a328b8950d5890 Mon Sep 17 00:00:00 2001 From: eccentriccoder01 Date: Tue, 29 Jul 2025 15:31:41 +0530 Subject: [PATCH 18/50] Update --- .github/FUNDING.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..8d75d76 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: mreccentric +thanks_dev: # +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 290bc0d3c8976778d2bacf85c089309ca02d6181 Mon Sep 17 00:00:00 2001 From: srishti1837 Date: Tue, 29 Jul 2025 15:56:18 +0530 Subject: [PATCH 19/50] Added Journal Page and login setup for the same --- TalkHeal.py | 70 +++++++++++-- auth/auth_utils.py | 46 +++++++++ auth/users.yaml | 0 journals.db | Bin 0 -> 12288 bytes pages/Journaling.py | 239 +++++++++++++++++++++++++++++++------------- users.db | Bin 0 -> 16384 bytes 6 files changed, 280 insertions(+), 75 deletions(-) create mode 100644 auth/auth_utils.py create mode 100644 auth/users.yaml create mode 100644 journals.db create mode 100644 users.db diff --git a/TalkHeal.py b/TalkHeal.py index 02ba9ea..ca0d692 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -1,12 +1,68 @@ import streamlit as st +from auth.auth_utils import init_db, register_user, authenticate_user -# ✅ MUST be the first Streamlit command -st.set_page_config( - page_title="TalkHeal", - page_icon="💬", - layout="wide", - initial_sidebar_state=st.session_state.get("sidebar_state", "expanded") -) +st.set_page_config(page_title="TalkHeal", page_icon="💬", layout="wide") + +if "db_initialized" not in st.session_state: + init_db() + st.session_state["db_initialized"] = True + +if "authenticated" not in st.session_state: + st.session_state.authenticated = False +if "show_signup" not in st.session_state: + st.session_state.show_signup = False + +def show_login_ui(): + st.subheader("🔐 Login") + email = st.text_input("Email", key="login_email") + password = st.text_input("Password", type="password", key="login_password") + if st.button("Login"): + success, user = authenticate_user(email, password) + if success: + st.session_state.authenticated = True + # Set user_email and user_name separately for journaling page access + st.session_state.user_email = user["email"] + st.session_state.user_name = user["name"] + st.rerun() + else: + st.warning("Invalid email or password.") + st.markdown("Don't have an account? [Sign up](#)", unsafe_allow_html=True) + if st.button("Go to Sign Up"): + st.session_state.show_signup = True + st.rerun() + +def show_signup_ui(): + st.subheader("📝 Sign Up") + name = st.text_input("Name", key="signup_name") + email = st.text_input("Email", key="signup_email") + password = st.text_input("Password", type="password", key="signup_password") + if st.button("Sign Up"): + success, message = register_user(name, email, password) + if success: + st.success("Account created! Please log in.") + st.session_state.show_signup = False + st.rerun() + else: + st.error(message) + st.markdown("Already have an account? [Login](#)", unsafe_allow_html=True) + if st.button("Go to Login"): + st.session_state.show_signup = False + st.rerun() + +if not st.session_state.authenticated: + if st.session_state.show_signup: + show_signup_ui() + else: + show_login_ui() +else: + st.title(f"Welcome to TalkHeal, {st.session_state.user_name}! 💬") + st.markdown("Navigate to other pages from the sidebar.") + + if st.button("Logout"): + for key in ["authenticated", "user_email", "user_name", "show_signup"]: + if key in st.session_state: + del st.session_state[key] + st.rerun() import google.generativeai as genai from core.utils import save_conversations, load_conversations diff --git a/auth/auth_utils.py b/auth/auth_utils.py new file mode 100644 index 0000000..8c11f89 --- /dev/null +++ b/auth/auth_utils.py @@ -0,0 +1,46 @@ +import sqlite3 +import bcrypt + +def init_db(): + conn = sqlite3.connect("users.db") + cursor = conn.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT UNIQUE NOT NULL, + password TEXT NOT NULL + ) + """) + conn.commit() + conn.close() + +def hash_password(password): + return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode() + +def check_password(password, hashed): + return bcrypt.checkpw(password.encode(), hashed.encode()) + +def register_user(name, email, password): + conn = sqlite3.connect("users.db") + cursor = conn.cursor() + hashed_pw = hash_password(password) + try: + cursor.execute("INSERT INTO users (name, email, password) VALUES (?, ?, ?)", (name, email, hashed_pw)) + conn.commit() + return True, "User registered successfully" + except sqlite3.IntegrityError: + return False, "Email already registered" + finally: + conn.close() + +def authenticate_user(email, password): + conn = sqlite3.connect("users.db") + cursor = conn.cursor() + cursor.execute("SELECT name, password FROM users WHERE email = ?", (email,)) + result = cursor.fetchone() + conn.close() + if result and check_password(password, result[1]): + user = {"name": result[0], "email": email} + return True, user + return False, None \ No newline at end of file diff --git a/auth/users.yaml b/auth/users.yaml new file mode 100644 index 0000000..e69de29 diff --git a/journals.db b/journals.db new file mode 100644 index 0000000000000000000000000000000000000000..597e43a566b77c618934ce06c6c97e44b646035b GIT binary patch literal 12288 zcmeI%O>fgM7zc2tn;1;m%5A5KhZ9UC)zY+UdH}6V5t=r{)^-v*6)$mHL*9tpjUKlH z--&O+N9hqKV+iFk4qOobM{yF{u^s(#dYs|uvDAVlX&!S;E_=)@i|rFJ#%iXlm~t1E zxmmrdm}^nLFKVp$b!Vf#WVOvD+g#Sa-8TUJ5P$##AOHafKmY;|fB*y_0D-?LaQ(_! z+iEnd8&~s-NPJA^dBUTyNOUfRs#I$S1HV7=Y1Dsx>{F$ap0Cr-kRgry_aizP9KP)j z&gsZMfAOnR#9T&Y|8H$(xkaT-nRk$}IaS(2Zu0bE`f|pdm$e65rwxnABotrN=g4I7 zV?NhuSsz#4aqLtYioE_|;kjXoRo`ajKtKQj5P$##AOHafKmY;|fB*#kPl4+X*4kPB z=E?R`&vl$$;BuS09oKGOcs<+m+JPO2ZgYv$>)~ zT&av_A~yrf1d+Ubwn^mzQJQ`#7BHi)cpjSBNzY_C>UPgUEKX7-wY(Iq-B!om?b@y0 ry7{tJ{W~)U0s;_#00bZa0SG_<0uX=z1Rwx`e@x)n%Gsko*|hip5D>0g literal 0 HcmV?d00001 diff --git a/pages/Journaling.py b/pages/Journaling.py index be5b2ff..715e19c 100644 --- a/pages/Journaling.py +++ b/pages/Journaling.py @@ -1,70 +1,173 @@ import streamlit as st -from datetime import datetime -import os -import json -import google.generativeai as genai - -# ---- Gemini API Setup ---- -genai.configure(api_key=st.secrets["GEMINI_API_KEY"]) -model = genai.GenerativeModel("models/gemini-1.5-flash") - -# ---- File Setup ---- -JOURNAL_DIR = "journal_entries" -os.makedirs(JOURNAL_DIR, exist_ok=True) - -# Get user ID (customize this logic for actual user login) -user_id = st.session_state.get("user_id", "default_user") -user_file = os.path.join(JOURNAL_DIR, f"{user_id}_journal.json") - -# Load past entries -if os.path.exists(user_file): - with open(user_file, "r") as f: - journal_entries = json.load(f) -else: - journal_entries = [] - -# ---- UI ---- -st.title("📝 Journaling") -st.markdown("Reflect on your thoughts. Gemini will help you analyze your emotional tone.") - -# New entry input -st.markdown("### ✍️ New Entry") -entry = st.text_area("What's on your mind today?", height=200) - -if st.button("Save Entry", type="primary"): - if entry.strip(): - # Add entry - journal_entries.append({ - "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), - "content": entry.strip() - }) - with open(user_file, "w") as f: - json.dump(journal_entries, f, indent=2) - st.success("Journal entry saved.") - st.experimental_rerun() +import sqlite3 +import datetime +import base64 +from uuid import uuid4 + +def get_base64_of_bin_file(bin_file_path): + with open(bin_file_path, 'rb') as f: + data = f.read() + return base64.b64encode(data).decode() + +def set_background(main_bg_path, sidebar_bg_path=None): + main_bg = get_base64_of_bin_file(main_bg_path) + sidebar_bg = get_base64_of_bin_file(sidebar_bg_path) if sidebar_bg_path else main_bg + + st.markdown( + f""" + + """, + unsafe_allow_html=True + ) + +def analyze_sentiment(entry: str) -> str: + if any(word in entry.lower() for word in ['sad', 'tired', 'upset', 'angry']): + return "Negative" + elif any(word in entry.lower() for word in ['happy', 'grateful', 'joy']): + return "Positive" + return "Neutral" + +DB_PATH = "journals.db" + +def init_journal_db(): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute(""" + CREATE TABLE IF NOT EXISTS journal_entries ( + id TEXT PRIMARY KEY, + email TEXT, + entry TEXT, + sentiment TEXT, + date TEXT + ) + """) + conn.commit() + conn.close() + +def save_entry(email, entry, sentiment): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + cursor.execute(""" + INSERT INTO journal_entries (id, email, entry, sentiment, date) + VALUES (?, ?, ?, ?, ?) + """, (str(uuid4()), email, entry, sentiment, str(datetime.date.today()))) + conn.commit() + conn.close() + +def fetch_entries(email, sentiment_filter=None, start_date=None, end_date=None): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + query = """ + SELECT entry, sentiment, date FROM journal_entries + WHERE email = ? + """ + params = [email] + + if sentiment_filter and sentiment_filter != "All": + query += " AND sentiment = ?" + params.append(sentiment_filter) + + if start_date and end_date: + query += " AND date BETWEEN ? AND ?" + params.extend([start_date, end_date]) + + rows = cursor.execute(query, params).fetchall() + conn.close() + return rows + +def journaling_app(): + set_background("mint.png") # Use your background image path or comment this line + st.markdown( + """ + + """, + unsafe_allow_html=True + ) + email = st.session_state.get("user_email") + if not email: + st.warning("⚠️ Please login from the main page to access your journal.") + st.stop() + + st.title("📝 My Journal") + st.markdown("Write about your day, thoughts, or anything you'd like to reflect on.") + + with st.form("journal_form"): + journal_text = st.text_area("How are you feeling today?", height=200) + submitted = st.form_submit_button("Submit Entry") + + if submitted and journal_text.strip(): + sentiment = analyze_sentiment(journal_text) + save_entry(email, journal_text, sentiment) + st.success(f"Entry saved! Sentiment: **{sentiment}**") + + st.markdown("---") + st.subheader("📖 Your Journal Entries") + + filter_sentiment = st.selectbox("Filter by Sentiment", ["All", "Positive", "Neutral", "Negative"]) + + col1, col2 = st.columns(2) + with col1: + start_date = st.date_input("Start Date", value=datetime.date.today().replace(day=1)) + with col2: + end_date = st.date_input("End Date", value=datetime.date.today()) + + entries = fetch_entries(email, sentiment_filter=filter_sentiment, start_date=start_date, end_date=end_date) + + if not entries: + st.info("No entries found for selected filters.") else: - st.warning("Please write something before saving.") - -# ---- View Past Entries ---- -if journal_entries: - st.markdown("### 📖 Your Journal History") - for i, item in enumerate(reversed(journal_entries)): - with st.expander(item["timestamp"]): - st.markdown(item["content"]) - - if st.button(f"Analyze Emotional Tone #{i}", key=f"analyze_{i}"): - with st.spinner("Analyzing with Gemini..."): - prompt = f""" -You are a helpful emotional assistant. Analyze the emotional tone of the following journal entry. -Provide a 1-line mood summary and then a short encouraging message. - -Journal Entry: {item['content']} - """ - try: - response = model.generate_content(prompt) - st.markdown("🧠 **Gemini Insight:**") - st.markdown(response.text) - except Exception as e: - st.error(f"Error: {e}") -else: - st.info("No journal entries yet.") + for entry, sentiment, date in entries: + with st.expander(f"{date} - Mood: {sentiment}"): + st.write(entry) + +init_journal_db() +journaling_app() diff --git a/users.db b/users.db new file mode 100644 index 0000000000000000000000000000000000000000..4053dcb89f88865e196f9cd727c98fa1cbddf862 GIT binary patch literal 16384 zcmeI(Z)@5>90%|_)4H|LX3z(DPzLwF2bGr0XrV$WG}_qtFVPsWm4U>VX{i4byUu4a z*qhk%y_7!No7j68^x$fbg+%mapXj#?oT~t)#WU0q| zR%R$S22(XZRSSjX_jb>8x{C%?HCwJK8`=#`*ZnjYx0c8HGgUMi3JVtkB~HI(KRhqp zoOpwv+z8gvkb-Q!w|_my-t$w4zaacK|Av471Rwwb2tWV=5P$##AOHafKww?qLnurR z&S%?Z({jg-+wmMJ8B4^@F5B}By=V=3k$5vA#Ut5#I@7C{E7QArVlXX@GWYIK((II5 zmoob4#IS01y{WxV-d)GkVPT}ldlHO&v?` z#G+WMooY?3>vZgHXDZ)IXR@8E>Fshgca^imoah-3rW3y^S9bkXh4@FpKk`4<7l;}N zKmY;|fB*y_009U<00Izz00jOWfkW<`Za9Gmz2)Sc&1VPm@BftlMf?i^0SG_<0uX=z Z1Rwwb2tWV=5P-n{B(Os{fiC`b_zj%N!_xo& literal 0 HcmV?d00001 From f254296d1d4bd7493d3b2f26dac0d0bbdd8afc12 Mon Sep 17 00:00:00 2001 From: srishti1837 Date: Wed, 30 Jul 2025 00:01:48 +0530 Subject: [PATCH 20/50] Add bcrypt to requirements for deployment --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7a85788..13d53ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ google-generativeai geopy requests Pillow -streamlit-lottie \ No newline at end of file +streamlit-lottie +bcrypt \ No newline at end of file From 62764500a84bce449e829ea6ba7ffc4dc2eb2e5f Mon Sep 17 00:00:00 2001 From: eccentriccoder01 Date: Wed, 30 Jul 2025 00:52:10 +0530 Subject: [PATCH 21/50] Update --- css/styles.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/css/styles.py b/css/styles.py index 525f831..18ec9cd 100644 --- a/css/styles.py +++ b/css/styles.py @@ -63,23 +63,10 @@ def apply_custom_css(): --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); }} - /* Streamlit header and toolbar fixes */ .stApp > header {{ background-color: transparent !important; }} - div[data-testid="stToolbar"] {{ - visibility: hidden; - }} - - .stDeployButton {{ - visibility: hidden; - }} - - footer {{ - visibility: hidden; - }} - /* Main app background and styling */ .stApp {{ background-image: url("data:image/jpeg;base64,{base64_image}"); From d807ead798becfd94b67af689ff49fd62f3c703a Mon Sep 17 00:00:00 2001 From: eccentriccoder01 Date: Wed, 30 Jul 2025 22:31:36 +0530 Subject: [PATCH 22/50] Update --- .github/workflows/sync-issue-labels.yml | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/sync-issue-labels.yml diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml new file mode 100644 index 0000000..075bbb3 --- /dev/null +++ b/.github/workflows/sync-issue-labels.yml @@ -0,0 +1,36 @@ +name: Sync Issue Labels to PR + +on: + workflow_dispatch: # Manual trigger + +jobs: + sync-labels: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Run label sync script + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api -H "Accept: application/vnd.github+json" \ + repos/${{ github.repository }}/pulls?state=closed \ + --paginate > prs.json + + jq -c '.[] | select(.merged_at != null)' prs.json | while read pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_body=$(echo "$pr" | jq -r '.body') + + issue_number=$(echo "$pr_body" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -n1) + + if [ -n "$issue_number" ]; then + issue_labels=$(gh api repos/${{ github.repository }}/issues/$issue_number | jq -r '.labels[].name') + + for label in $issue_labels; do + echo "Adding label '$label' to PR #$pr_number" + gh api -X POST repos/${{ github.repository }}/issues/$pr_number/labels \ + -f labels[]="$label" + done + fi + done From 9030e8612757b3e2014b8e3f6ef30bc9873b5b0a Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 22:47:54 +0530 Subject: [PATCH 23/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 61 +++++++++++++++++-------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 075bbb3..17d5920 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,36 +1,61 @@ -name: Sync Issue Labels to PR +name: Label Merged PRs from Linked Issues on: - workflow_dispatch: # Manual trigger + workflow_dispatch: + # Or run on schedule + # schedule: + # - cron: '0 0 * * *' jobs: - sync-labels: + label-prs: runs-on: ubuntu-latest + steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout (required to use GH CLI) + uses: actions/checkout@v4 + + - name: Set up GitHub CLI + uses: cli/cli-action@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} - - name: Run label sync script - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Fetch and label merged PRs run: | + set -eo pipefail + + # Fetch all closed PRs gh api -H "Accept: application/vnd.github+json" \ repos/${{ github.repository }}/pulls?state=closed \ --paginate > prs.json - jq -c '.[] | select(.merged_at != null)' prs.json | while read pr; do + # Loop through only merged PRs + jq -c '.[] | select(.merged_at != null)' prs.json | while read -r pr; do pr_number=$(echo "$pr" | jq -r '.number') pr_body=$(echo "$pr" | jq -r '.body') + # Extract issue number using regex (e.g. "#5") issue_number=$(echo "$pr_body" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -n1) - if [ -n "$issue_number" ]; then - issue_labels=$(gh api repos/${{ github.repository }}/issues/$issue_number | jq -r '.labels[].name') - - for label in $issue_labels; do - echo "Adding label '$label' to PR #$pr_number" - gh api -X POST repos/${{ github.repository }}/issues/$pr_number/labels \ - -f labels[]="$label" - done + if [ -z "$issue_number" ]; then + echo "No linked issue found in PR #$pr_number. Skipping..." + continue + fi + + echo "Fetching labels from issue #$issue_number for PR #$pr_number..." + + # Get labels from issue + issue_labels=$(gh api repos/${{ github.repository }}/issues/$issue_number | jq -r '.labels[].name') + + if [ -z "$issue_labels" ]; then + echo "No labels found on issue #$issue_number. Skipping labeling." + continue fi - done + + # Add each label to the PR + for label in $issue_labels; do + echo "Adding label '$label' to PR #$pr_number..." + gh api -X POST repos/${{ github.repository }}/issues/$pr_number/labels \ + -f labels[]="$label" + sleep 0.2 + done + done || echo "Handled a parsing or pipe error gracefully." From 299bdbaa112d0ecda87d7edb60f0c61c12f46c76 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 22:51:10 +0530 Subject: [PATCH 24/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 30 +++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 17d5920..8b2ba57 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -2,16 +2,16 @@ name: Label Merged PRs from Linked Issues on: workflow_dispatch: - # Or run on schedule + # Uncomment to run nightly: # schedule: - # - cron: '0 0 * * *' + # - cron: '0 1 * * *' jobs: label-prs: runs-on: ubuntu-latest steps: - - name: Checkout (required to use GH CLI) + - name: Checkout repo (needed by GH CLI) uses: actions/checkout@v4 - name: Set up GitHub CLI @@ -19,43 +19,45 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} - - name: Fetch and label merged PRs + - name: Sync labels from issues to merged PRs run: | set -eo pipefail - # Fetch all closed PRs + echo "Fetching all closed PRs (may take time)..." gh api -H "Accept: application/vnd.github+json" \ repos/${{ github.repository }}/pulls?state=closed \ --paginate > prs.json - # Loop through only merged PRs + echo "Processing each merged PR..." + jq -c '.[] | select(.merged_at != null)' prs.json | while read -r pr; do pr_number=$(echo "$pr" | jq -r '.number') pr_body=$(echo "$pr" | jq -r '.body') - # Extract issue number using regex (e.g. "#5") + # Try to extract issue number from body (e.g., #123) issue_number=$(echo "$pr_body" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -n1) if [ -z "$issue_number" ]; then - echo "No linked issue found in PR #$pr_number. Skipping..." + echo "❌ No linked issue in PR #$pr_number" continue fi - echo "Fetching labels from issue #$issue_number for PR #$pr_number..." + echo "🔄 PR #$pr_number links to issue #$issue_number" - # Get labels from issue + # Get labels from the issue issue_labels=$(gh api repos/${{ github.repository }}/issues/$issue_number | jq -r '.labels[].name') if [ -z "$issue_labels" ]; then - echo "No labels found on issue #$issue_number. Skipping labeling." + echo "⚠️ No labels found on issue #$issue_number" continue fi # Add each label to the PR for label in $issue_labels; do - echo "Adding label '$label' to PR #$pr_number..." + echo "🏷️ Adding label '$label' to PR #$pr_number" gh api -X POST repos/${{ github.repository }}/issues/$pr_number/labels \ -f labels[]="$label" - sleep 0.2 + sleep 0.3 done - done || echo "Handled a parsing or pipe error gracefully." + + done || echo "⚠️ Parsing or pipe failure handled gracefully" From 753353569863dac7d3d69d5e07513c9251c6d307 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 22:54:44 +0530 Subject: [PATCH 25/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 90 +++++++++++-------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 8b2ba57..c1a9369 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,63 +1,55 @@ -name: Label Merged PRs from Linked Issues +#!/bin/bash -on: - workflow_dispatch: - # Uncomment to run nightly: - # schedule: - # - cron: '0 1 * * *' +# Required: gh CLI must be installed and authenticated (gh auth login) -jobs: - label-prs: - runs-on: ubuntu-latest +REPO="eccentriccoder01/YourRepoName" # Replace with your full repo path +MAX_RETRIES=3 - steps: - - name: Checkout repo (needed by GH CLI) - uses: actions/checkout@v4 +echo "Fetching merged PRs for $REPO..." - - name: Set up GitHub CLI - uses: cli/cli-action@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} +gh pr list --state merged --limit 200 --repo "$REPO" --json number,title,body > merged_prs.json - - name: Sync labels from issues to merged PRs - run: | - set -eo pipefail +jq -c '.[]' merged_prs.json | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_body=$(echo "$pr" | jq -r '.body') + + # Try to extract linked issue number from title/body (e.g. "#12") + issue_number=$(echo "$pr_title $pr_body" | grep -oE '#[0-9]+' | head -n 1 | tr -d '#') - echo "Fetching all closed PRs (may take time)..." - gh api -H "Accept: application/vnd.github+json" \ - repos/${{ github.repository }}/pulls?state=closed \ - --paginate > prs.json + if [ -z "$issue_number" ]; then + echo "❌ PR #$pr_number has no linked issue." + continue + fi - echo "Processing each merged PR..." + echo "🔗 PR #$pr_number linked to issue #$issue_number" - jq -c '.[] | select(.merged_at != null)' prs.json | while read -r pr; do - pr_number=$(echo "$pr" | jq -r '.number') - pr_body=$(echo "$pr" | jq -r '.body') + # Fetch labels from the linked issue + issue_labels=$(gh issue view "$issue_number" --repo "$REPO" --json labels | jq -r '.labels[].name') - # Try to extract issue number from body (e.g., #123) - issue_number=$(echo "$pr_body" | grep -oE '#[0-9]+' | grep -oE '[0-9]+' | head -n1) + if [ -z "$issue_labels" ]; then + echo "⚠️ Issue #$issue_number has no labels." + continue + fi - if [ -z "$issue_number" ]; then - echo "❌ No linked issue in PR #$pr_number" - continue - fi + # Add each label to the PR with retry logic + for label in $issue_labels; do + success=false + for attempt in $(seq 1 $MAX_RETRIES); do + echo "🏷️ Applying label '$label' to PR #$pr_number (Attempt $attempt)..." + gh pr edit "$pr_number" --repo "$REPO" --add-label "$label" && success=true && break + echo "⚠️ Failed to apply label '$label' (Attempt $attempt). Retrying in 2s..." + sleep 2 + done - echo "🔄 PR #$pr_number links to issue #$issue_number" + if ! $success; then + echo "❌ Giving up on label '$label' after $MAX_RETRIES attempts." + fi + done - # Get labels from the issue - issue_labels=$(gh api repos/${{ github.repository }}/issues/$issue_number | jq -r '.labels[].name') + # Delay to avoid rate-limiting + sleep 0.5 - if [ -z "$issue_labels" ]; then - echo "⚠️ No labels found on issue #$issue_number" - continue - fi +done - # Add each label to the PR - for label in $issue_labels; do - echo "🏷️ Adding label '$label' to PR #$pr_number" - gh api -X POST repos/${{ github.repository }}/issues/$pr_number/labels \ - -f labels[]="$label" - sleep 0.3 - done - - done || echo "⚠️ Parsing or pipe failure handled gracefully" +echo "✅ Done syncing labels." From 72cb962b3a15c99bcced6cb26e6228c4e6a53936 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 22:59:33 +0530 Subject: [PATCH 26/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index c1a9369..430520c 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,8 +1,4 @@ -#!/bin/bash - -# Required: gh CLI must be installed and authenticated (gh auth login) - -REPO="eccentriccoder01/YourRepoName" # Replace with your full repo path +REPO="eccentriccoder01/TalkHeal" # Replace with your full repo path MAX_RETRIES=3 echo "Fetching merged PRs for $REPO..." From 321e911c5b7c5b9e348d602741358cc69be1ccbc Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:05:10 +0530 Subject: [PATCH 27/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 430520c..9ba07da 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,4 +1,4 @@ -REPO="eccentriccoder01/TalkHeal" # Replace with your full repo path +REPO="eccentriccoder01/TalkHeal" MAX_RETRIES=3 echo "Fetching merged PRs for $REPO..." @@ -10,7 +10,6 @@ jq -c '.[]' merged_prs.json | while read -r pr; do pr_title=$(echo "$pr" | jq -r '.title') pr_body=$(echo "$pr" | jq -r '.body') - # Try to extract linked issue number from title/body (e.g. "#12") issue_number=$(echo "$pr_title $pr_body" | grep -oE '#[0-9]+' | head -n 1 | tr -d '#') if [ -z "$issue_number" ]; then @@ -20,7 +19,6 @@ jq -c '.[]' merged_prs.json | while read -r pr; do echo "🔗 PR #$pr_number linked to issue #$issue_number" - # Fetch labels from the linked issue issue_labels=$(gh issue view "$issue_number" --repo "$REPO" --json labels | jq -r '.labels[].name') if [ -z "$issue_labels" ]; then @@ -29,19 +27,22 @@ jq -c '.[]' merged_prs.json | while read -r pr; do fi # Add each label to the PR with retry logic - for label in $issue_labels; do + while IFS= read -r label; do success=false for attempt in $(seq 1 $MAX_RETRIES); do echo "🏷️ Applying label '$label' to PR #$pr_number (Attempt $attempt)..." - gh pr edit "$pr_number" --repo "$REPO" --add-label "$label" && success=true && break + if gh pr edit "$pr_number" --repo "$REPO" --add-label "$label"; then + success=true + break + fi echo "⚠️ Failed to apply label '$label' (Attempt $attempt). Retrying in 2s..." sleep 2 done - if ! $success; then + if [ "$success" = false ]; then echo "❌ Giving up on label '$label' after $MAX_RETRIES attempts." fi - done + done <<< "$issue_labels" # Delay to avoid rate-limiting sleep 0.5 From daeb9767fd3875d854ae3c525a36f12e798bfa6d Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:06:54 +0530 Subject: [PATCH 28/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 9ba07da..18185d7 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,3 +1,4 @@ +#!/bin/bash REPO="eccentriccoder01/TalkHeal" MAX_RETRIES=3 From 9e08320f3267ba28846166ea6550eb712a097d2e Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:08:56 +0530 Subject: [PATCH 29/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 18185d7..9340be6 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,5 +1,5 @@ #!/bin/bash -REPO="eccentriccoder01/TalkHeal" +REPO = "eccentriccoder01/TalkHeal" MAX_RETRIES=3 echo "Fetching merged PRs for $REPO..." From 156d6330c31f31d247122fb1d10630ef029f3f7c Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:36:03 +0530 Subject: [PATCH 30/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 9340be6..e82ec3b 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,5 +1,5 @@ #!/bin/bash -REPO = "eccentriccoder01/TalkHeal" +REPO = "eccentriccoder01/talkheal" MAX_RETRIES=3 echo "Fetching merged PRs for $REPO..." From 81b51f86de3de01f7d50c6b184f7dcc41e80e37e Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:47:20 +0530 Subject: [PATCH 31/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 128 ++++++++++++++---------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index e82ec3b..933f3de 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -1,53 +1,75 @@ -#!/bin/bash -REPO = "eccentriccoder01/talkheal" -MAX_RETRIES=3 - -echo "Fetching merged PRs for $REPO..." - -gh pr list --state merged --limit 200 --repo "$REPO" --json number,title,body > merged_prs.json - -jq -c '.[]' merged_prs.json | while read -r pr; do - pr_number=$(echo "$pr" | jq -r '.number') - pr_title=$(echo "$pr" | jq -r '.title') - pr_body=$(echo "$pr" | jq -r '.body') - - issue_number=$(echo "$pr_title $pr_body" | grep -oE '#[0-9]+' | head -n 1 | tr -d '#') - - if [ -z "$issue_number" ]; then - echo "❌ PR #$pr_number has no linked issue." - continue - fi - - echo "🔗 PR #$pr_number linked to issue #$issue_number" - - issue_labels=$(gh issue view "$issue_number" --repo "$REPO" --json labels | jq -r '.labels[].name') - - if [ -z "$issue_labels" ]; then - echo "⚠️ Issue #$issue_number has no labels." - continue - fi - - # Add each label to the PR with retry logic - while IFS= read -r label; do - success=false - for attempt in $(seq 1 $MAX_RETRIES); do - echo "🏷️ Applying label '$label' to PR #$pr_number (Attempt $attempt)..." - if gh pr edit "$pr_number" --repo "$REPO" --add-label "$label"; then - success=true - break - fi - echo "⚠️ Failed to apply label '$label' (Attempt $attempt). Retrying in 2s..." - sleep 2 - done - - if [ "$success" = false ]; then - echo "❌ Giving up on label '$label' after $MAX_RETRIES attempts." - fi - done <<< "$issue_labels" - - # Delay to avoid rate-limiting - sleep 0.5 - -done - -echo "✅ Done syncing labels." +name: Sync Labels from Issues to Merged PRs + +on: + workflow_dispatch: + schedule: + - cron: '0 2 * * *' # Optional: runs every day at 2 AM UTC + +jobs: + sync-labels: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install GitHub CLI and jq + run: | + sudo apt-get update + sudo apt-get install -y gh jq + + - name: Authenticate GitHub CLI + run: gh auth setup-git + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Fetch merged PRs and sync labels from issues + env: + REPO: eccentriccoder01/talkheal + MAX_RETRIES: 3 + run: | + echo "📥 Fetching merged PRs for $REPO..." + gh pr list --state merged --limit 100 --repo "$REPO" --json number,title,body > merged_prs.json + + jq -c '.[]' merged_prs.json | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + pr_title=$(echo "$pr" | jq -r '.title') + pr_body=$(echo "$pr" | jq -r '.body') + + issue_number=$(echo "$pr_title $pr_body" | grep -oE '#[0-9]+' | head -n 1 | tr -d '#') + + if [ -z "$issue_number" ]; then + echo "❌ PR #$pr_number has no linked issue." + continue + fi + + echo "🔗 PR #$pr_number linked to issue #$issue_number" + + issue_labels=$(gh issue view "$issue_number" --repo "$REPO" --json labels | jq -r '.labels[].name') + + if [ -z "$issue_labels" ]; then + echo "⚠️ Issue #$issue_number has no labels." + continue + fi + + while IFS= read -r label; do + success=false + for attempt in $(seq 1 $MAX_RETRIES); do + echo "🏷️ Applying label '$label' to PR #$pr_number (Attempt $attempt)..." + if gh pr edit "$pr_number" --repo "$REPO" --add-label "$label"; then + success=true + break + fi + echo "⚠️ Failed to apply label '$label'. Retrying in 2s..." + sleep 2 + done + + if [ "$success" = false ]; then + echo "❌ Giving up on label '$label' after $MAX_RETRIES attempts." + fi + done <<< "$issue_labels" + + sleep 0.5 + done + + echo "✅ Done syncing labels." From e92647dfb580a0be05dccfcc8a28f8abfa26f86a Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:49:28 +0530 Subject: [PATCH 32/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 933f3de..80bfce8 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -17,9 +17,9 @@ jobs: run: | sudo apt-get update sudo apt-get install -y gh jq - + - name: Authenticate GitHub CLI - run: gh auth setup-git + run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 2b9f9733c1cc7cf422e6c11c718055af2c578c84 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:52:36 +0530 Subject: [PATCH 33/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index 80bfce8..aea7570 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -19,7 +19,7 @@ jobs: sudo apt-get install -y gh jq - name: Authenticate GitHub CLI - run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token + run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token --hostname github.com env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 764e092abb48cba38014ae56400b01d0fdd6f503 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Wed, 30 Jul 2025 23:55:21 +0530 Subject: [PATCH 34/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 31 ++++++------------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index aea7570..cc9c759 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -3,30 +3,25 @@ name: Sync Labels from Issues to Merged PRs on: workflow_dispatch: schedule: - - cron: '0 2 * * *' # Optional: runs every day at 2 AM UTC + - cron: '0 2 * * *' # optional daily run jobs: sync-labels: runs-on: ubuntu-latest steps: - - name: Checkout repository + - name: Checkout repo uses: actions/checkout@v4 - - name: Install GitHub CLI and jq + - name: Install GitHub CLI & jq run: | sudo apt-get update sudo apt-get install -y gh jq - - - name: Authenticate GitHub CLI - run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token --hostname github.com - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Fetch merged PRs and sync labels from issues + - name: Sync labels from linked issues to merged PRs env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: eccentriccoder01/talkheal - MAX_RETRIES: 3 run: | echo "📥 Fetching merged PRs for $REPO..." gh pr list --state merged --limit 100 --repo "$REPO" --json number,title,body > merged_prs.json @@ -53,20 +48,8 @@ jobs: fi while IFS= read -r label; do - success=false - for attempt in $(seq 1 $MAX_RETRIES); do - echo "🏷️ Applying label '$label' to PR #$pr_number (Attempt $attempt)..." - if gh pr edit "$pr_number" --repo "$REPO" --add-label "$label"; then - success=true - break - fi - echo "⚠️ Failed to apply label '$label'. Retrying in 2s..." - sleep 2 - done - - if [ "$success" = false ]; then - echo "❌ Giving up on label '$label' after $MAX_RETRIES attempts." - fi + echo "🏷️ Applying label '$label' to PR #$pr_number..." + gh pr edit "$pr_number" --repo "$REPO" --add-label "$label" done <<< "$issue_labels" sleep 0.5 From b5c1d78890704da15f4676fe8eb08c04884a82f1 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Thu, 31 Jul 2025 00:07:13 +0530 Subject: [PATCH 35/50] Create assign-pr-authors.yml --- .github/workflows/assign-pr-authors.yml | 44 +++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/assign-pr-authors.yml diff --git a/.github/workflows/assign-pr-authors.yml b/.github/workflows/assign-pr-authors.yml new file mode 100644 index 0000000..f7f83dc --- /dev/null +++ b/.github/workflows/assign-pr-authors.yml @@ -0,0 +1,44 @@ +name: Assign PR Authors to Merged PRs + +on: + workflow_dispatch: + schedule: + - cron: '0 3 * * *' # runs daily at 3 AM UTC (8:30 AM IST) + +jobs: + assign-authors: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install GitHub CLI and jq + run: | + sudo apt-get update + sudo apt-get install -y gh jq + + - name: Assign authors to their merged PRs + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: eccentriccoder01/talkheal + run: | + echo "📥 Fetching merged PRs for $REPO..." + gh pr list --state merged --limit 100 --repo "$REPO" --json number,author,assignees > merged_prs.json + + jq -c '.[]' merged_prs.json | while read -r pr; do + pr_number=$(echo "$pr" | jq -r '.number') + author_login=$(echo "$pr" | jq -r '.author.login') + current_assignees=$(echo "$pr" | jq -r '.assignees[].login') + + if echo "$current_assignees" | grep -q "$author_login"; then + echo "✅ PR #$pr_number already assigned to $author_login" + else + echo "👤 Assigning $author_login to PR #$pr_number..." + gh pr edit "$pr_number" --repo "$REPO" --add-assignee "$author_login" + fi + + sleep 0.5 + done + + echo "✅ Done assigning PR authors." From b640ec7ba0edb8fdd6d70b77918a7b32f32a762d Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Thu, 31 Jul 2025 00:30:22 +0530 Subject: [PATCH 36/50] Update assign-pr-authors.yml --- .github/workflows/assign-pr-authors.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/assign-pr-authors.yml b/.github/workflows/assign-pr-authors.yml index f7f83dc..2189231 100644 --- a/.github/workflows/assign-pr-authors.yml +++ b/.github/workflows/assign-pr-authors.yml @@ -20,24 +20,31 @@ jobs: - name: Assign authors to their merged PRs env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} REPO: eccentriccoder01/talkheal run: | echo "📥 Fetching merged PRs for $REPO..." gh pr list --state merged --limit 100 --repo "$REPO" --json number,author,assignees > merged_prs.json + echo "🔑 Checking authentication context..." + gh auth status + jq -c '.[]' merged_prs.json | while read -r pr; do pr_number=$(echo "$pr" | jq -r '.number') author_login=$(echo "$pr" | jq -r '.author.login') current_assignees=$(echo "$pr" | jq -r '.assignees[].login') - + if echo "$current_assignees" | grep -q "$author_login"; then echo "✅ PR #$pr_number already assigned to $author_login" else echo "👤 Assigning $author_login to PR #$pr_number..." - gh pr edit "$pr_number" --repo "$REPO" --add-assignee "$author_login" + if gh pr edit "$pr_number" --repo "$REPO" --add-assignee "$author_login"; then + echo "✅ Successfully assigned $author_login to PR #$pr_number." + else + echo "⚠️ Could not assign $author_login to PR #$pr_number. Username or permissions issue." + fi fi - + sleep 0.5 done From 5103eb33b37835a2c3c6e96e0a85321fd3074df7 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Thu, 31 Jul 2025 00:41:36 +0530 Subject: [PATCH 37/50] Update assign-pr-authors.yml --- .github/workflows/assign-pr-authors.yml | 56 +++++-------------------- 1 file changed, 11 insertions(+), 45 deletions(-) diff --git a/.github/workflows/assign-pr-authors.yml b/.github/workflows/assign-pr-authors.yml index 2189231..32dc8d7 100644 --- a/.github/workflows/assign-pr-authors.yml +++ b/.github/workflows/assign-pr-authors.yml @@ -1,51 +1,17 @@ -name: Assign PR Authors to Merged PRs +# .github/workflows/auto-author-assign.yml +name: Auto Author Assign on: - workflow_dispatch: - schedule: - - cron: '0 3 * * *' # runs daily at 3 AM UTC (8:30 AM IST) + pull_request_target: + types: [ opened, reopened ] + +permissions: + pull-requests: write jobs: - assign-authors: + assign-author: runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install GitHub CLI and jq - run: | - sudo apt-get update - sudo apt-get install -y gh jq - - - name: Assign authors to their merged PRs - env: - GH_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }} - REPO: eccentriccoder01/talkheal - run: | - echo "📥 Fetching merged PRs for $REPO..." - gh pr list --state merged --limit 100 --repo "$REPO" --json number,author,assignees > merged_prs.json - - echo "🔑 Checking authentication context..." - gh auth status - - jq -c '.[]' merged_prs.json | while read -r pr; do - pr_number=$(echo "$pr" | jq -r '.number') - author_login=$(echo "$pr" | jq -r '.author.login') - current_assignees=$(echo "$pr" | jq -r '.assignees[].login') - - if echo "$current_assignees" | grep -q "$author_login"; then - echo "✅ PR #$pr_number already assigned to $author_login" - else - echo "👤 Assigning $author_login to PR #$pr_number..." - if gh pr edit "$pr_number" --repo "$REPO" --add-assignee "$author_login"; then - echo "✅ Successfully assigned $author_login to PR #$pr_number." - else - echo "⚠️ Could not assign $author_login to PR #$pr_number. Username or permissions issue." - fi - fi - - sleep 0.5 - done - - echo "✅ Done assigning PR authors." + - uses: toshimaru/auto-author-assign@v2.1.1 + with: + repo-token: ${{ secrets.GH_PERSONAL_TOKEN }} From 2bb097ce7e87b426216b85fb77d2493b456fa9f5 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Thu, 31 Jul 2025 00:41:50 +0530 Subject: [PATCH 38/50] Update assign-pr-authors.yml --- .github/workflows/assign-pr-authors.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/assign-pr-authors.yml b/.github/workflows/assign-pr-authors.yml index 32dc8d7..2ab0bb7 100644 --- a/.github/workflows/assign-pr-authors.yml +++ b/.github/workflows/assign-pr-authors.yml @@ -1,4 +1,3 @@ -# .github/workflows/auto-author-assign.yml name: Auto Author Assign on: From 67cd988136bf474dab0cc1d0213765216f283110 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Thu, 31 Jul 2025 00:48:19 +0530 Subject: [PATCH 39/50] Rename assign-pr-authors.yml to auto-author-assign.yml --- .../workflows/{assign-pr-authors.yml => auto-author-assign.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{assign-pr-authors.yml => auto-author-assign.yml} (100%) diff --git a/.github/workflows/assign-pr-authors.yml b/.github/workflows/auto-author-assign.yml similarity index 100% rename from .github/workflows/assign-pr-authors.yml rename to .github/workflows/auto-author-assign.yml From eaafe22a0091a483c3f8520749719da46c8933af Mon Sep 17 00:00:00 2001 From: mreccentric01 Date: Thu, 31 Jul 2025 00:52:23 +0530 Subject: [PATCH 40/50] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bfe1f9..ce5e063 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## 🧠 Your AI-Powered Mental Health Companion -**TalkHeal** is an empathetic, intelligent, and interactive mental health support assistant built using **Python** and **Streamlit**. Designed with compassion and care at its core, it offers 24/7 support, emotional journaling, resource guidance, and AI-powered conversations powered by Google’s Gemini Pro. +**TalkHeal** is an empathetic, intelligent, and interactive mental health support assistant built using **Python** and **Streamlit**. Designed with compassion and care at its core, it offers 24/7 support, emotional journaling, resource guidance, and AI-powered conversations powered by Google’s Gemini. --- From 2f2892a1f8be4d4c41af5c97940379cfe8ca7930 Mon Sep 17 00:00:00 2001 From: Sagnik Chakraborty Date: Thu, 31 Jul 2025 01:07:37 +0530 Subject: [PATCH 41/50] Update sync-issue-labels.yml --- .github/workflows/sync-issue-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-issue-labels.yml b/.github/workflows/sync-issue-labels.yml index cc9c759..5abc15d 100644 --- a/.github/workflows/sync-issue-labels.yml +++ b/.github/workflows/sync-issue-labels.yml @@ -3,7 +3,7 @@ name: Sync Labels from Issues to Merged PRs on: workflow_dispatch: schedule: - - cron: '0 2 * * *' # optional daily run + - cron: '0 2 * * *' jobs: sync-labels: From 2f27cb690e4ef2180cec7eb582e261a9d6195997 Mon Sep 17 00:00:00 2001 From: eccentriccoder01 Date: Thu, 31 Jul 2025 02:18:34 +0530 Subject: [PATCH 42/50] Update --- users.db | Bin 16384 -> 16384 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/users.db b/users.db index 4053dcb89f88865e196f9cd727c98fa1cbddf862..11ed19f17fc26156ad5bbe45e1b921cabaeec243 100644 GIT binary patch delta 277 zcmZo@U~Fh$oFL7}I#I@%k#%Fj5`G>QzR3*yv-xfLuJcXaEGW>-SMS8a#=y(l$f{zL zq+)2KV(H-oAxh6Y;rT9l@m7?_)tWcrq6n&?+p2D=5N2Nf2iPPUal s&cnjLfPsGj|5E;A{Es#ZDoo&4V`1iHWKm#tWyEJO^X6mvmI90{00^v7g8%>k delta 58 zcmZo@U~Fh$oFL7}JW&&2Z|hH;6KLyXtSWf NF8 From 4551207c172f41834ddc85e1c515e50b9f36927d Mon Sep 17 00:00:00 2001 From: aditya singh rathore Date: Thu, 31 Jul 2025 16:48:01 +0530 Subject: [PATCH 43/50] Added Templates --- .github/ISSUE_TEMPLATE/bug_report.yml | 27 +++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 2 ++ .github/ISSUE_TEMPLATE/feature_request.yml | 26 +++++++++++++++ .github/pull_request_template.md | 38 ++++++++++++++++++++++ 4 files changed, 93 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..58c0cff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,27 @@ +name: Bug report +description: Create a report to help us improve +labels: bug +body: + - type: textarea + attributes: + label: Describe the bug + description: Describe the bug. + placeholder: > + A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + attributes: + label: To Reproduce + placeholder: > + Steps to reproduce the behavior: + - type: textarea + attributes: + label: Expected behavior + placeholder: > + A clear and concise description of what you expected to happen. + - type: textarea + attributes: + label: Additional context + placeholder: > + Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..12a08a1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,2 @@ +blank_issue_enable: true +blank_issue_title: "Please provide a title for your issue" \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..bfe9d8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,26 @@ +name: Feature request +description: Suggest an idea for this project +labels: enhancement +body: + - type: textarea + attributes: + label: Is your feature request related to a problem or challenge? + description: Please describe what you are trying to do. + placeholder: > + A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + (This section helps developers understand the context and *why* for this feature, in addition to the *what*) + - type: textarea + attributes: + label: Describe the solution you'd like + placeholder: > + A clear and concise description of what you want to happen. + - type: textarea + attributes: + label: Describe alternatives you've considered + placeholder: > + A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + attributes: + label: Additional context + placeholder: > + Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..790fcf6 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,38 @@ +## Which issue does this PR close? + + + +- Closes #. + +## Rationale for this change + + + +## What changes are included in this PR? + + + +## Are these changes tested? + + + +## Are there any user-facing changes? + + + + \ No newline at end of file From 07658c17518fb314a54cfb0793a8738b6d75e1f1 Mon Sep 17 00:00:00 2001 From: tilakjain619 Date: Thu, 31 Jul 2025 18:28:58 +0530 Subject: [PATCH 44/50] fix: Retry GeoIP helpline detection patch after incomplete commit --- components/sidebar.py | 63 +++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/components/sidebar.py b/components/sidebar.py index 77d7307..462617e 100644 --- a/components/sidebar.py +++ b/components/sidebar.py @@ -4,8 +4,8 @@ from core.utils import create_new_conversation, get_current_time from core.theme import get_current_theme, toggle_theme, set_palette, PALETTES from components.profile import initialize_profile_state, render_profile_section +from streamlit_js_eval import streamlit_js_eval import requests -import streamlit_js_eval as st_js # --- Structured Emergency Resources --- GLOBAL_RESOURCES = [ @@ -21,44 +21,49 @@ "url": "https://www.childhelplineinternational.org/"} ] -# Get the user's country for showing localized resources. -def get_user_country(): - # First we try Streamlit's experimental user info API (client-side, most accurate) + +def get_country_from_coords(lat, lon): try: - user_info = st.experimental_user_info() if hasattr(st, "experimental_user_info") else None - if user_info and "country" in user_info and user_info["country"]: - return user_info["country"] - except Exception: + url = f"https://geocode.maps.co/reverse?lat={lat}&lon={lon}" + resp = requests.get(url, timeout=5) + if resp.status_code == 200: + data = resp.json() + return data.get("address", {}).get("country_code", "").upper() + except: pass + return None - # Second we try browser geolocation via JavaScript (requires Streamlit component, not always available) - try: - js_code = """ - navigator.geolocation.getCurrentPosition( - pos => { - fetch(`https://geocode.xyz/${pos.coords.latitude},${pos.coords.longitude}?geoit=json`) - .then(resp => resp.json()) - .then(data => window.streamlitSendToPython(data.country)); - }, - err => window.streamlitSendToPython(null) - ); - """ - country = st_js.run(js_code, key="geo_country") +def get_user_country(): + # 1. Try to get user's actual browser location (via JS) + coords = streamlit_js_eval( + js_expressions=""" + new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition( + position => resolve({ + latitude: position.coords.latitude, + longitude: position.coords.longitude + }), + error => resolve(null) + ); + }); + """, + key="get_coords" + ) + + if coords and "latitude" in coords and "longitude" in coords: + country = get_country_from_coords(coords["latitude"], coords["longitude"]) if country: return country - except Exception: - pass - # (Fallback option): Third we try IP-based lookup (may return server location) + # 2. Fallback to IP-based location using ipapi.co (no key required) try: - resp = requests.get("https://ipinfo.io/json", timeout=2) + resp = requests.get("https://ipapi.co/json/", timeout=3) if resp.status_code == 200: - data = resp.json() - return data.get("country", None) - except Exception: + return resp.json().get("country_code", "").upper() + except: pass - return None + return None # final fallback if everything fails country_helplines = { "US": [ From 8e689f481e7dbe0a68d75dcc0d44206b1f55921b Mon Sep 17 00:00:00 2001 From: Pragnya Khandelwal Date: Thu, 31 Jul 2025 19:29:14 +0530 Subject: [PATCH 45/50] fix: hide sidebar and chat UI on login screen using session_state --- TalkHeal.py | 92 +++++++++++++++++------------------- pink.png => assets/pink.png | Bin components/header.py | 44 ++++------------- components/login_page.py | 72 ++++++++++++++++++++++++++++ users.db | Bin 16384 -> 16384 bytes 5 files changed, 125 insertions(+), 83 deletions(-) rename pink.png => assets/pink.png (100%) create mode 100644 components/login_page.py diff --git a/TalkHeal.py b/TalkHeal.py index ca0d692..98e9c5f 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -1,69 +1,49 @@ import streamlit as st -from auth.auth_utils import init_db, register_user, authenticate_user +from auth.auth_utils import init_db +from components.login_page import show_login_page st.set_page_config(page_title="TalkHeal", page_icon="💬", layout="wide") +# --- DB Initialization --- if "db_initialized" not in st.session_state: init_db() st.session_state["db_initialized"] = True +# --- Auth State Initialization --- if "authenticated" not in st.session_state: st.session_state.authenticated = False if "show_signup" not in st.session_state: st.session_state.show_signup = False -def show_login_ui(): - st.subheader("🔐 Login") - email = st.text_input("Email", key="login_email") - password = st.text_input("Password", type="password", key="login_password") - if st.button("Login"): - success, user = authenticate_user(email, password) - if success: - st.session_state.authenticated = True - # Set user_email and user_name separately for journaling page access - st.session_state.user_email = user["email"] - st.session_state.user_name = user["name"] +# --- LOGIN PAGE --- +if not st.session_state.authenticated: + show_login_page() + st.stop() + +# --- TOP RIGHT BUTTONS: THEME TOGGLE & LOGOUT --- +if st.session_state.get("authenticated", False): + col_spacer, col_theme, col_logout = st.columns([5, 1, 1]) + with col_spacer: + pass # empty spacer to push buttons right + with col_theme: + is_dark = st.session_state.get('dark_mode', False) + if st.button("🌙" if is_dark else "☀️", key="top_theme_toggle", help="Toggle Light/Dark Mode", use_container_width=True): + st.session_state.dark_mode = not is_dark + st.session_state.theme_changed = True st.rerun() - else: - st.warning("Invalid email or password.") - st.markdown("Don't have an account? [Sign up](#)", unsafe_allow_html=True) - if st.button("Go to Sign Up"): - st.session_state.show_signup = True - st.rerun() - -def show_signup_ui(): - st.subheader("📝 Sign Up") - name = st.text_input("Name", key="signup_name") - email = st.text_input("Email", key="signup_email") - password = st.text_input("Password", type="password", key="signup_password") - if st.button("Sign Up"): - success, message = register_user(name, email, password) - if success: - st.success("Account created! Please log in.") - st.session_state.show_signup = False + with col_logout: + if st.button("Logout", key="logout_btn", use_container_width=True): + for key in ["authenticated", "user_email", "user_name", "show_signup"]: + if key in st.session_state: + del st.session_state[key] st.rerun() - else: - st.error(message) - st.markdown("Already have an account? [Login](#)", unsafe_allow_html=True) - if st.button("Go to Login"): - st.session_state.show_signup = False - st.rerun() -if not st.session_state.authenticated: - if st.session_state.show_signup: - show_signup_ui() - else: - show_login_ui() -else: +# --- MAIN UI (only after login) --- +header_col1, header_col2, header_col3 = st.columns([6, 1, 1]) +with header_col1: st.title(f"Welcome to TalkHeal, {st.session_state.user_name}! 💬") st.markdown("Navigate to other pages from the sidebar.") - if st.button("Logout"): - for key in ["authenticated", "user_email", "user_name", "show_signup"]: - if key in st.session_state: - del st.session_state[key] - st.rerun() - import google.generativeai as genai from core.utils import save_conversations, load_conversations from core.config import configure_gemini, PAGE_CONFIG @@ -170,4 +150,20 @@ def get_tone_prompt(): } setTimeout(scrollToBottom, 100); -""", unsafe_allow_html=True) \ No newline at end of file +""", unsafe_allow_html=True) + +st.markdown(""" + +""", unsafe_allow_html=True) \ No newline at end of file diff --git a/pink.png b/assets/pink.png similarity index 100% rename from pink.png rename to assets/pink.png diff --git a/components/header.py b/components/header.py index 43bd86f..6a1224c 100644 --- a/components/header.py +++ b/components/header.py @@ -2,38 +2,12 @@ def render_header(): with st.container(): - # Top bar with hamburger menu and theme toggle - col1, col2, col3 = st.columns([0.1, 0.8, 0.1]) - - with col1: - if st.button("☰", key="top_hamburger_menu", help="Toggle Sidebar", use_container_width=True): - if st.session_state.sidebar_state == "expanded": - st.session_state.sidebar_state = "collapsed" - else: - st.session_state.sidebar_state = "expanded" - st.rerun() - - with col3: - is_dark = st.session_state.get('dark_mode', False) - if st.button("🌙" if is_dark else "☀️", key="top_theme_toggle", help="Toggle Light/Dark Mode", use_container_width=True): - st.session_state.dark_mode = not is_dark - st.session_state.theme_changed = True - st.rerun() - - st.markdown(""" -
-

TalkHeal

-

Your Mental Health Companion 💙

-
- """, unsafe_allow_html=True) - ## Commented out this part of the header because the 'emergency button' functionality is quite similar, hence causing redundancy. ## - - # with st.expander("📍 Find Help Nearby"): - # location_input = st.text_input("Enter your city", key="header_location_search") - # if st.button("🔍 Search Centers", key="header_search_nearby"): - # if location_input: - # search_url = f"https://www.google.com/maps/search/mental+health+centers+{location_input.replace(' ', '+')}" - # st.markdown(f'🗺️ View Mental Health Centers Near {location_input}', unsafe_allow_html=True) - # st.success("Opening search results in a new tab...") - # else: - # st.warning("Please enter a city name") + # Only one column for header + column = st.columns(1)[0] + with column: + st.markdown(""" +
+

TalkHeal

+

Your Mental Health Companion 💙

+
+ """, unsafe_allow_html=True) diff --git a/components/login_page.py b/components/login_page.py new file mode 100644 index 0000000..a84e209 --- /dev/null +++ b/components/login_page.py @@ -0,0 +1,72 @@ +import streamlit as st +from auth.auth_utils import register_user, authenticate_user + +def show_login_page(): + st.markdown( + """ + + """, unsafe_allow_html=True + ) + + # Use session state to switch between login and signup + if "show_signup" not in st.session_state: + st.session_state.show_signup = False + + if st.session_state.show_signup: + st.subheader("📝 Sign Up") + name = st.text_input("Name", key="signup_name") + email = st.text_input("Email", key="signup_email") + password = st.text_input("Password", type="password", key="signup_password") + if st.button("Sign Up"): + success, message = register_user(name, email, password) + if success: + st.success("Account created! Welcome.") + st.session_state.authenticated = True + st.session_state.user_email = email + st.session_state.user_name = name + st.session_state.show_signup = False + st.rerun() + else: + st.error(message) + # Only this button for switching to login + if st.button("Already have an account? Login"): + st.session_state.show_signup = False + st.rerun() + else: + st.subheader("🔐 Login") + email = st.text_input("Email", key="login_email") + password = st.text_input("Password", type="password", key="login_password") + if st.button("Login"): + success, user = authenticate_user(email, password) + if success: + st.session_state.authenticated = True + st.session_state.user_email = user["email"] + st.session_state.user_name = user["name"] + st.rerun() + else: + st.warning("Invalid email or password.") + # Only this button for switching to signup + if st.button("Don't have an account? Sign up"): + st.session_state.show_signup = True + st.rerun() \ No newline at end of file diff --git a/users.db b/users.db index 4053dcb89f88865e196f9cd727c98fa1cbddf862..1931765848cb489d53d33d07a6ff7138341f9456 100644 GIT binary patch delta 153 zcmZo@U~Fh$oFL7}Hc`fzm5o8Ktb1e10)8%LzK;z2v-xfLK5iBic+BVH#lprQD%!|u zXk=^x!YW2dDuzZX#sP+Yrs>AT*=fdZX1<30<@yChNqQ9)l@Xcci5XFzxn)j~#$JIT z*`6tW?iGH%mf=+aiIZ*Rk8?5e?`7cM%YTgj(Plw~_57S1%%Y4SgIP8o)3+30WB~xi C)hhA; delta 62 zcmZo@U~Fh$oFL7}JW<+)0)7rAep?3q+5EPf1r-eWC)>&&2Z|hH;6KLy RXtSWfF8 Date: Thu, 31 Jul 2025 19:29:14 +0530 Subject: [PATCH 46/50] fix: hide sidebar and chat UI on login screen using session_state --- TalkHeal.py | 92 +++++++++++++++++------------------- pink.png => assets/pink.png | Bin components/header.py | 44 ++++------------- components/login_page.py | 72 ++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 83 deletions(-) rename pink.png => assets/pink.png (100%) create mode 100644 components/login_page.py diff --git a/TalkHeal.py b/TalkHeal.py index ca0d692..98e9c5f 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -1,69 +1,49 @@ import streamlit as st -from auth.auth_utils import init_db, register_user, authenticate_user +from auth.auth_utils import init_db +from components.login_page import show_login_page st.set_page_config(page_title="TalkHeal", page_icon="💬", layout="wide") +# --- DB Initialization --- if "db_initialized" not in st.session_state: init_db() st.session_state["db_initialized"] = True +# --- Auth State Initialization --- if "authenticated" not in st.session_state: st.session_state.authenticated = False if "show_signup" not in st.session_state: st.session_state.show_signup = False -def show_login_ui(): - st.subheader("🔐 Login") - email = st.text_input("Email", key="login_email") - password = st.text_input("Password", type="password", key="login_password") - if st.button("Login"): - success, user = authenticate_user(email, password) - if success: - st.session_state.authenticated = True - # Set user_email and user_name separately for journaling page access - st.session_state.user_email = user["email"] - st.session_state.user_name = user["name"] +# --- LOGIN PAGE --- +if not st.session_state.authenticated: + show_login_page() + st.stop() + +# --- TOP RIGHT BUTTONS: THEME TOGGLE & LOGOUT --- +if st.session_state.get("authenticated", False): + col_spacer, col_theme, col_logout = st.columns([5, 1, 1]) + with col_spacer: + pass # empty spacer to push buttons right + with col_theme: + is_dark = st.session_state.get('dark_mode', False) + if st.button("🌙" if is_dark else "☀️", key="top_theme_toggle", help="Toggle Light/Dark Mode", use_container_width=True): + st.session_state.dark_mode = not is_dark + st.session_state.theme_changed = True st.rerun() - else: - st.warning("Invalid email or password.") - st.markdown("Don't have an account? [Sign up](#)", unsafe_allow_html=True) - if st.button("Go to Sign Up"): - st.session_state.show_signup = True - st.rerun() - -def show_signup_ui(): - st.subheader("📝 Sign Up") - name = st.text_input("Name", key="signup_name") - email = st.text_input("Email", key="signup_email") - password = st.text_input("Password", type="password", key="signup_password") - if st.button("Sign Up"): - success, message = register_user(name, email, password) - if success: - st.success("Account created! Please log in.") - st.session_state.show_signup = False + with col_logout: + if st.button("Logout", key="logout_btn", use_container_width=True): + for key in ["authenticated", "user_email", "user_name", "show_signup"]: + if key in st.session_state: + del st.session_state[key] st.rerun() - else: - st.error(message) - st.markdown("Already have an account? [Login](#)", unsafe_allow_html=True) - if st.button("Go to Login"): - st.session_state.show_signup = False - st.rerun() -if not st.session_state.authenticated: - if st.session_state.show_signup: - show_signup_ui() - else: - show_login_ui() -else: +# --- MAIN UI (only after login) --- +header_col1, header_col2, header_col3 = st.columns([6, 1, 1]) +with header_col1: st.title(f"Welcome to TalkHeal, {st.session_state.user_name}! 💬") st.markdown("Navigate to other pages from the sidebar.") - if st.button("Logout"): - for key in ["authenticated", "user_email", "user_name", "show_signup"]: - if key in st.session_state: - del st.session_state[key] - st.rerun() - import google.generativeai as genai from core.utils import save_conversations, load_conversations from core.config import configure_gemini, PAGE_CONFIG @@ -170,4 +150,20 @@ def get_tone_prompt(): } setTimeout(scrollToBottom, 100); -""", unsafe_allow_html=True) \ No newline at end of file +""", unsafe_allow_html=True) + +st.markdown(""" + +""", unsafe_allow_html=True) \ No newline at end of file diff --git a/pink.png b/assets/pink.png similarity index 100% rename from pink.png rename to assets/pink.png diff --git a/components/header.py b/components/header.py index 43bd86f..6a1224c 100644 --- a/components/header.py +++ b/components/header.py @@ -2,38 +2,12 @@ def render_header(): with st.container(): - # Top bar with hamburger menu and theme toggle - col1, col2, col3 = st.columns([0.1, 0.8, 0.1]) - - with col1: - if st.button("☰", key="top_hamburger_menu", help="Toggle Sidebar", use_container_width=True): - if st.session_state.sidebar_state == "expanded": - st.session_state.sidebar_state = "collapsed" - else: - st.session_state.sidebar_state = "expanded" - st.rerun() - - with col3: - is_dark = st.session_state.get('dark_mode', False) - if st.button("🌙" if is_dark else "☀️", key="top_theme_toggle", help="Toggle Light/Dark Mode", use_container_width=True): - st.session_state.dark_mode = not is_dark - st.session_state.theme_changed = True - st.rerun() - - st.markdown(""" -
-

TalkHeal

-

Your Mental Health Companion 💙

-
- """, unsafe_allow_html=True) - ## Commented out this part of the header because the 'emergency button' functionality is quite similar, hence causing redundancy. ## - - # with st.expander("📍 Find Help Nearby"): - # location_input = st.text_input("Enter your city", key="header_location_search") - # if st.button("🔍 Search Centers", key="header_search_nearby"): - # if location_input: - # search_url = f"https://www.google.com/maps/search/mental+health+centers+{location_input.replace(' ', '+')}" - # st.markdown(f'🗺️ View Mental Health Centers Near {location_input}', unsafe_allow_html=True) - # st.success("Opening search results in a new tab...") - # else: - # st.warning("Please enter a city name") + # Only one column for header + column = st.columns(1)[0] + with column: + st.markdown(""" +
+

TalkHeal

+

Your Mental Health Companion 💙

+
+ """, unsafe_allow_html=True) diff --git a/components/login_page.py b/components/login_page.py new file mode 100644 index 0000000..a84e209 --- /dev/null +++ b/components/login_page.py @@ -0,0 +1,72 @@ +import streamlit as st +from auth.auth_utils import register_user, authenticate_user + +def show_login_page(): + st.markdown( + """ + + """, unsafe_allow_html=True + ) + + # Use session state to switch between login and signup + if "show_signup" not in st.session_state: + st.session_state.show_signup = False + + if st.session_state.show_signup: + st.subheader("📝 Sign Up") + name = st.text_input("Name", key="signup_name") + email = st.text_input("Email", key="signup_email") + password = st.text_input("Password", type="password", key="signup_password") + if st.button("Sign Up"): + success, message = register_user(name, email, password) + if success: + st.success("Account created! Welcome.") + st.session_state.authenticated = True + st.session_state.user_email = email + st.session_state.user_name = name + st.session_state.show_signup = False + st.rerun() + else: + st.error(message) + # Only this button for switching to login + if st.button("Already have an account? Login"): + st.session_state.show_signup = False + st.rerun() + else: + st.subheader("🔐 Login") + email = st.text_input("Email", key="login_email") + password = st.text_input("Password", type="password", key="login_password") + if st.button("Login"): + success, user = authenticate_user(email, password) + if success: + st.session_state.authenticated = True + st.session_state.user_email = user["email"] + st.session_state.user_name = user["name"] + st.rerun() + else: + st.warning("Invalid email or password.") + # Only this button for switching to signup + if st.button("Don't have an account? Sign up"): + st.session_state.show_signup = True + st.rerun() \ No newline at end of file From 9760c95fd69c2d37e8bdd8c86687091d16210cfe Mon Sep 17 00:00:00 2001 From: Pragnya Khandelwal Date: Thu, 31 Jul 2025 21:55:58 +0530 Subject: [PATCH 47/50] update --- TalkHeal.py | 2 +- streamlit.toml | 2 +- users.db | Bin 16384 -> 16384 bytes 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TalkHeal.py b/TalkHeal.py index 98e9c5f..785d7e1 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -22,7 +22,7 @@ # --- TOP RIGHT BUTTONS: THEME TOGGLE & LOGOUT --- if st.session_state.get("authenticated", False): - col_spacer, col_theme, col_logout = st.columns([5, 1, 1]) + col_spacer, col_theme, col_logout = st.columns([5, 0.5, 0.7]) with col_spacer: pass # empty spacer to push buttons right with col_theme: diff --git a/streamlit.toml b/streamlit.toml index 0be0cd3..f694a7d 100644 --- a/streamlit.toml +++ b/streamlit.toml @@ -1,2 +1,2 @@ -GEMINI_API_KEY = "AIzaSyDsLJgA58LvgFtnUdVBLFb08GZQV0wXYjQ" +GEMINI_API_KEY = "AIzaSyDFmW-jSijEoql28pUYXTlVJJGxeK-uPyc" diff --git a/users.db b/users.db index 11ed19f17fc26156ad5bbe45e1b921cabaeec243..af1dad59815edd26831379f3c33ec4bca84e7595 100644 GIT binary patch delta 162 zcmZo@U~Fh$oFL7}Hc`fzm5o8Ktb1e10)9SLK6eKG+5EPA*ZC&%xo;K}Fy-^{0?Lbu zHnJKT8JmExicyk^p^=JFR#;-5PgG%EUV(F-w~@13fRnFJsjF*Fv425ES!7UDrCwoV zT3DK+Z&X&kr)g=Zzi~wA=E-t}jC`#8(;4^|@K5Jo%72Xi(Plw~R(?(nW>H3v>8zU< K=*J5%vH}3o1qA>uhAFXRpbrcL4wC>6n-4t>*AA1jAs~Yevy&ci a0R{vQpa2h`vkV}M50Ov{v!E|u5CH^zauS*V From 4955a5cc1b90fefd293b969f2b5ae8f89be95b73 Mon Sep 17 00:00:00 2001 From: Deenu Bansal Date: Thu, 31 Jul 2025 23:40:09 +0530 Subject: [PATCH 48/50] Add keyboard navigation for mood slider with ARIA attributes --- TalkHeal.py | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/TalkHeal.py b/TalkHeal.py index ca0d692..234c356 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -75,7 +75,6 @@ def show_signup_ui(): from components.emergency_page import render_emergency_page from components.profile import apply_global_font_size - # --- 1. INITIALIZE SESSION STATE --- if "chat_history" not in st.session_state: st.session_state.chat_history = [] @@ -100,7 +99,6 @@ def show_signup_ui(): # --- 2. SET PAGE CONFIG --- apply_global_font_size() - # --- 3. APPLY STYLES & CONFIGURATIONS --- apply_custom_css() model = configure_gemini() @@ -156,6 +154,76 @@ def get_tone_prompt():

🗣️ Current Chatbot Tone: {st.session_state['selected_tone']}

""", unsafe_allow_html=True) + + # --- Mood Slider with Keyboard Navigation --- + def mood_slider(): + slider_html = """ +
+ + +
Neutral
+ + +
+ """ + mood_value = st.components.v1.html(slider_html, height=100, key="mood_slider") + return mood_value + + st.subheader("😊 Track Your Mood") + mood = mood_slider() + if mood: + st.write(f"Selected mood: {['Very Sad', 'Sad', 'Neutral', 'Happy', 'Very Happy'][mood - 1]}") + # AI-assisted coping tips based on mood + coping_tips = { + 1: "It’s okay to feel this way. Try some deep breathing exercises to find calm.", + 2: "Consider writing down your thoughts in the journal to process your feelings.", + 3: "A short walk or some light stretching might help you feel balanced.", + 4: "Great to hear you’re feeling happy! Share something positive in your journal.", + 5: "You’re shining today! Keep spreading that positivity with a kind act." + } + st.write(f"Coping tip: {coping_tips.get(mood, 'Let’s explore how you’re feeling.')}") + render_chat_interface() handle_chat_input(model, system_prompt=get_tone_prompt()) @@ -170,4 +238,4 @@ def get_tone_prompt(): } setTimeout(scrollToBottom, 100); -""", unsafe_allow_html=True) \ No newline at end of file +""", unsafe_allow_html=True) \ No newline at end of file From 0283fa52acd3159360cdb7b39384164908100561 Mon Sep 17 00:00:00 2001 From: Pragnya Date: Fri, 1 Aug 2025 23:52:23 +0530 Subject: [PATCH 49/50] Update streamlit.toml --- streamlit.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streamlit.toml b/streamlit.toml index f694a7d..5cdfe5c 100644 --- a/streamlit.toml +++ b/streamlit.toml @@ -1,2 +1,2 @@ -GEMINI_API_KEY = "AIzaSyDFmW-jSijEoql28pUYXTlVJJGxeK-uPyc" +GEMINI_API_KEY = "GEMINI_API_KEY" From 2a4080b59bd9e0e3a8acf7452a083027baf96b0f Mon Sep 17 00:00:00 2001 From: Deenu Bansal Date: Sat, 2 Aug 2025 14:30:45 +0530 Subject: [PATCH 50/50] Resolved merge conflict in TalkHeal.py --- TalkHeal.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/TalkHeal.py b/TalkHeal.py index df278d4..cc28d6e 100644 --- a/TalkHeal.py +++ b/TalkHeal.py @@ -187,22 +187,25 @@ def mood_slider(): """ - mood_value = st.components.v1.html(slider_html, height=100, key="mood_slider") + mood_value = st.components.v1.html(slider_html, height=100) return mood_value + # --- Mood Slider --- st.subheader("😊 Track Your Mood") - mood = mood_slider() - if mood: - st.write(f"Selected mood: {['Very Sad', 'Sad', 'Neutral', 'Happy', 'Very Happy'][mood - 1]}") - # AI-assisted coping tips based on mood - coping_tips = { - 1: "It’s okay to feel this way. Try some deep breathing exercises to find calm.", - 2: "Consider writing down your thoughts in the journal to process your feelings.", - 3: "A short walk or some light stretching might help you feel balanced.", - 4: "Great to hear you’re feeling happy! Share something positive in your journal.", - 5: "You’re shining today! Keep spreading that positivity with a kind act." - } - st.write(f"Coping tip: {coping_tips.get(mood, 'Let’s explore how you’re feeling.')}") + mood_options = ['Very Sad', 'Sad', 'Neutral', 'Happy', 'Very Happy'] + mood = st.slider( + 'Select your mood', + min_value=1, max_value=5, value=3, step=1 +) + coping_tips = { + 1: "It’s okay to feel this way. Try some deep breathing exercises to find calm.", + 2: "Consider writing down your thoughts in the journal to process your feelings.", + 3: "A short walk or some light stretching might help you feel balanced.", + 4: "Great to hear you’re feeling happy! Share something positive in your journal.", + 5: "You’re shining today! Keep spreading that positivity with a kind act." +} + st.write(f"Selected mood: {mood_options[mood-1]}") + st.write(f"Coping tip: {coping_tips.get(mood, 'Let’s explore how you’re feeling.')}") render_chat_interface() handle_chat_input(model, system_prompt=get_tone_prompt())

- - GitHub Stars - - - GitHub Forks - - - Contributors - - - Open Issues - - - Open Pull Requests - - - Last Commit - - - License - +