diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..36994f10c53a560e7dee296a9825ff7bd6d81dad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +GS_Sales_Proposal/Client/__pycache__/client.cpython-313.pyc filter=lfs diff=lfs merge=lfs -text +GS_Sales_Proposal/Document_Upload_Vectordb/rfi2.pdf filter=lfs diff=lfs merge=lfs -text diff --git a/GS_Sales_Proposal/.env b/GS_Sales_Proposal/.env new file mode 100644 index 0000000000000000000000000000000000000000..50431fb9b5c3896c6fd81554bba5b9d138f0d0f8 --- /dev/null +++ b/GS_Sales_Proposal/.env @@ -0,0 +1,3 @@ +GOOGLE_API_KEY=AIzaSyC_ojLXS1ysa83mUerhxxNMWIK10Z1MqjQ +SERP_API_KEY = 0900f7a2f830083d80385bc46c1ff1f1e6626da3f568de640fe13759e9655450 +FILE_SAVE_PATH = "Document_Upload_Vectordb/Files" \ No newline at end of file diff --git a/GS_Sales_Proposal/.gitignore b/GS_Sales_Proposal/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..956ccff2485eec22cacfb79c455f8dc74558f7fa --- /dev/null +++ b/GS_Sales_Proposal/.gitignore @@ -0,0 +1,5 @@ +.env +.pycache +.pyc +.ipynb +.__pycache__ \ No newline at end of file diff --git a/GS_Sales_Proposal/Client/__init__.py b/GS_Sales_Proposal/Client/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/GS_Sales_Proposal/Client/__pycache__/__init__.cpython-313.pyc b/GS_Sales_Proposal/Client/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0102e5c833e842b959d9f32c9cd259cda02a0002 Binary files /dev/null and b/GS_Sales_Proposal/Client/__pycache__/__init__.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Client/__pycache__/client.cpython-313.pyc b/GS_Sales_Proposal/Client/__pycache__/client.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..affe6cd4138a17da09a522fcc29982c0541abfa3 --- /dev/null +++ b/GS_Sales_Proposal/Client/__pycache__/client.cpython-313.pyc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bcc0854139a4b9db9c029168978b5677fb1df4293bd788a31cbef0c5b5dfd83 +size 104585 diff --git a/GS_Sales_Proposal/Client/__pycache__/client_css.cpython-313.pyc b/GS_Sales_Proposal/Client/__pycache__/client_css.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f24615e7b96b33293608e6aa201aba5df79b036f Binary files /dev/null and b/GS_Sales_Proposal/Client/__pycache__/client_css.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Client/__pycache__/client_dataclass.cpython-313.pyc b/GS_Sales_Proposal/Client/__pycache__/client_dataclass.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0f6008c55831df2a9c57aa23678aad2605b5ca4 Binary files /dev/null and b/GS_Sales_Proposal/Client/__pycache__/client_dataclass.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Client/__pycache__/client_utils.cpython-313.pyc b/GS_Sales_Proposal/Client/__pycache__/client_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6949f2a19cec1d490965492d4e58cb33be1239bc Binary files /dev/null and b/GS_Sales_Proposal/Client/__pycache__/client_utils.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Client/client.py b/GS_Sales_Proposal/Client/client.py new file mode 100644 index 0000000000000000000000000000000000000000..da7a84ca781fbde1b92e68693bf8f5c3238d3f47 --- /dev/null +++ b/GS_Sales_Proposal/Client/client.py @@ -0,0 +1,2111 @@ +import streamlit as st +import pandas as pd +import os +from typing import List +from .client_utils import * +import threading +import time +from Search.Linkedin.linkedin_serp import * +from Recommendation.recommendation_utils import * +from .client_css import * +from .client_dataclass import ClientData, ClientDataManager + + +def save_uploaded_file_and_get_path(uploaded_file): + """Save uploaded file to a temporary directory and return the file path""" + if uploaded_file is not None: + # Create uploads directory if it doesn't exist + upload_dir =os.getenv("FILE_SAVE_PATH") + if not os.path.exists(upload_dir): + os.makedirs(upload_dir) + + # Create file path + file_path = os.path.join(upload_dir, uploaded_file.name) + + # Save the file + with open(file_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + + return file_path + return None + + +def validate_client_mandatory_fields(): + """Validate client mandatory fields using dataclass""" + client_data = ClientDataManager.get_client_data() + return True + return client_data.validate_mandatory_fields() + + +def client_tab(st): + # Get client data from dataclass manager + client_data = ClientDataManager.get_client_data() + + # Apply CSS only once + if not client_data.css_applied: + st.markdown(client_css, unsafe_allow_html=True) + #st.markdown(focus_styles,unsafe_allow_html = True) + ClientDataManager.update_client_data(css_applied=True) + + # Re-apply CSS after every rerun to ensure persistence + st.markdown(client_css, unsafe_allow_html=True) + + # Top section with client name and URLs + col1, col2 = st.columns([1, 1]) + + with col1: + st.markdown(""" +
+ Client Enterprise Name * +
ā“˜
+
+ """, unsafe_allow_html=True) + + # Create a sub-column layout for name input and find URLs button + name_col, button_col = st.columns([3, 1]) + + with name_col: + client_enterprise_name = st.text_input( + label="Client Enterprise Name", + value=client_data.enterprise_name, + placeholder="Enter client enterprise name...", + key="client_enterprise_name_input", + label_visibility="collapsed", + + ) + # Update dataclass when input changes + if client_enterprise_name != client_data.enterprise_name: + ClientDataManager.update_client_data(enterprise_name=client_enterprise_name) + + with button_col: + # Find URLs button - only enabled when client name has more than 2 characters + find_urls_disabled = not (client_enterprise_name and len(client_enterprise_name.strip()) > 2) + + if st.button("šŸ” Find Website", + disabled=find_urls_disabled, + help="Find website URLs for this company", + key="find_urls_button", + type = "secondary"): + # Add spinner while fetching URLs + with st.spinner(f"Finding Websites for '{client_enterprise_name.strip()}'..."): + try: + urls_list = get_urls_list(client_enterprise_name.strip()) + ClientDataManager.update_client_data( + website_urls_list=urls_list, + enterprise_name=client_enterprise_name # Update last company name + ) + except Exception as e: + ClientDataManager.update_client_data(website_urls_list=[]) + st.error(f"Error finding URLs: {str(e)}") + + # Clear URLs if company name is cleared + if not client_enterprise_name and client_data.enterprise_name: + ClientDataManager.update_client_data( + website_urls_list=[], + enterprise_name="" + ) + + # Show validation warning if triggered and field is empty + if client_data.show_validation and check_field_validation("Client Enterprise Name", client_enterprise_name, True): + show_field_warning("Client Enterprise Name") + + with col2: + # Label row with inline emoji and tooltip + st.markdown(''' +
+ Client Website URL +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Create columns for dropdown and buttons + url_col, btn1_col, btn2_col, btn3_col = st.columns([7, 1, 1, 1]) + + with url_col: + # URL selection logic + client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip()) + + if not client_data.website_urls_list: + url_options = ["Select client website URL"] + else: + url_options = ["Select client website URL"] + client_data.website_urls_list + + # Set default selection + default_index = 0 + if client_data.website_url and client_data.website_url in url_options: + default_index = url_options.index(client_data.website_url) + + client_website_url = st.selectbox( + label="Client Website URL", + options=url_options, + index=default_index, + key="client_website_url_selector", + label_visibility="collapsed", + disabled=not client_name_provided, + accept_new_options=True + ) + + # Reset to empty string if default option is selected + if client_website_url == "Select client website URL": + client_website_url = "" + + # Update dataclass when URL changes + if client_website_url != client_data.website_url: + ClientDataManager.update_client_data(website_url=client_website_url) + + # Buttons for website actions + with btn1_col: + if client_website_url: + st.link_button("🌐", client_website_url, help="Visit website", use_container_width=True) + else: + st.button("🌐", help="Visit website", disabled=True, use_container_width=True) + + with btn2_col: + refresh_clicked = st.button("šŸ”„", help="Refresh website URLs list", key="refresh_urls_btn", + use_container_width=True, disabled=not client_website_url) + + with btn3_col: + scrape_clicked = st.button("šŸ“‘", help="Get enterprise details", key="scrape_website_btn", + use_container_width=True, disabled=not client_website_url) + + if scrape_clicked and client_website_url: + ClientDataManager.update_client_data( + pending_scrape_url=client_website_url, + scraping_in_progress=True + ) + st.rerun() + + # Handle refresh action + if refresh_clicked and client_name_provided: + try: + with st.spinner("Refreshing website URLs..."): + urls_list = get_urls_list(client_enterprise_name) + ClientDataManager.update_client_data(website_urls_list=urls_list) + st.success("Website URLs refreshed!") + st.rerun() + except Exception as e: + st.error(f"Error refreshing URLs: {str(e)}") + + # Handle pending scraping operation + if client_data.scraping_in_progress and client_data.pending_scrape_url: + with st.spinner(f"Scraping website details from {client_data.pending_scrape_url}..."): + try: + website_details = get_url_details(client_data.pending_scrape_url) + ClientDataManager.update_client_data( + enterprise_details_content=website_details, + last_analyzed_url=client_data.pending_scrape_url, + scraping_in_progress=False, + pending_scrape_url=None + ) + st.success("Website details extracted successfully!") + st.rerun() + except Exception as e: + ClientDataManager.update_client_data( + scraping_in_progress=False, + pending_scrape_url=None + ) + st.error(f"Error scraping website: {str(e)}") + + # Show validation warning for URL field + if client_data.show_validation and check_field_validation("Client Website URL", client_website_url, False): + show_field_warning("Client Website URL") + + # File upload and enterprise details section + col3, col4 = st.columns([1, 1]) + + with col3: + st.markdown(''' +
+ Upload RFI Document +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Add custom CSS for file uploader + st.markdown(""" + + """, unsafe_allow_html=True) + + # FILE UPLOAD + rfi_document_upload = st.file_uploader( + label="Upload RFI Document", + type=['pdf', 'docx', 'txt', 'csv', 'png', 'jpg', 'jpeg'], + key="rfi_document_uploader", + label_visibility="collapsed" + ) + + # Show file info and analyze button + if rfi_document_upload is not None: + file_size_kb = round(rfi_document_upload.size / 1024, 1) + file_size_display = f"{file_size_kb}KB" if file_size_kb < 1024 else f"{round(file_size_kb/1024, 1)}MB" + + # Single compact row + col_info, col_btn = st.columns([2.5, 1]) + + with col_info: + if client_data.processing_rfi: + st.markdown(f""" +
+ + šŸ”„ {rfi_document_upload.name[:20]}{'...' if len(rfi_document_upload.name) > 20 else ''} (Analyzing...) + +
+ """, unsafe_allow_html=True) + else: + st.markdown(f"šŸ“„ {rfi_document_upload.name[:25]}{'...' if len(rfi_document_upload.name) > 25 else ''} ({file_size_display})", + unsafe_allow_html=True) + + with col_btn: + button_color = "#FF6B6B" if client_data.processing_rfi else "#4CAF50" + st.markdown(f""" + + """, unsafe_allow_html=True) + + analyze_clicked = st.button( + "Analyzing..." if client_data.processing_rfi else "Get pain points", + key="analyze_rfi_document_btn", + help="Process RFI document" if not client_data.processing_rfi else "Processing in progress...", + type="secondary", + disabled=client_data.processing_rfi, + use_container_width=True + ) + + # Handle analyze button click + if analyze_clicked and not client_data.processing_rfi: + if not client_enterprise_name: + st.error("āŒ Please enter the Client Enterprise Name first") + else: + ClientDataManager.update_client_data(processing_rfi=True) + st.rerun() + + # Show processing indicator + if client_data.processing_rfi: + with st.container(): + col_spinner, col_text = st.columns([0.5, 4]) + with col_spinner: + with st.spinner(''): + pass + with col_text: + st.markdown("**šŸ” Analyzing RFI document and extracting key insights...**") + + # Perform the actual processing + try: + file_path = save_uploaded_file_and_get_path(rfi_document_upload) + if file_path and client_enterprise_name: + pain_points_data = get_pain_points(file_path, client_enterprise_name) + ClientDataManager.update_client_data( + uploaded_file_path=file_path, + rfi_pain_points_items=pain_points_data, + document_analyzed=True, + processing_rfi=False + ) + st.success("āœ… RFI document analyzed successfully!") + st.rerun() + else: + st.error("āŒ Error saving the uploaded file") + ClientDataManager.update_client_data(processing_rfi=False) + except Exception as e: + st.error(f"āŒ Error analyzing RFI document: {str(e)}") + ClientDataManager.update_client_data( + rfi_pain_points_items={}, + document_analyzed=False, + processing_rfi=False + ) + + with col4: + st.markdown(''' +
+ Client Enterprise Details +
ā“˜
+
+ ''', unsafe_allow_html=True) + + client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip()) + + enterprise_details = st.text_area( + label="Client Enterprise Details", + value=client_data.enterprise_details_content if client_name_provided else "", + placeholder="Enter client name first to enable this field" if not client_name_provided else "Select/Enter the client website URL to fetch enterprise details", + height=150, + key="enterprise_details_textarea", + label_visibility="collapsed", + disabled=not client_name_provided + ) + + # Update dataclass when text area changes + if client_name_provided and enterprise_details != client_data.enterprise_details_content: + ClientDataManager.update_client_data(enterprise_details_content=enterprise_details) + + # Client Requirements and Pain Points Row + col5, col6 = st.columns([1, 1]) + + with col5: + st.markdown(''' +
+ Client Requirements * +
ā“˜
+
+ ''', unsafe_allow_html=True) + + client_requirements = st.text_area( + label="Client Requirements", + value=client_data.client_requirements_content if client_name_provided else "", + height=200, + key="client_requirements_textarea", + label_visibility="collapsed", + disabled=not client_name_provided, + placeholder="Enter client name first to enable this field" if not client_name_provided else "Add your client requirements here youmay take suggestions from AI in the right as well" + ) + + # Update the client data when the text area changes (only if enabled) + if client_name_provided: + ClientDataManager.update_client_data(client_requirements_content=client_requirements) + + client_requirements_provided = bool(client_name_provided and client_requirements.strip()) + + with col6: + # Title with tooltip only (no buttons) + st.markdown(''' +
+ Client Pain Points +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Get RFI pain points items from client data or use dummy data + if client_name_provided and client_data.rfi_pain_points_items: + rfi_pain_points_items = client_data.rfi_pain_points_items + else: + # Dummy data when no client name or no file uploaded + rfi_pain_points_items = { + "Revenue Challenges": "**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\n• Average deal size decreased by 22% due to increased price competition\n• Customer churn rate increased to 18%, up from 12% previous year\n• Revenue per customer dropped 8% as clients downgraded service tiers\n• New product launches generated only 60% of projected revenue\n• Seasonal revenue fluctuations creating 40% variance between peak and low periods\n• Pipeline conversion rates fell from 35% to 24% over past 12 months\n\n", + + "Cost and Margin Pressure": "**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\n• Labor costs rose 18% while productivity remained flat\n• Raw material prices up 25% with limited ability to pass costs to customers\n• Operational efficiency decreased by 14% due to outdated processes\n• Procurement costs increased 20% from supplier consolidation issues\n• Technology infrastructure costs grew 30% without proportional business benefits\n• Regulatory compliance expenses added $1.8M in unexpected annual costs\n• Facility and overhead costs up 16% while revenue remained stagnant\n\n", + + "Market Expansion and Customer Acquisition": "**Market Expansion and Customer Acquisition**\n\n • Win rate on new business opportunities dropped from 42% to 28%\n• Customer acquisition cost increased 35% while customer lifetime value declined\n• Expansion into new geographic markets yielding only 40% of projected results\n• Lack of local market knowledge resulting in 60% longer sales cycles\n• Digital marketing campaigns generating 50% fewer qualified leads\n• Competition from new market entrants capturing 25% of target customer segment\n• Limited brand recognition in new markets requiring 3x marketing investment\n• Difficulty penetrating enterprise accounts with average sales cycle extending to 18 months\n\n" + } + + # Use a single container for all pain points items + with st.container(): + # Display pain points items with add/remove buttons + for i, (key, value) in enumerate(rfi_pain_points_items.items()): + # Check if this item is selected + is_selected = key in client_data.selected_pain_points + + # Create a box container with +/- button and content on same horizontal level + col_add, col_content = st.columns([0.5, 9], gap="medium") + + with col_add: + # Style the button to align vertically with the content box + st.markdown(""" + + """, unsafe_allow_html=True) + + # Change button appearance based on selection state + button_text = "āŒ" if is_selected else "āž•" + button_help = f"Remove '{key}' from client requirements" if is_selected else f"Add '{key}' to client requirements section" + button_type = "secondary" + + if st.button(button_text, + key=f"toggle_rfi_pain_point_item_{i}", + help=button_help, + type=button_type, + disabled=not client_name_provided): + + if is_selected: + # REMOVE FUNCTIONALITY + # Get current content from the client data + current_content = client_data.client_requirements_content + + # Get the original content that was added for this key + original_content = client_data.pain_point_content_map.get(key, value) + + # Remove this specific pain point section from content + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + updated_content = updated_content.replace(pattern, "") + + # Clean up any excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update client data + client_data.selected_pain_points.discard(key) + if key in client_data.pain_point_content_map: + del client_data.pain_point_content_map[key] + + ClientDataManager.update_client_data( + client_requirements_content=updated_content, + selected_pain_points=client_data.selected_pain_points, + pain_point_content_map=client_data.pain_point_content_map + ) + + else: + # ADD FUNCTIONALITY + # Get current content from client data + current_content = client_data.client_requirements_content + + # Append the value to the content + new_content = current_content + f"\n\n{value}" if current_content else value + + # Update client data + client_data.selected_pain_points.add(key) + client_data.pain_point_content_map[key] = value + + ClientDataManager.update_client_data( + client_requirements_content=new_content, + selected_pain_points=client_data.selected_pain_points, + pain_point_content_map=client_data.pain_point_content_map + ) + + st.rerun() + + with col_content: + # Style the content box based on selection state + if is_selected: + background_color = "#2e7d32" + border_color = "#5a9f9f" + text_color = "#ffffff" + icon = "āœ…" + box_shadow = "0 2px 8px rgba(76, 175, 80, 0.3)" + else: + background_color = "#f5f5f5" + border_color = "#5a9f9f" + text_color = "#000000" + icon = "šŸ“‹" + box_shadow = "0 2px 4px rgba(0,0,0,0.1)" + + st.markdown(f""" +
+ {icon} {key} +
+ """, unsafe_allow_html=True) + + # SPOC Row +import streamlit as st +import pandas as pd +import os +import logging +from typing import List +from .client_utils import * +import threading +import time +from Search.Linkedin.linkedin_serp import * +from Recommendation.recommendation_utils import * +from .client_css import * +from .client_dataclass import ClientData, ClientDataManager + +# Configure logging +def setup_logging(): + """Setup logging configuration for client module""" + try: + # Create logs directory if it doesn't exist + logs_dir = "logs" + if not os.path.exists(logs_dir): + os.makedirs(logs_dir) + + # Configure logger + logger = logging.getLogger('client_module') + logger.setLevel(logging.DEBUG) + + # Remove existing handlers to avoid duplicates + for handler in logger.handlers[:]: + logger.removeHandler(handler) + + # Create file handler + file_handler = logging.FileHandler(os.path.join(logs_dir, 'client_logs.log')) + file_handler.setLevel(logging.DEBUG) + + # Create console handler + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.INFO) + + # Create formatter + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s' + ) + file_handler.setFormatter(formatter) + console_handler.setFormatter(formatter) + + # Add handlers to logger + logger.addHandler(file_handler) + logger.addHandler(console_handler) + + return logger + except Exception as e: + print(f"Error setting up logging: {str(e)}") + return logging.getLogger('client_module') + +# Initialize logger +logger = setup_logging() + +def save_uploaded_file_and_get_path(uploaded_file): + """Save uploaded file to a temporary directory and return the file path""" + logger.info(f"Starting file upload process for file: {uploaded_file.name if uploaded_file else 'None'}") + + try: + if uploaded_file is not None: + logger.debug(f"File details - Name: {uploaded_file.name}, Size: {uploaded_file.size} bytes") + + # Create uploads directory if it doesn't exist + upload_dir = os.getenv("FILE_SAVE_PATH") + logger.debug(f"Upload directory path: {upload_dir}") + + if not os.path.exists(upload_dir): + try: + os.makedirs(upload_dir) + logger.info(f"Created upload directory: {upload_dir}") + except OSError as e: + logger.error(f"Failed to create upload directory {upload_dir}: {str(e)}") + raise + + # Create file path + file_path = os.path.join(upload_dir, uploaded_file.name) + logger.debug(f"Full file path: {file_path}") + + # Save the file + try: + with open(file_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + logger.info(f"Successfully saved file to: {file_path}") + return file_path + except IOError as e: + logger.error(f"Failed to save file {file_path}: {str(e)}") + raise + + else: + logger.warning("No file provided for upload") + return None + + except Exception as e: + logger.error(f"Unexpected error in save_uploaded_file_and_get_path: {str(e)}") + raise + + +def validate_client_mandatory_fields(): + """Validate client mandatory fields using dataclass""" + logger.info("Starting client mandatory fields validation") + + try: + client_data = ClientDataManager.get_client_data() + logger.debug("Retrieved client data for validation") + + # Temporarily return True - validation disabled + logger.info("Validation bypassed - returning True") + return True + + # Uncomment below for actual validation + # result = client_data.validate_mandatory_fields() + # logger.info(f"Validation result: {result}") + # return result + + except Exception as e: + logger.error(f"Error in validate_client_mandatory_fields: {str(e)}") + return False + + +def client_tab(st): + logger.info("Starting client_tab function") + + try: + # Get client data from dataclass manager + client_data = ClientDataManager.get_client_data() + logger.debug("Retrieved client data from dataclass manager") + + # Apply CSS only once + try: + if not client_data.css_applied: + logger.debug("Applying CSS for the first time") + st.markdown(client_css, unsafe_allow_html=True) + ClientDataManager.update_client_data(css_applied=True) + logger.info("CSS applied and updated in client data") + + # Re-apply CSS after every rerun to ensure persistence + st.markdown(client_css, unsafe_allow_html=True) + logger.debug("CSS re-applied for persistence") + + except Exception as e: + logger.error(f"Error applying CSS: {str(e)}") + st.error("Error loading page styles") + + # Top section with client name and URLs + logger.debug("Creating top section with client name and URLs") + col1, col2 = st.columns([1, 1]) + + with col1: + try: + logger.debug("Processing client enterprise name section") + + st.markdown(""" +
+ Client Enterprise Name * +
ā“˜
+
+ """, unsafe_allow_html=True) + + # Create a sub-column layout for name input and find URLs button + name_col, button_col = st.columns([3, 1]) + + with name_col: + client_enterprise_name = st.text_input( + label="Client Enterprise Name", + value=client_data.enterprise_name, + placeholder="Enter client enterprise name...", + key="client_enterprise_name_input", + label_visibility="collapsed", + ) + + # Update dataclass when input changes + if client_enterprise_name != client_data.enterprise_name: + try: + ClientDataManager.update_client_data(enterprise_name=client_enterprise_name) + logger.info(f"Updated enterprise name: {client_enterprise_name}") + except Exception as e: + logger.error(f"Error updating enterprise name: {str(e)}") + + with button_col: + try: + # Find URLs button - only enabled when client name has more than 2 characters + find_urls_disabled = not (client_enterprise_name and len(client_enterprise_name.strip()) > 2) + logger.debug(f"Find URLs button disabled: {find_urls_disabled}") + + if st.button("šŸ” Find Website", + disabled=find_urls_disabled, + help="Find website URLs for this company", + key="find_urls_button", + type="secondary"): + + logger.info(f"Find URLs button clicked for: {client_enterprise_name.strip()}") + + # Add spinner while fetching URLs + with st.spinner(f"Finding Websites for '{client_enterprise_name.strip()}'..."): + try: + urls_list = get_urls_list(client_enterprise_name.strip()) + logger.info(f"Found {len(urls_list)} URLs for {client_enterprise_name.strip()}") + + ClientDataManager.update_client_data( + website_urls_list=urls_list, + enterprise_name=client_enterprise_name + ) + logger.debug("Updated client data with URLs list") + + except Exception as e: + logger.error(f"Error finding URLs for {client_enterprise_name.strip()}: {str(e)}") + ClientDataManager.update_client_data(website_urls_list=[]) + st.error(f"Error finding URLs: {str(e)}") + + except Exception as e: + logger.error(f"Error in Find URLs button section: {str(e)}") + + # Clear URLs if company name is cleared + try: + if not client_enterprise_name and client_data.enterprise_name: + logger.info("Company name cleared, clearing URLs list") + ClientDataManager.update_client_data( + website_urls_list=[], + enterprise_name="" + ) + except Exception as e: + logger.error(f"Error clearing URLs when company name cleared: {str(e)}") + + # Show validation warning if triggered and field is empty + try: + if client_data.show_validation and check_field_validation("Client Enterprise Name", client_enterprise_name, True): + show_field_warning("Client Enterprise Name") + logger.debug("Showed validation warning for Client Enterprise Name") + except Exception as e: + logger.error(f"Error showing validation warning: {str(e)}") + + except Exception as e: + logger.error(f"Error in client enterprise name column: {str(e)}") + st.error("Error in client name section") + + with col2: + try: + logger.debug("Processing client website URL section") + + # Label row with inline emoji and tooltip + st.markdown(''' +
+ Client Website URL +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Create columns for dropdown and buttons + url_col, btn1_col, btn2_col, btn3_col = st.columns([7, 1, 1, 1]) + + with url_col: + try: + # URL selection logic + client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip()) + logger.debug(f"Client name provided: {client_name_provided}") + + if not client_data.website_urls_list: + url_options = ["Select client website URL"] + else: + url_options = ["Select client website URL"] + client_data.website_urls_list + + logger.debug(f"URL options count: {len(url_options)}") + + # Set default selection + default_index = 0 + if client_data.website_url and client_data.website_url in url_options: + default_index = url_options.index(client_data.website_url) + + client_website_url = st.selectbox( + label="Client Website URL", + options=url_options, + index=default_index, + key="client_website_url_selector", + label_visibility="collapsed", + disabled=not client_name_provided, + accept_new_options=True + ) + + # Reset to empty string if default option is selected + if client_website_url == "Select client website URL": + client_website_url = "" + + # Update dataclass when URL changes + if client_website_url != client_data.website_url: + try: + ClientDataManager.update_client_data(website_url=client_website_url) + logger.info(f"Updated website URL: {client_website_url}") + except Exception as e: + logger.error(f"Error updating website URL: {str(e)}") + + except Exception as e: + logger.error(f"Error in URL selection: {str(e)}") + client_website_url = "" + + # Buttons for website actions + with btn1_col: + try: + if client_website_url: + st.link_button("🌐", client_website_url, help="Visit website", use_container_width=True) + else: + st.button("🌐", help="Visit website", disabled=True, use_container_width=True) + except Exception as e: + logger.error(f"Error creating visit website button: {str(e)}") + + with btn2_col: + try: + refresh_clicked = st.button("šŸ”„", help="Refresh website URLs list", key="refresh_urls_btn", + use_container_width=True, disabled=not client_website_url) + except Exception as e: + logger.error(f"Error creating refresh button: {str(e)}") + refresh_clicked = False + + with btn3_col: + try: + scrape_clicked = st.button("šŸ“‘", help="Get enterprise details", key="scrape_website_btn", + use_container_width=True, disabled=not client_website_url) + + if scrape_clicked and client_website_url: + logger.info(f"Scrape button clicked for URL: {client_website_url}") + ClientDataManager.update_client_data( + pending_scrape_url=client_website_url, + scraping_in_progress=True + ) + st.rerun() + except Exception as e: + logger.error(f"Error creating scrape button: {str(e)}") + scrape_clicked = False + + # Handle refresh action + if refresh_clicked and client_name_provided: + try: + logger.info(f"Refreshing URLs for: {client_enterprise_name}") + with st.spinner("Refreshing website URLs..."): + urls_list = get_urls_list(client_enterprise_name) + ClientDataManager.update_client_data(website_urls_list=urls_list) + logger.info(f"Successfully refreshed URLs, found {len(urls_list)} URLs") + st.success("Website URLs refreshed!") + st.rerun() + except Exception as e: + logger.error(f"Error refreshing URLs: {str(e)}") + st.error(f"Error refreshing URLs: {str(e)}") + + # Handle pending scraping operation + if client_data.scraping_in_progress and client_data.pending_scrape_url: + try: + logger.info(f"Starting website scraping for: {client_data.pending_scrape_url}") + with st.spinner(f"Scraping website details from {client_data.pending_scrape_url}..."): + try: + website_details = get_url_details(client_data.pending_scrape_url) + + # Check if scraping returned empty or no data + if not website_details or len(website_details.strip()) == 0: + logger.warning(f"Website scraping returned empty data for: {client_data.pending_scrape_url}") + ClientDataManager.update_client_data( + scraping_in_progress=False, + pending_scrape_url=None + ) + st.error("āš ļø Website scraping failed - No content could be extracted from the website. Please check if the URL is accessible and contains readable content.") + time.sleep(3) + st.rerun() + else: + logger.info(f"Successfully scraped website details, length: {len(website_details)}") + ClientDataManager.update_client_data( + enterprise_details_content=website_details, + last_analyzed_url=client_data.pending_scrape_url, + scraping_in_progress=False, + pending_scrape_url=None + ) + st.success("āœ… Website details extracted successfully!") + st.rerun() + + except Exception as e: + logger.error(f"Error scraping website {client_data.pending_scrape_url}: {str(e)}") + ClientDataManager.update_client_data( + scraping_in_progress=False, + pending_scrape_url=None + ) + st.error(f"āŒ Error scraping website: {str(e)}") + except Exception as e: + logger.error(f"Error in scraping operation: {str(e)}") + except Exception as e: + logger.error(f"Error in scraping operation: {str(e)}") + st.error(f"āŒ Error scraping website: {str(e)}") + + # File upload and enterprise details section + logger.debug("Creating file upload and enterprise details section") + col3, col4 = st.columns([1, 1]) + + with col3: + try: + logger.debug("Processing file upload section") + + st.markdown(''' +
+ Upload RFI Document +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Add custom CSS for file uploader + st.markdown(""" + + """, unsafe_allow_html=True) + + # FILE UPLOAD + try: + rfi_document_upload = st.file_uploader( + label="Upload RFI Document", + type=['pdf', 'docx', 'txt', 'csv', 'png', 'jpg', 'jpeg'], + key="rfi_document_uploader", + label_visibility="collapsed" + ) + + if rfi_document_upload is not None: + logger.info(f"File uploaded: {rfi_document_upload.name}") + + except Exception as e: + logger.error(f"Error creating file uploader: {str(e)}") + rfi_document_upload = None + + # Show file info and analyze button + if rfi_document_upload is not None: + try: + file_size_kb = round(rfi_document_upload.size / 1024, 1) + file_size_display = f"{file_size_kb}KB" if file_size_kb < 1024 else f"{round(file_size_kb/1024, 1)}MB" + logger.debug(f"File size: {file_size_display}") + + # Single compact row + col_info, col_btn = st.columns([2.5, 1]) + + with col_info: + if client_data.processing_rfi: + st.markdown(f""" +
+ + šŸ”„ {rfi_document_upload.name[:20]}{'...' if len(rfi_document_upload.name) > 20 else ''} (Analyzing...) + +
+ """, unsafe_allow_html=True) + else: + st.markdown(f"šŸ“„ {rfi_document_upload.name[:25]}{'...' if len(rfi_document_upload.name) > 25 else ''} ({file_size_display})", + unsafe_allow_html=True) + + with col_btn: + try: + button_color = "#FF6B6B" if client_data.processing_rfi else "#4CAF50" + st.markdown(f""" + + """, unsafe_allow_html=True) + + analyze_clicked = st.button( + "Analyzing..." if client_data.processing_rfi else "Get pain points", + key="analyze_rfi_document_btn", + help="Process RFI document" if not client_data.processing_rfi else "Processing in progress...", + type="secondary", + disabled=client_data.processing_rfi, + use_container_width=True + ) + + except Exception as e: + logger.error(f"Error creating analyze button: {str(e)}") + analyze_clicked = False + + # Handle analyze button click + if analyze_clicked and not client_data.processing_rfi: + try: + if not client_enterprise_name: + logger.warning("Analyze clicked but no client enterprise name provided") + st.error("āŒ Please enter the Client Enterprise Name first") + else: + logger.info("Starting RFI analysis process") + ClientDataManager.update_client_data(processing_rfi=True) + st.rerun() + except Exception as e: + logger.error(f"Error handling analyze button click: {str(e)}") + + # Show processing indicator + if client_data.processing_rfi: + try: + with st.container(): + col_spinner, col_text = st.columns([0.5, 4]) + with col_spinner: + with st.spinner(''): + pass + with col_text: + st.markdown("**šŸ” Analyzing RFI document and extracting key insights...**") + + # Perform the actual processing + try: + logger.info("Starting RFI document processing") + file_path = save_uploaded_file_and_get_path(rfi_document_upload) + + if file_path and client_enterprise_name: + logger.info(f"Processing RFI file: {file_path}") + pain_points_data = get_pain_points(file_path, client_enterprise_name) + logger.info(f"Successfully extracted pain points, count: {len(pain_points_data) if pain_points_data else 0}") + + ClientDataManager.update_client_data( + uploaded_file_path=file_path, + rfi_pain_points_items=pain_points_data, + document_analyzed=True, + processing_rfi=False + ) + st.success("āœ… RFI document analyzed successfully!") + st.rerun() + else: + logger.error("Error saving the uploaded file or missing client name") + st.error("āŒ Error saving the uploaded file") + ClientDataManager.update_client_data(processing_rfi=False) + + except Exception as e: + logger.error(f"Error analyzing RFI document: {str(e)}") + st.error(f"āŒ Error analyzing RFI document: {str(e)}") + ClientDataManager.update_client_data( + rfi_pain_points_items={}, + document_analyzed=False, + processing_rfi=False + ) + + except Exception as e: + logger.error(f"Error in processing indicator section: {str(e)}") + + except Exception as e: + logger.error(f"Error in file info section: {str(e)}") + + except Exception as e: + logger.error(f"Error in file upload column: {str(e)}") + st.error("Error in file upload section") + + with col4: + try: + logger.debug("Processing enterprise details section") + + st.markdown(''' +
+ Client Enterprise Details +
ā“˜
+
+ ''', unsafe_allow_html=True) + + client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip()) + + try: + enterprise_details = st.text_area( + label="Client Enterprise Details", + value=client_data.enterprise_details_content if client_name_provided else "", + placeholder="Enter client name first to enable this field" if not client_name_provided else "Select/Enter the client website URL to fetch enterprise details", + height=150, + key="enterprise_details_textarea", + label_visibility="collapsed", + disabled=not client_name_provided + ) + + # Update dataclass when text area changes + if client_name_provided and enterprise_details != client_data.enterprise_details_content: + try: + ClientDataManager.update_client_data(enterprise_details_content=enterprise_details) + logger.debug("Updated enterprise details content") + except Exception as e: + logger.error(f"Error updating enterprise details: {str(e)}") + + except Exception as e: + logger.error(f"Error creating enterprise details textarea: {str(e)}") + + except Exception as e: + logger.error(f"Error in enterprise details column: {str(e)}") + st.error("Error in enterprise details section") + + # Client Requirements and Pain Points Row + logger.debug("Creating client requirements and pain points section") + col5, col6 = st.columns([1, 1]) + + with col5: + try: + logger.debug("Processing client requirements section") + + st.markdown(''' +
+ Client Requirements * +
ā“˜
+
+ ''', unsafe_allow_html=True) + + try: + client_requirements = st.text_area( + label="Client Requirements", + value=client_data.client_requirements_content if client_name_provided else "", + height=200, + key="client_requirements_textarea", + label_visibility="collapsed", + disabled=not client_name_provided, + placeholder="Enter client name first to enable this field" if not client_name_provided else "Add your client requirements here youmay take suggestions from AI in the right as well" + ) + + # Update the client data when the text area changes (only if enabled) + if client_name_provided: + try: + ClientDataManager.update_client_data(client_requirements_content=client_requirements) + logger.debug("Updated client requirements content") + except Exception as e: + logger.error(f"Error updating client requirements: {str(e)}") + + client_requirements_provided = bool(client_name_provided and client_requirements.strip()) + logger.debug(f"Client requirements provided: {client_requirements_provided}") + + except Exception as e: + logger.error(f"Error creating client requirements textarea: {str(e)}") + client_requirements = "" + client_requirements_provided = False + + except Exception as e: + logger.error(f"Error in client requirements column: {str(e)}") + st.error("Error in client requirements section") + + + # Continue from COL6 with enhanced logging and error handling + with col6: + try: + logger.info("Starting COL6 - Client Pain Points section rendering") + + # Title with tooltip only (no buttons) + st.markdown(''' +
+ Client Pain Points +
ā“˜
+
+ ''', unsafe_allow_html=True) + + try: + # Get RFI pain points items from client data or use dummy data + if client_name_provided and client_data.rfi_pain_points_items: + rfi_pain_points_items = client_data.rfi_pain_points_items + logger.info(f"Using client RFI pain points data with {len(rfi_pain_points_items)} items") + else: + # Dummy data when no client name or no file uploaded + rfi_pain_points_items = { + "Revenue Challenges": "**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\n• Average deal size decreased by 22% due to increased price competition\n• Customer churn rate increased to 18%, up from 12% previous year\n• Revenue per customer dropped 8% as clients downgraded service tiers\n• New product launches generated only 60% of projected revenue\n• Seasonal revenue fluctuations creating 40% variance between peak and low periods\n• Pipeline conversion rates fell from 35% to 24% over past 12 months\n\n", + + "Cost and Margin Pressure": "**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\n• Labor costs rose 18% while productivity remained flat\n• Raw material prices up 25% with limited ability to pass costs to customers\n• Operational efficiency decreased by 14% due to outdated processes\n• Procurement costs increased 20% from supplier consolidation issues\n• Technology infrastructure costs grew 30% without proportional business benefits\n• Regulatory compliance expenses added $1.8M in unexpected annual costs\n• Facility and overhead costs up 16% while revenue remained stagnant\n\n", + + "Market Expansion and Customer Acquisition": "**Market Expansion and Customer Acquisition**\n\n • Win rate on new business opportunities dropped from 42% to 28%\n• Customer acquisition cost increased 35% while customer lifetime value declined\n• Expansion into new geographic markets yielding only 40% of projected results\n• Lack of local market knowledge resulting in 60% longer sales cycles\n• Digital marketing campaigns generating 50% fewer qualified leads\n• Competition from new market entrants capturing 25% of target customer segment\n• Limited brand recognition in new markets requiring 3x marketing investment\n• Difficulty penetrating enterprise accounts with average sales cycle extending to 18 months\n\n" + } + logger.info("Using dummy pain points data as fallback") + + except Exception as e: + logger.error(f"Error retrieving pain points data: {str(e)}") + rfi_pain_points_items = {} + st.error("Error loading pain points data") + + # Use a single container for all pain points items + try: + with st.container(): + logger.debug(f"Rendering {len(rfi_pain_points_items)} pain point items") + + # Display pain points items with add/remove buttons + for i, (key, value) in enumerate(rfi_pain_points_items.items()): + try: + logger.debug(f"Processing pain point item {i}: {key}") + + # Check if this item is selected + try: + is_selected = key in client_data.selected_pain_points + logger.debug(f"Pain point '{key}' selection status: {is_selected}") + except Exception as e: + logger.error(f"Error checking selection status for '{key}': {str(e)}") + is_selected = False + + # Create a box container with +/- button and content on same horizontal level + col_add, col_content = st.columns([0.5, 9], gap="medium") + + with col_add: + try: + # Style the button to align vertically with the content box + st.markdown(""" + + """, unsafe_allow_html=True) + + # Change button appearance based on selection state + button_text = "āŒ" if is_selected else "āž•" + button_help = f"Remove '{key}' from client requirements" if is_selected else f"Add '{key}' to client requirements section" + button_type = "secondary" + + if st.button(button_text, + key=f"toggle_rfi_pain_point_item_{i}", + help=button_help, + type=button_type, + disabled=not client_name_provided): + + logger.info(f"Pain point button clicked for '{key}', current selection: {is_selected}") + + try: + if is_selected: + # REMOVE FUNCTIONALITY + logger.info(f"Removing pain point '{key}' from requirements") + + try: + # Get current content from the client data + current_content = client_data.client_requirements_content + logger.debug(f"Current content length: {len(current_content) if current_content else 0}") + + # Get the original content that was added for this key + original_content = client_data.pain_point_content_map.get(key, value) + logger.debug(f"Original content to remove length: {len(original_content)}") + + # Remove this specific pain point section from content + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + if pattern in updated_content: + updated_content = updated_content.replace(pattern, "") + logger.debug(f"Removed pattern from content") + break + + # Clean up any excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update client data + client_data.selected_pain_points.discard(key) + if key in client_data.pain_point_content_map: + del client_data.pain_point_content_map[key] + + ClientDataManager.update_client_data( + client_requirements_content=updated_content, + selected_pain_points=client_data.selected_pain_points, + pain_point_content_map=client_data.pain_point_content_map + ) + + logger.info(f"Successfully removed pain point '{key}'") + + except Exception as e: + logger.error(f"Error in remove functionality for '{key}': {str(e)}") + st.error(f"Error removing pain point: {str(e)}") + + else: + # ADD FUNCTIONALITY + logger.info(f"Adding pain point '{key}' to requirements") + + try: + # Get current content from client data + current_content = client_data.client_requirements_content + logger.debug(f"Current content length before add: {len(current_content) if current_content else 0}") + + # Append the value to the content + new_content = current_content + f"\n\n{value}" if current_content else value + logger.debug(f"New content length after add: {len(new_content)}") + + # Update client data + client_data.selected_pain_points.add(key) + client_data.pain_point_content_map[key] = value + + ClientDataManager.update_client_data( + client_requirements_content=new_content, + selected_pain_points=client_data.selected_pain_points, + pain_point_content_map=client_data.pain_point_content_map + ) + + logger.info(f"Successfully added pain point '{key}'") + + except Exception as e: + logger.error(f"Error in add functionality for '{key}': {str(e)}") + st.error(f"Error adding pain point: {str(e)}") + + st.rerun() + + except Exception as e: + logger.error(f"Error handling button click for '{key}': {str(e)}") + st.error(f"Error processing pain point selection: {str(e)}") + + except Exception as e: + logger.error(f"Error rendering button for pain point '{key}': {str(e)}") + st.error(f"Error rendering button: {str(e)}") + + with col_content: + try: + # Style the content box based on selection state + if is_selected: + background_color = "#2e7d32" + border_color = "#5a9f9f" + text_color = "#ffffff" + icon = "āœ…" + box_shadow = "0 2px 8px rgba(76, 175, 80, 0.3)" + else: + background_color = "#f5f5f5" + border_color = "#5a9f9f" + text_color = "#000000" + icon = "šŸ“‹" + box_shadow = "0 2px 4px rgba(0,0,0,0.1)" + + st.markdown(f""" +
+ {icon} {key} +
+ """, unsafe_allow_html=True) + + logger.debug(f"Successfully rendered content box for '{key}'") + + except Exception as e: + logger.error(f"Error rendering content box for '{key}': {str(e)}") + st.error(f"Error rendering content: {str(e)}") + + except Exception as e: + logger.error(f"Error processing pain point item {i} ('{key}'): {str(e)}") + st.error(f"Error processing pain point item: {str(e)}") + + except Exception as e: + logger.error(f"Error rendering pain points container: {str(e)}") + st.error("Error loading pain points section") + + except Exception as e: + logger.error(f"Critical error in COL6 rendering: {str(e)}") + st.error("Critical error in pain points section") + + # SPOC Row + except Exception as e: + logger.error(f"Error in SPOC row: {str(e)}") + col_spoc1, col_spoc2 = st.columns([1, 1]) + + with col_spoc1: + try: + st.markdown(''' +
+ SPOC Name +
ā“˜
+
+ ''', unsafe_allow_html=True) + logger.info("Rendered SPOC name tooltip") + except Exception as e: + logger.error(f"Error rendering SPOC name tooltip: {str(e)}") + + try: + spoc_name = st.text_input( + label="SPOC Name", + value=client_data.spoc_name, + placeholder="Enter SPOC full name...", + key="spoc_name_input", + label_visibility="collapsed", + disabled=not client_name_provided + ) + logger.info(f"SPOC name input rendered with value: {spoc_name}") + except Exception as e: + logger.error(f"Error creating SPOC name input: {str(e)}") + spoc_name = "" + + try: + # Update client data when SPOC name changes + if spoc_name != client_data.spoc_name: + ClientDataManager.update_client_data(spoc_name=spoc_name) + logger.info(f"Updated SPOC name to: {spoc_name}") + except Exception as e: + logger.error(f"Error updating SPOC name: {str(e)}") + + try: + # Automatically search for LinkedIn profiles when SPOC name changes + if spoc_name and spoc_name.strip() and spoc_name != client_data.last_searched_spoc and client_name_provided: + with st.spinner(f"Searching LinkedIn profiles for {spoc_name}..."): + # Assuming search_linkedin_serpapi is available + linkedin_profiles = search_linkedin_serpapi(spoc_name.strip()) + ClientDataManager.update_client_data( + linkedin_profiles=linkedin_profiles, + last_searched_spoc=spoc_name + ) + logger.info(f"LinkedIn search completed for: {spoc_name}") + st.rerun() + except Exception as e: + logger.error(f"Error searching LinkedIn profiles: {str(e)}") + + with col_spoc2: + try: + # Check if SPOC name is provided (for disabling LinkedIn field) + spoc_name_provided = bool(spoc_name and spoc_name.strip()) and client_name_provided + logger.info(f"SPOC name provided status: {spoc_name_provided}") + except Exception as e: + logger.error(f"Error checking SPOC name provided status: {str(e)}") + spoc_name_provided = False + + try: + st.markdown(''' +
+ Select SPOC LinkedIn Profile +
ā“˜
+
+ ''', unsafe_allow_html=True) + logger.info("Rendered LinkedIn profile tooltip") + except Exception as e: + logger.error(f"Error rendering LinkedIn profile tooltip: {str(e)}") + + try: + # Prepare LinkedIn profile options + if spoc_name_provided and client_data.linkedin_profiles: + # Create options with profile titles for better selection + linkedin_options = ["Select a LinkedIn profile..."] + linkedin_url_mapping = {} # To map display text to actual URL + + for url, profile_data in client_data.linkedin_profiles.items(): + display_text = f"{profile_data['role']} ({profile_data['name']})" + linkedin_options.append(display_text) + linkedin_url_mapping[display_text] = url + + selected_linkedin_display = st.selectbox( + label="SPOC LinkedIn Profile", + options=linkedin_options, + key="spoc_linkedin_profile_selector", + label_visibility="collapsed", + disabled=not spoc_name_provided, + accept_new_options=True, + ) + + # Extract the actual URL from the selected option + if selected_linkedin_display != "Select a LinkedIn profile...": + spoc_linkedin_profile = linkedin_url_mapping[selected_linkedin_display] + ClientDataManager.update_client_data(spoc_linkedin_profile=spoc_linkedin_profile) + logger.info(f"LinkedIn profile selected: {spoc_linkedin_profile}") + else: + spoc_linkedin_profile = None + logger.info("No LinkedIn profile selected") + + elif spoc_name_provided and not client_data.linkedin_profiles: + # Show message when no profiles found + st.selectbox( + label="SPOC LinkedIn Profile", + options=["No LinkedIn profiles found. Try a different name."], + key="spoc_linkedin_profile_selector", + label_visibility="collapsed", + disabled=True, + accept_new_options=True + ) + spoc_linkedin_profile = None + logger.info("No LinkedIn profiles found") + else: + # Default disabled state + spoc_linkedin_profile = st.selectbox( + label="SPOC LinkedIn Profile", + options=["Enter SPOC name to get LinkedIn profiles"], + key="spoc_linkedin_profile_selector", + label_visibility="collapsed", + disabled=not spoc_name_provided, + accept_new_options=True + ) + logger.info("LinkedIn profile selector in disabled state") + except Exception as e: + logger.error(f"Error creating LinkedIn profile selector: {str(e)}") + spoc_linkedin_profile = None + + try: + # Display selected profile information and handle dynamic updates + if spoc_name_provided and client_data.linkedin_profiles: + # Check if LinkedIn profile selection has changed + profile_changed = False + if 'spoc_linkedin_profile' in locals() and spoc_linkedin_profile: + if client_data.current_selected_profile_url != spoc_linkedin_profile: + ClientDataManager.update_client_data(current_selected_profile_url=spoc_linkedin_profile) + profile_changed = True + logger.info(f"LinkedIn profile changed to: {spoc_linkedin_profile}") + + selected_profile_data = client_data.linkedin_profiles.get(spoc_linkedin_profile) + if selected_profile_data: + st.info(f"""**Selected Profile:** {selected_profile_data['role']} - {selected_profile_data['name']} + **LinkedIn URL:** {spoc_linkedin_profile}""") + + # Update roles and priorities when profile changes + if profile_changed: + # Remove previously auto-populated LinkedIn role if it exists + linkedin_roles_to_remove = [] + for i, role in enumerate(client_data.selected_target_roles): + # Check if this role was from a previous LinkedIn profile + for url, profile in client_data.linkedin_profiles.items(): + if url != spoc_linkedin_profile and profile['role'] == role: + linkedin_roles_to_remove.append(i) + break + + # Remove old LinkedIn roles + for idx in reversed(linkedin_roles_to_remove): + client_data.selected_target_roles.pop(idx) + + # Add new LinkedIn role + linkedin_role = selected_profile_data['role'] + if linkedin_role and linkedin_role not in client_data.selected_target_roles: + client_data.selected_target_roles.append(linkedin_role) + + # Remove old LinkedIn priorities and add new ones + linkedin_priorities_to_remove = [] + for priority in client_data.selected_business_priorities: + # Check if this priority was from a previous LinkedIn profile + for url, profile in client_data.linkedin_profiles.items(): + if url != spoc_linkedin_profile and priority in profile.get('top_3_priorities', []): + linkedin_priorities_to_remove.append(priority) + break + + # Remove old LinkedIn priorities + for priority in linkedin_priorities_to_remove: + if priority in client_data.selected_business_priorities: + client_data.selected_business_priorities.remove(priority) + + # Add new LinkedIn priorities + inferred_priorities = selected_profile_data.get('top_3_priorities', []) + for priority in inferred_priorities: + if priority not in client_data.selected_business_priorities: + client_data.selected_business_priorities.append(priority) + + # Update client data + ClientDataManager.update_client_data( + selected_target_roles=client_data.selected_target_roles, + selected_business_priorities=client_data.selected_business_priorities + ) + logger.info("Updated roles and priorities based on LinkedIn profile change") + + # Force rerun to update the display + st.rerun() + elif client_data.current_selected_profile_url is not None: + # Profile was deselected + ClientDataManager.update_client_data(current_selected_profile_url=None) + profile_changed = True + logger.info("LinkedIn profile deselected") + except Exception as e: + logger.error(f"Error handling LinkedIn profile changes: {str(e)}") + + try: + # Roles and Priorities Row + col7, col8 = st.columns([1, 1]) + logger.info("Created roles and priorities columns") + except Exception as e: + logger.error(f"Error creating roles and priorities columns: {str(e)}") + + with col7: + try: + st.markdown(''' +
+ SPOC Role +
ā“˜
+
+ ''', unsafe_allow_html=True) + logger.info("Rendered SPOC role tooltip") + except Exception as e: + logger.error(f"Error rendering SPOC role tooltip: {str(e)}") + + try: + # Prepare role options for dropdown based on LinkedIn profile selection + role_options = ["Select a role..."] + + # Get default roles from function (assuming this function exists) + target_roles_list = get_roles_list() or [] + logger.info(f"Retrieved {len(target_roles_list)} default roles") + + # Check if a LinkedIn profile is selected + selected_linkedin_role = None + if (spoc_name_provided and + client_data.linkedin_profiles and + 'spoc_linkedin_profile' in locals() and + spoc_linkedin_profile): + + # Get the selected LinkedIn profile data + selected_profile_data = client_data.linkedin_profiles.get(spoc_linkedin_profile) + if selected_profile_data: + selected_linkedin_role = selected_profile_data.get('role') + if selected_linkedin_role: + # Show LinkedIn profile role + default roles from get_roles_list() + role_options = ["Select a role...", selected_linkedin_role] + # Add default roles, avoiding duplicates + for role in target_roles_list: + if role not in role_options: + role_options.append(role) + logger.info(f"Added LinkedIn role: {selected_linkedin_role}") + + # If no LinkedIn profile selected, show all available roles + if not selected_linkedin_role: + # Add standard roles from get_roles_list() + role_options.extend(target_roles_list) + + # Add LinkedIn roles if available (but no specific profile selected) + if spoc_name_provided and client_data.linkedin_profiles: + for url, profile_data in client_data.linkedin_profiles.items(): + linkedin_role = profile_data.get('role') + if linkedin_role and linkedin_role not in role_options: + role_options.append(linkedin_role) + except Exception as e: + logger.error(f"Error preparing role options: {str(e)}") + role_options = ["Select a role..."] + + try: + # Determine the default/current value for the selectbox + current_selection = "Select a role..." + if selected_linkedin_role and selected_linkedin_role in role_options: + # Auto-select the LinkedIn role + current_selection = selected_linkedin_role + elif "target_role_selector_dropdown" in st.session_state: + # Keep the current selection if it exists in options + current_value = st.session_state["target_role_selector_dropdown"] + if current_value in role_options: + current_selection = current_value + + # ROLES DROPDOWN - Only one role can be selected + selected_target_role = st.selectbox( + label="Target Role Selector", + options=role_options, + index=role_options.index(current_selection) if current_selection in role_options else 0, + key="target_role_selector_dropdown", + label_visibility="collapsed", + disabled=not (client_name_provided and spoc_name_provided), + accept_new_options=True + ) + logger.info(f"Role selector rendered with selection: {selected_target_role}") + except Exception as e: + logger.error(f"Error creating role selector: {str(e)}") + selected_target_role = None + + try: + # Update client_data with the single selected role + if selected_target_role and selected_target_role != "Select a role...": + # Store as a single role, not a list + client_data.selected_target_role = selected_target_role + ClientDataManager.update_client_data(selected_target_role=selected_target_role) + logger.info(f"Updated selected target role: {selected_target_role}") + else: + client_data.selected_target_role = None + ClientDataManager.update_client_data(selected_target_role=None) + logger.info("Cleared selected target role") + except Exception as e: + logger.error(f"Error updating selected target role: {str(e)}") + + with col8: + try: + # Label with tooltip + st.markdown(''' +
+ SPOC Business priorities +
ā“˜
+
+ ''', unsafe_allow_html=True) + logger.info("Rendered business priorities tooltip") + except Exception as e: + logger.error(f"Error rendering business priorities tooltip: {str(e)}") + + try: + # Default priorities (used if role is not selected or error occurs) + default_priorities = [ + {'title': 'Revenue Growth and Market Share Expansion', 'icon': 'šŸ“ˆ'}, + {'title': 'Profitability and Cost Optimization', 'icon': 'šŸ’°'}, + {'title': 'Digital Transformation and Innovation', 'icon': 'šŸ¤–'} + ] + + # Load role-based priorities if role is selected + business_priorities_list = default_priorities # Start with default + if ( + spoc_name_provided and + hasattr(client_data, 'selected_target_role') and + client_data.selected_target_role and + client_data.selected_target_role != "Select a role..." + ): + try: + role_priorities = get_ai_business_priorities(client_data.selected_target_role) + if role_priorities: + business_priorities_list = role_priorities + logger.info(f"Loaded {len(business_priorities_list)} role-based priorities") + except Exception as priority_error: + logger.error(f"Error loading role-based priorities: {str(priority_error)}") + pass # Silently fall back to default + + logger.info(f"Using {len(business_priorities_list)} business priorities") + except Exception as e: + logger.error(f"Error preparing business priorities: {str(e)}") + business_priorities_list = default_priorities + + try: + # Initialize selected_business_priorities if missing + if not hasattr(client_data, 'selected_business_priorities'): + client_data.selected_business_priorities = [] + logger.info("Initialized selected_business_priorities") + except Exception as e: + logger.error(f"Error initializing selected_business_priorities: {str(e)}") + + try: + # Show checkboxes for priorities + for i, priority in enumerate(business_priorities_list): + priority_title = priority.get('title') if isinstance(priority, dict) else str(priority) + priority_icon = priority.get('icon', 'šŸ“‹') if isinstance(priority, dict) else 'šŸ“‹' + display_text = f"{priority_icon} **{priority_title}**" + + # Determine checkbox state + default_checked = priority_title in client_data.selected_business_priorities + is_enabled = ( + spoc_name_provided and + hasattr(client_data, 'selected_target_role') and + client_data.selected_target_role and + client_data.selected_target_role != "Select a role..." + ) + + is_checked = st.checkbox( + display_text, + key=f"business_priority_checkbox_{i}", + value=default_checked, + disabled=not spoc_name_provided + ) + + # Update selected priorities + if is_checked and priority_title not in client_data.selected_business_priorities: + client_data.selected_business_priorities.append(priority_title) + ClientDataManager.update_client_data(selected_business_priorities=client_data.selected_business_priorities) + logger.info(f"Added business priority: {priority_title}") + elif not is_checked and priority_title in client_data.selected_business_priorities: + client_data.selected_business_priorities.remove(priority_title) + ClientDataManager.update_client_data(selected_business_priorities=client_data.selected_business_priorities) + logger.info(f"Removed business priority: {priority_title}") + except Exception as e: + logger.error(f"Error handling business priorities checkboxes: {str(e)}") + + try: + col9, col10 = st.columns([1, 1]) + logger.info("Created additional requirements columns") + except Exception as e: + logger.error(f"Error creating additional requirements columns: {str(e)}") + + with col9: + try: + st.markdown(''' +
+ Additional Client Requirements +
ā“˜
+
+ ''', unsafe_allow_html=True) + logger.info("Rendered additional client requirements tooltip") + except Exception as e: + logger.error(f"Error rendering additional client requirements tooltip: {str(e)}") + + try: + # TEXT AREA - DISABLED if no client name + client_additional_requirements = st.text_area( + label="Additional Client Requirements", + value=client_data.client_additional_requirements_content if client_name_provided else "", + placeholder="Enter client name first to enable this field" if not client_name_provided else "Enter specific client requirements, expectations, project scope, compliance needs, budget constraints...", + height=200, + key="client_additional_requirements_textarea", + label_visibility="collapsed", + disabled=not client_name_provided + ) + logger.info(f"Additional requirements text area rendered with {len(client_additional_requirements)} characters") + except Exception as e: + logger.error(f"Error creating additional requirements text area: {str(e)}") + client_additional_requirements = "" + + try: + # Update the dataclass when the text area changes (only if enabled) + if client_name_provided and client_additional_requirements != client_data.client_additional_requirements_content: + ClientDataManager.update_client_data(client_additional_requirements_content=client_additional_requirements) + client_data = ClientDataManager.get_client_data() # Refresh reference + logger.info("Updated additional client requirements content") + except Exception as e: + logger.error(f"Error updating additional client requirements: {str(e)}") + + try: + client_additional_requirements_provided = bool(client_name_provided and client_additional_requirements.strip()) + logger.info(f"Additional requirements provided status: {client_additional_requirements_provided}") + except Exception as e: + logger.error(f"Error checking additional requirements provided status: {str(e)}") + client_additional_requirements_provided = False + + with col10: + try: + # Title with tooltip only (no buttons) + st.markdown(''' +
+ Additional Specifications to be considered +
ā“˜
+
+ ''', unsafe_allow_html=True) + logger.info("Rendered additional specifications tooltip") + except Exception as e: + logger.error(f"Error rendering additional specifications tooltip: {str(e)}") + + try: + # Get additional specs items from client data or use dummy data + if client_name_provided and client_data.additional_specs_items: + additional_specs_items = client_data.additional_specs_items + logger.info(f"Using {len(additional_specs_items)} client-specific additional specs") + else: + # Dummy data when no client name or no specific data + additional_specs_items = { + "Technical Infrastructure Requirements": "**Technical Infrastructure Requirements**\n• Cloud hosting with 99.9% uptime SLA and auto-scaling capabilities\n• Multi-region deployment for disaster recovery and performance optimization\n• Integration with existing ERP, CRM, and financial management systems\n• API-first architecture with RESTful services and webhook support\n• Database performance optimization with sub-second query response times\n• Security compliance with SOC2, ISO 27001, and industry-specific regulations\n• Load balancing and CDN implementation for global content delivery\n• Automated backup and recovery systems with point-in-time restoration\n\n", + + "Compliance and Security Standards": "**Compliance and Security Standards**\n• GDPR, CCPA, and regional data privacy regulation compliance\n• End-to-end encryption for data in transit and at rest\n• Multi-factor authentication and role-based access controls\n• Regular security audits and penetration testing protocols\n• Data retention and deletion policies per regulatory requirements\n• Audit trail logging for all system interactions and data changes\n• Incident response plan with 4-hour notification requirements\n• Employee background checks and security clearance verification\n\n", + + "Performance and Scalability Metrics": "**Performance and Scalability Metrics**\n• System response time under 2 seconds for 95% of user interactions\n• Concurrent user capacity of 10,000+ with linear scaling capability\n• Database query optimization with indexing and caching strategies\n• Mobile application performance with offline synchronization\n• Bandwidth optimization for low-connectivity environments\n• Real-time analytics and reporting with sub-minute data refresh\n• Automated performance monitoring with threshold-based alerting\n• Capacity planning with predictive scaling based on usage patterns\n\n" + } + logger.info("Using default additional specs items") + except Exception as e: + logger.error(f"Error preparing additional specs items: {str(e)}") + additional_specs_items = {} + + try: + # Use a single container for all additional specs items + with st.container(): + # Display additional specs items with add/remove buttons + for i, (key, value) in enumerate(additional_specs_items.items()): + try: + # Check if this item is selected + is_selected = key in client_data.selected_additional_specs + + # Create a box container with +/- button and content on same horizontal level + col_add, col_content = st.columns([0.5, 9], gap="medium") + + with col_add: + # Style the button to align vertically with the content box + st.markdown(""" + + """, unsafe_allow_html=True) + + # Change button appearance based on selection state + button_text = "āŒ" if is_selected else "āž•" + button_help = f"Remove '{key}' from additional requirements" if is_selected else f"Add '{key}' to additional requirements section" + button_type = "secondary" + + if st.button(button_text, + key=f"toggle_additional_spec_item_{i}", + help=button_help, + type=button_type, + disabled=not client_name_provided): + + if is_selected: + try: + # REMOVE FUNCTIONALITY + # Get current content from the dataclass + current_content = client_data.client_additional_requirements_content + + # Get the original content that was added for this key + original_content = client_data.additional_specs_content_map.get(key, value) + + # Remove this specific additional spec section from content + # Try multiple removal patterns to be more robust + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + updated_content = updated_content.replace(pattern, "") + + # Clean up any excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update the dataclass + client_data.client_additional_requirements_content = updated_content + client_data.selected_additional_specs.discard(key) + if key in client_data.additional_specs_content_map: + del client_data.additional_specs_content_map[key] + + # Save changes to session state + ClientDataManager.save_client_data(client_data) + logger.info(f"Removed additional spec: {key}") + except Exception as remove_error: + logger.error(f"Error removing additional spec '{key}': {str(remove_error)}") + + else: + try: + # ADD FUNCTIONALITY + # Get current content from the dataclass + current_content = client_data.client_additional_requirements_content + + # Append the value to the content + new_content = current_content + f"\n\n{value}" if current_content else value + + # Update the dataclass + client_data.client_additional_requirements_content = new_content + client_data.additional_specs_content_map[key] = value + client_data.selected_additional_specs.add(key) + + # Save changes to session state + ClientDataManager.save_client_data(client_data) + logger.info(f"Added additional spec: {key}") + except Exception as add_error: + logger.error(f"Error adding additional spec '{key}': {str(add_error)}") + + st.rerun() + + with col_content: + try: + # Style the content box based on selection state + if is_selected: + background_color = "#2e7d32" + border_color = "#5a9f9f" + text_color = "#ffffff" + icon = "āœ…" + box_shadow = "0 2px 8px rgba(76, 175, 80, 0.3)" + else: + background_color = "#f5f5f5" + border_color = "#5a9f9f" + text_color = "#000000" + icon = "šŸ“‹" + box_shadow = "0 2px 4px rgba(0,0,0,0.1)" + + st.markdown(f""" +
+ {icon} {key} +
+ """, unsafe_allow_html=True) + except Exception as content_error: + logger.error(f"Error rendering content box for '{key}': {str(content_error)}") + except Exception as item_error: + logger.error(f"Error processing additional spec item '{key}': {str(item_error)}") + except Exception as e: + logger.error(f"Error displaying additional specs items: {str(e)}") + + try: + # Handle validation trigger from main app + if client_data.show_validation: + # Your validation logic here + logger.info("Validation triggered") + pass + except Exception as e: + logger.error(f"Error handling validation: {str(e)}") \ No newline at end of file diff --git a/GS_Sales_Proposal/Client/client_css.py b/GS_Sales_Proposal/Client/client_css.py new file mode 100644 index 0000000000000000000000000000000000000000..ab8eb0ad6d8c7734da7661ab7ff4f67347b95069 --- /dev/null +++ b/GS_Sales_Proposal/Client/client_css.py @@ -0,0 +1,448 @@ +client_css = """ + +""" \ No newline at end of file diff --git a/GS_Sales_Proposal/Client/client_dataclass.py b/GS_Sales_Proposal/Client/client_dataclass.py new file mode 100644 index 0000000000000000000000000000000000000000..6471ca1ee9657ed15b306486bf2806c4d11eb6cb --- /dev/null +++ b/GS_Sales_Proposal/Client/client_dataclass.py @@ -0,0 +1,210 @@ +from dataclasses import dataclass, field +from typing import List, Dict, Set, Optional +import streamlit as st + +@dataclass +class ClientData: + """Centralized data structure for client information""" + + # Basic client information + enterprise_name: str = "" + website_url: str = "" + website_urls_list: List[str] = field(default_factory=list) + + # Client details and requirements + enterprise_details_content: str = "" + client_requirements_content: str = "" + client_additional_requirements_content: str = "" + + # SPOC information + spoc_name: str = "" + spoc_linkedin_profile: str = "" + linkedin_profiles: Dict[str, Dict] = field(default_factory=dict) + last_searched_spoc: str = "" + current_selected_profile_url: Optional[str] = None + + # File handling + uploaded_file_path: Optional[str] = None + document_analyzed: bool = False + + # Pain points and specifications + rfi_pain_points_items: Dict[str, str] = field(default_factory=dict) + selected_pain_points: Set[str] = field(default_factory=set) + pain_point_content_map: Dict[str, str] = field(default_factory=dict) + + # Additional specifications + additional_specs_items: Dict[str, str] = field(default_factory=dict) + selected_additional_specs: Set[str] = field(default_factory=set) + additional_specs_content_map: Dict[str, str] = field(default_factory=dict) + + # Role and priority management + selected_target_roles: List[str] = field(default_factory=list) + selected_business_priorities: List[str] = field(default_factory=list) + + # UI state management + show_validation: bool = False + processing_rfi: bool = False + scraping_in_progress: bool = False + pending_scrape_url: Optional[str] = None + css_applied: bool = False + last_analyzed_url: Optional[str] = None + debug_mode: bool = False + + def to_dict(self) -> dict: + """Convert dataclass to dictionary for session state storage""" + return { + 'enterprise_name': self.enterprise_name, + 'website_url': self.website_url, + 'website_urls_list': self.website_urls_list, + 'enterprise_details_content': self.enterprise_details_content, + 'client_requirements_content': self.client_requirements_content, + 'client_additional_requirements_content': self.client_additional_requirements_content, + 'spoc_name': self.spoc_name, + 'spoc_linkedin_profile': self.spoc_linkedin_profile, + 'linkedin_profiles': self.linkedin_profiles, + 'last_searched_spoc': self.last_searched_spoc, + 'current_selected_profile_url': self.current_selected_profile_url, + 'uploaded_file_path': self.uploaded_file_path, + 'document_analyzed': self.document_analyzed, + 'rfi_pain_points_items': self.rfi_pain_points_items, + 'selected_pain_points': self.selected_pain_points, + 'pain_point_content_map': self.pain_point_content_map, + 'additional_specs_items': self.additional_specs_items, + 'selected_additional_specs': self.selected_additional_specs, + 'additional_specs_content_map': self.additional_specs_content_map, + 'selected_target_roles': self.selected_target_roles, + 'selected_business_priorities': self.selected_business_priorities, + 'show_validation': self.show_validation, + 'processing_rfi': self.processing_rfi, + 'scraping_in_progress': self.scraping_in_progress, + 'pending_scrape_url': self.pending_scrape_url, + 'css_applied': self.css_applied, + 'last_analyzed_url': self.last_analyzed_url, + 'debug_mode': self.debug_mode + } + + @classmethod + def from_dict(cls, data: dict) -> 'ClientData': + """Create ClientData instance from dictionary""" + return cls( + enterprise_name=data.get('enterprise_name', ''), + website_url=data.get('website_url', ''), + website_urls_list=data.get('website_urls_list', []), + enterprise_details_content=data.get('enterprise_details_content', ''), + client_requirements_content=data.get('client_requirements_content', ''), + client_additional_requirements_content=data.get('client_additional_requirements_content', ''), + spoc_name=data.get('spoc_name', ''), + spoc_linkedin_profile=data.get('spoc_linkedin_profile', ''), + linkedin_profiles=data.get('linkedin_profiles', {}), + last_searched_spoc=data.get('last_searched_spoc', ''), + current_selected_profile_url=data.get('current_selected_profile_url'), + uploaded_file_path=data.get('uploaded_file_path'), + document_analyzed=data.get('document_analyzed', False), + rfi_pain_points_items=data.get('rfi_pain_points_items', {}), + selected_pain_points=set(data.get('selected_pain_points', [])), + pain_point_content_map=data.get('pain_point_content_map', {}), + additional_specs_items=data.get('additional_specs_items', {}), + selected_additional_specs=set(data.get('selected_additional_specs', [])), + additional_specs_content_map=data.get('additional_specs_content_map', {}), + selected_target_roles=data.get('selected_target_roles', []), + selected_business_priorities=data.get('selected_business_priorities', []), + show_validation=data.get('show_validation', False), + processing_rfi=data.get('processing_rfi', False), + scraping_in_progress=data.get('scraping_in_progress', False), + pending_scrape_url=data.get('pending_scrape_url'), + css_applied=data.get('css_applied', False), + last_analyzed_url=data.get('last_analyzed_url'), + debug_mode=data.get('debug_mode', False) + ) + + def validate_mandatory_fields(self) -> bool: + """Validate mandatory fields""" + client_name = self.enterprise_name.strip() + client_requirement = self.client_requirements_content.strip() + + if self.debug_mode: + print(f"DEBUG - Client Name: '{client_name}'") + print(f"DEBUG - Client Requirement: '{client_requirement}'") + print(f"DEBUG - Validation Result: {bool(client_name) and bool(client_requirement)}") + + return bool(client_name) and bool(client_requirement) + + def clear_data(self): + """Clear all client data""" + self.__init__() + + def update_from_ui_inputs(self, **kwargs): + """Update dataclass fields from UI inputs""" + for key, value in kwargs.items(): + if hasattr(self, key): + setattr(self, key, value) + + +class ClientDataManager: + """Manager class for handling ClientData persistence in Streamlit session state""" + + SESSION_KEY = 'client_data' + + @classmethod + def get_client_data(cls) -> ClientData: + """Get ClientData from session state or create new instance""" + if cls.SESSION_KEY not in st.session_state: + st.session_state[cls.SESSION_KEY] = ClientData() + return st.session_state[cls.SESSION_KEY] + + @classmethod + def save_client_data(cls, client_data: ClientData): + """Save ClientData to session state""" + st.session_state[cls.SESSION_KEY] = client_data + + @classmethod + def update_client_data(cls, **kwargs): + """Update specific fields in ClientData""" + client_data = cls.get_client_data() + client_data.update_from_ui_inputs(**kwargs) + cls.save_client_data(client_data) + + @classmethod + def clear_client_data(cls): + """Clear all client data""" + if cls.SESSION_KEY in st.session_state: + del st.session_state[cls.SESSION_KEY] + + @classmethod + def export_to_dict(cls) -> dict: + """Export client data as dictionary""" + client_data = cls.get_client_data() + return client_data.to_dict() + + @classmethod + def import_from_dict(cls, data: dict): + """Import client data from dictionary""" + client_data = ClientData.from_dict(data) + cls.save_client_data(client_data) + + +# Utility functions for backwards compatibility +def validate_client_mandatory_fields() -> bool: + """Validate client mandatory fields using dataclass""" + client_data = ClientDataManager.get_client_data() + return client_data.validate_mandatory_fields() + +def get_client_enterprise_name() -> str: + """Get client enterprise name""" + client_data = ClientDataManager.get_client_data() + return client_data.enterprise_name + +def set_client_enterprise_name(name: str): + """Set client enterprise name""" + ClientDataManager.update_client_data(enterprise_name=name) + +def get_client_requirements() -> str: + """Get client requirements""" + client_data = ClientDataManager.get_client_data() + return client_data.client_requirements_content + +def set_client_requirements(requirements: str): + """Set client requirements""" + ClientDataManager.update_client_data(client_requirements_content=requirements) + +# Add more utility functions as needed... \ No newline at end of file diff --git a/GS_Sales_Proposal/Client/client_utils.py b/GS_Sales_Proposal/Client/client_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..ac7e2d5e150bd3064142dc01959958a4093a42e7 --- /dev/null +++ b/GS_Sales_Proposal/Client/client_utils.py @@ -0,0 +1,140 @@ +import streamlit as st +import pandas as pd +from typing import List +import os + +from WebsiteUrl_Agent.agent_runner import get_urls +import asyncio +from Document_Upload_Vectordb.pain_points_extractor import * +# Function to get URLs (placeholder function) + +def get_urls_list(company_name) -> List[str]: + """ + Placeholder function that returns a list of URLs + Replace this with your actual function that fetches URLs + """ + return asyncio.run(get_urls(company_name)) + +# Function to get LinkedIn profiles (NEW) + + +# Function to get roles list +def get_roles_list() -> List[str]: + """ + Function that returns a list of executive roles + """ + return [ + "CEO (Chief Executive Officer)", + "CMO (Chief Marketing Officer)", + "CTO (Chief Technology Officer)", + "CFO (Chief Financial Officer)", + "COO (Chief Operating Officer)", + "CHRO (Chief Human Resources Officer)", + "CDO (Chief Data Officer)", + "CPO (Chief Product Officer)", + "CRO (Chief Revenue Officer)", + "CIO (Chief Information Officer)" + ] + + +from WebScraper.scrape import get_data + +def get_url_details(url:str): + """Use this if you want to run async function synchronously""" + try: + # Run the async function synchronously + website_details = asyncio.run(get_data(url)) + return website_details + except Exception as e: + print(f"Error: {e}") + return None + +def get_priority_suggestions() -> List[dict]: + """ + Function that returns a list of priority suggestions with titles and descriptions + Replace this with your actual function that fetches priority suggestions + """ + return [ + { + "title": "Digital Transformation Initiative", + "description": "Modernize systems and processes for improved efficiency", + "icon": "šŸš€" + }, + { + "title": "Data Analytics & Business Intelligence", + "description": "Implement advanced analytics for better decision making", + "icon": "šŸ“Š" + }, + { + "title": "Process Optimization & Automation", + "description": "Streamline workflows and reduce manual tasks", + "icon": "šŸ”§" + } + ] + +def get_editable_content() -> str: + """ + Placeholder function that returns editable content + Replace this with your actual function that fetches editable content + """ + return """This is editable content from the function: + +- Project requirements and specifications +- Current implementation status +- Key stakeholder feedback +- Next steps and action items +- Additional notes and observations + +You can modify this content as needed.""" + + +# Function to get summary items (NEW) +# from Rag.rag import get_pain_points + + + + +def get_pain_items(file,company_name): + print("-----------------------------------------------------------") + return get_pain_points(file,company_name) + + + + +def check_field_validation(field_name: str, field_value: str, is_mandatory: bool = False) -> bool: + """Check if field validation should show warning""" + if is_mandatory and not field_value.strip(): + return True + return False + +def show_field_warning(field_name: str): + """Show warning message for mandatory fields""" + st.markdown(f'
āš ļø {field_name} is mandatory and cannot be empty!
', unsafe_allow_html=True) + + +def save_uploaded_file(uploaded_file, save_dir="uploaded_rf_is"): + os.makedirs(save_dir, exist_ok=True) + save_path = os.path.join(save_dir, uploaded_file.name) + + with open(save_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + + return save_path + +def save_uploaded_file_and_get_path(uploaded_file): + """Save uploaded file to a temporary directory and return the file path""" + if uploaded_file is not None: + # Create uploads directory if it doesn't exist + upload_dir = "uploads" + if not os.path.exists(upload_dir): + os.makedirs(upload_dir) + + # Create file path + file_path = os.path.join(upload_dir, uploaded_file.name) + + # Save the file + with open(file_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + + return file_path + return None diff --git a/GS_Sales_Proposal/Document_Upload_Vectordb/__init__.py b/GS_Sales_Proposal/Document_Upload_Vectordb/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/GS_Sales_Proposal/Document_Upload_Vectordb/doc_vectorizer.py b/GS_Sales_Proposal/Document_Upload_Vectordb/doc_vectorizer.py new file mode 100644 index 0000000000000000000000000000000000000000..6ab170c594a1730a4d38e16881025a7f2cf32436 --- /dev/null +++ b/GS_Sales_Proposal/Document_Upload_Vectordb/doc_vectorizer.py @@ -0,0 +1,564 @@ +import os +import base64 +from io import BytesIO +import filetype +from pdf2image import convert_from_path +from datetime import datetime +import hashlib + +# New imports for PPT and DOC support +from pptx import Presentation +from docx import Document +import docx2txt +from langchain_community.document_loaders import UnstructuredPowerPointLoader, Docx2txtLoader +from langchain_community.document_loaders import UnstructuredWordDocumentLoader + +from langchain_core.messages import HumanMessage +from langchain_community.document_loaders import PyPDFLoader +from langchain_chroma import Chroma +from langchain_google_genai import ChatGoogleGenerativeAI +from langchain_text_splitters import RecursiveCharacterTextSplitter +from langchain_huggingface import HuggingFaceEmbeddings +from .prompts import image_prompt # Make sure this exists + +# --- Utility Functions --- + +def get_filename(file_path): + return os.path.splitext(os.path.basename(file_path))[0] + +def get_file_hash(file_path): + """Generate SHA-256 hash of file for duplicate detection""" + try: + with open(file_path, 'rb') as f: + return hashlib.sha256(f.read()).hexdigest() + except: + return None + +def get_file_size(file_path): + """Get file size in bytes""" + try: + return os.path.getsize(file_path) + except: + return None + +def create_base_metadata(file_path, company_name, file_type): + """Create base metadata common to all file types""" + timestamp = datetime.now().isoformat() + + metadata = { + 'company_name': company_name, + 'file_type': file_type, + 'filename': os.path.basename(file_path) if isinstance(file_path, str) else 'uploaded_image', + 'file_path': file_path if isinstance(file_path, str) else None, + 'processed_timestamp': timestamp, + 'processing_date': datetime.now().strftime('%Y-%m-%d'), + 'processing_time': datetime.now().strftime('%H:%M:%S'), + 'chunk_strategy': 'recursive_character_text_splitter', + 'embedding_model': 'huggingface_default' + } + + # Add file-specific metadata if it's a file path + if isinstance(file_path, str) and os.path.exists(file_path): + metadata.update({ + 'file_hash': get_file_hash(file_path), + 'file_size_bytes': get_file_size(file_path), + 'file_extension': os.path.splitext(file_path)[1].lower() + }) + + return metadata + +def file_router(file): + """Enhanced file router with PPT and DOC support""" + try: + # Get file extension first + file_extension = os.path.splitext(file)[1].lower() + + # Handle specific extensions + if file_extension in ['.ppt', '.pptx']: + return 'powerpoint' + elif file_extension in ['.doc', '.docx']: + return 'word_document' + + # Use filetype for other formats + kind = filetype.guess(file) + if kind is None: + return "Unknown" + + file_type = kind.mime + + if file_type.startswith("image/"): + return 'imagesingle' + + # Check if it's a PDF with text or images + if file_type == 'application/pdf' or file_extension == '.pdf': + loader = PyPDFLoader(file) + docs = loader.load() + + if not len(docs[0].page_content.strip()): + return 'imagepdf' + else: + return 'pdf' + + return 'pdf' # Default fallback + + except Exception as e: + print(f"Error in file_router: {e}") + return 'pdf' # Default fallback + +def encode_image(image) -> str: + buffer = BytesIO() + image.save(buffer, format="PNG") + return base64.b64encode(buffer.getvalue()).decode("utf-8") + +# --- LLM Setup --- + +model = ChatGoogleGenerativeAI(model='gemini-2.0-flash') + +def image_summarize(model, base64_image: str, prompt: str) -> str: + msg = model.invoke([ + HumanMessage( + content=[ + {"type": "text", "text": prompt}, + { + "type": "image_url", + "image_url": {"url": f"data:image/png;base64,{base64_image}"}, + }, + ] + ) + ]) + return msg.content + +# --- Image Handlers --- + +def image_handler(image): + base64_img = encode_image(image) + summary = image_summarize(model, base64_img, prompt=image_prompt) + with open('example.txt', 'w') as f: + f.write(summary) + return summary + +def image_handler_append(image): + base64_img = encode_image(image) + summary = image_summarize(model, base64_img, prompt=image_prompt) + with open('example.txt', 'a') as f: + f.write(summary + '\n') + return summary + +# --- PowerPoint Handler --- + +def extract_ppt_content(filepath: str): + """Extract text content from PowerPoint files""" + try: + prs = Presentation(filepath) + full_text = [] + slide_count = 0 + + for slide_num, slide in enumerate(prs.slides, 1): + slide_text = f"=== Slide {slide_num} ===\n" + + for shape in slide.shapes: + if hasattr(shape, "text") and shape.text.strip(): + slide_text += shape.text + "\n" + + # Handle tables in slides + if shape.has_table: + table = shape.table + for row in table.rows: + row_text = [] + for cell in row.cells: + if cell.text.strip(): + row_text.append(cell.text.strip()) + if row_text: + slide_text += " | ".join(row_text) + "\n" + + if slide_text.strip() != f"=== Slide {slide_num} ===": + full_text.append(slide_text) + slide_count += 1 + + return "\n\n".join(full_text), slide_count + + except Exception as e: + print(f"Error extracting PowerPoint content with python-pptx: {e}") + # Fallback to langchain loader + try: + loader = UnstructuredPowerPointLoader(filepath) + docs = loader.load() + content = "\n\n".join([doc.page_content for doc in docs]) + return content, len(docs) + except Exception as fallback_error: + print(f"Fallback PowerPoint loader failed: {fallback_error}") + return f"Error processing PowerPoint file: {str(e)}", 0 + +# --- Word Document Handler --- + +def extract_word_content(filepath: str): + """Extract text content from Word documents""" + try: + file_extension = os.path.splitext(filepath)[1].lower() + + if file_extension == '.docx': + # Use python-docx for .docx files + doc = Document(filepath) + full_text = [] + + # Extract paragraphs + for para in doc.paragraphs: + if para.text.strip(): + full_text.append(para.text) + + # Extract tables + for table in doc.tables: + for row in table.rows: + row_text = [] + for cell in row.cells: + if cell.text.strip(): + row_text.append(cell.text.strip()) + if row_text: + full_text.append(" | ".join(row_text)) + + content = "\n\n".join(full_text) + return content, len(doc.paragraphs) + + elif file_extension == '.doc': + # Use python-docx2txt for .doc files + content = docx2txt.process(filepath) + return content, len(content.split('\n')) + + except Exception as e: + print(f"Error extracting Word content: {e}") + # Fallback to langchain loaders + try: + if filepath.endswith('.docx'): + loader = Docx2txtLoader(filepath) + else: + loader = UnstructuredWordDocumentLoader(filepath) + + docs = loader.load() + content = "\n\n".join([doc.page_content for doc in docs]) + return content, len(docs) + + except Exception as fallback_error: + print(f"Fallback Word loader failed: {fallback_error}") + return f"Error processing Word document: {str(e)}", 0 + +# --- Enhanced Vectorization Functions --- + +def vectorize_text(text: str, company_name: str, filename: str = "text_input", base_metadata: dict = None): + """Vectorize text content with metadata""" + try: + splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=50) + docs = splitter.split_text(text) + + # Create persist directory + persist_directory = os.path.join("chroma_store", company_name, filename) + os.makedirs(persist_directory, exist_ok=True) + + # Create collection name (sanitize company name) + collection_name = f"{company_name}_{filename}".replace(" ", "_").replace("-", "_").lower() + + # Create metadata for each chunk + metadatas = [] + for i, chunk in enumerate(docs): + chunk_metadata = base_metadata.copy() if base_metadata else {} + chunk_metadata.update({ + 'chunk_index': i, + 'chunk_size': len(chunk), + 'total_chunks': len(docs), + 'content_type': 'text', + 'source_document': filename + }) + metadatas.append(chunk_metadata) + + vectorstore = Chroma.from_texts( + texts=docs, + embedding=HuggingFaceEmbeddings(), + metadatas=metadatas, + persist_directory=persist_directory, + collection_name=collection_name + ) + return vectorstore + + except Exception as e: + print(f"Error in vectorize_text: {e}") + # Fallback to in-memory store + metadatas = [{'error': str(e), 'fallback': True} for _ in docs] + vectorstore = Chroma.from_texts( + texts=docs, + embedding=HuggingFaceEmbeddings(), + metadatas=metadatas, + collection_name=f"fallback_{collection_name}" + ) + return vectorstore + +def vectorize_powerpoint(filepath: str, company_name: str): + """Vectorize PowerPoint presentations""" + try: + content, slide_count = extract_ppt_content(filepath) + filename = get_filename(filepath) + + # Create base metadata + base_metadata = create_base_metadata(filepath, company_name, 'powerpoint') + base_metadata.update({ + 'total_slides': slide_count, + 'content_source': 'powerpoint_extraction', + 'extraction_method': 'python_pptx_with_langchain_fallback', + 'supports_tables': True, + 'supports_shapes': True + }) + + return vectorize_text(content, company_name, filename, base_metadata) + + except Exception as e: + print(f"Error in vectorize_powerpoint: {e}") + error_metadata = { + 'error': str(e), + 'file_type': 'powerpoint', + 'extraction_failed': True + } + return vectorize_text("Error processing PowerPoint file", company_name, "error_ppt", error_metadata) + +def vectorize_word_document(filepath: str, company_name: str): + """Vectorize Word documents""" + try: + content, paragraph_count = extract_word_content(filepath) + filename = get_filename(filepath) + + # Create base metadata + base_metadata = create_base_metadata(filepath, company_name, 'word_document') + base_metadata.update({ + 'paragraph_count': paragraph_count, + 'content_source': 'word_extraction', + 'extraction_method': 'python_docx_with_langchain_fallback', + 'supports_tables': True, + 'supports_formatting': True + }) + + return vectorize_text(content, company_name, filename, base_metadata) + + except Exception as e: + print(f"Error in vectorize_word_document: {e}") + error_metadata = { + 'error': str(e), + 'file_type': 'word_document', + 'extraction_failed': True + } + return vectorize_text("Error processing Word document", company_name, "error_doc", error_metadata) + +def vectorize_single_image(image, company_name: str): + """Vectorize single images""" + try: + # Create base metadata for image + base_metadata = create_base_metadata(image, company_name, 'single_image') + base_metadata.update({ + 'content_source': 'ai_image_summary', + 'ai_model_used': 'gemini-2.0-flash', + 'processing_method': 'image_to_text_summary' + }) + + summary = image_handler(image) + filename = "image_single" + return vectorize_text(summary, company_name, filename, base_metadata) + except Exception as e: + print(f"Error in vectorize_single_image: {e}") + error_metadata = {'error': str(e), 'file_type': 'single_image'} + return vectorize_text("Error processing image", company_name, "error_image", error_metadata) + +def vectorize_multiple_images(image_path: str, company_name: str): + """Vectorize PDF with images""" + try: + images = convert_from_path(image_path) + filename = get_filename(image_path) + + # Create base metadata + base_metadata = create_base_metadata(image_path, company_name, 'pdf_images') + base_metadata.update({ + 'total_pages': len(images), + 'content_source': 'ai_image_summary', + 'ai_model_used': 'gemini-2.0-flash', + 'processing_method': 'pdf_to_images_to_text', + 'conversion_tool': 'pdf2image' + }) + + summary = '' + for i, image in enumerate(images): + if i == 0: + summary = image_handler(image) + else: + summary += image_handler_append(image) + + return vectorize_text(summary, company_name, filename, base_metadata) + except Exception as e: + print(f"Error in vectorize_multiple_images: {e}") + error_metadata = {'error': str(e), 'file_type': 'pdf_images'} + return vectorize_text("Error processing PDF images", company_name, "error_pdf_images", error_metadata) + +def vectorize_docs(filepath: str, company_name: str): + """Vectorize PDF documents""" + try: + loader = PyPDFLoader(filepath) + docs = loader.load() + splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=80) + chunks = splitter.split_documents(docs) + filename = get_filename(filepath) + + # Create base metadata + base_metadata = create_base_metadata(filepath, company_name, 'pdf_document') + base_metadata.update({ + 'total_pages': len(docs), + 'total_chunks_created': len(chunks), + 'chunk_size': 600, + 'chunk_overlap': 80, + 'content_source': 'direct_pdf_text', + 'loader_used': 'PyPDFLoader' + }) + + # Create persist directory + persist_directory = os.path.join("chroma_store", company_name, filename) + os.makedirs(persist_directory, exist_ok=True) + + # Create collection name (sanitize) + collection_name = f"{company_name}_{filename}".replace(" ", "_").replace("-", "_").lower() + + # Add metadata to each chunk + for i, chunk in enumerate(chunks): + chunk.metadata.update(base_metadata) + chunk.metadata.update({ + 'chunk_index': i, + 'page_number': chunk.metadata.get('page', 'unknown'), + 'chunk_char_count': len(chunk.page_content) + }) + + vectorstore = Chroma.from_documents( + documents=chunks, + embedding=HuggingFaceEmbeddings(), + persist_directory=persist_directory, + collection_name=collection_name + ) + return vectorstore + + except Exception as e: + print(f"Error in vectorize_docs: {e}") + # Fallback to in-memory store + try: + loader = PyPDFLoader(filepath) + docs = loader.load() + splitter = RecursiveCharacterTextSplitter(chunk_size=600, chunk_overlap=80) + chunks = splitter.split_documents(docs) + + # Add error metadata to fallback + error_metadata = {'error': str(e), 'fallback': True, 'file_type': 'pdf_document'} + for chunk in chunks: + chunk.metadata.update(error_metadata) + + vectorstore = Chroma.from_documents( + documents=chunks, + embedding=HuggingFaceEmbeddings(), + collection_name=f"fallback_{company_name}_{filename}".replace(" ", "_").lower() + ) + return vectorstore + except Exception as fallback_error: + print(f"Fallback also failed: {fallback_error}") + # Return minimal vectorstore + return Chroma.from_texts( + texts=["Error loading document"], + embedding=HuggingFaceEmbeddings(), + metadatas=[{'error': str(fallback_error), 'critical_failure': True}], + collection_name="error_fallback" + ) + +# --- Entry Point for Routing --- + +def vectorize(filepath: str, company_name: str): + """Main vectorization function with enhanced file type support""" + try: + file_type = file_router(filepath) + print(f"Detected file type: {file_type}") + + if file_type == 'imagesingle': + return vectorize_single_image(filepath, company_name) + elif file_type == 'imagepdf': + return vectorize_multiple_images(filepath, company_name) + elif file_type == 'powerpoint': + return vectorize_powerpoint(filepath, company_name) + elif file_type == 'word_document': + return vectorize_word_document(filepath, company_name) + else: + return vectorize_docs(filepath, company_name) + + except Exception as e: + print(f"Error in vectorize main function: {e}") + # Ultimate fallback with comprehensive error metadata + error_metadata = { + 'error': str(e), + 'critical_failure': True, + 'processed_timestamp': datetime.now().isoformat(), + 'company_name': company_name, + 'attempted_file': filepath + } + return Chroma.from_texts( + texts=[f"Error processing file: {str(e)}"], + embedding=HuggingFaceEmbeddings(), + metadatas=[error_metadata], + collection_name="ultimate_fallback" + ) + +# --- Utility Functions for Metadata Queries --- + +def search_by_metadata(vectorstore, metadata_filter: dict, query: str = None, k: int = 5): + """Search documents using metadata filters""" + try: + if query: + # Similarity search with metadata filter + results = vectorstore.similarity_search( + query=query, + k=k, + filter=metadata_filter + ) + else: + # Get all documents matching metadata filter + results = vectorstore.get(where=metadata_filter, limit=k) + return results + except Exception as e: + print(f"Error in metadata search: {e}") + return [] + +def get_document_metadata_summary(vectorstore): + """Get a summary of all metadata in the vectorstore""" + try: + # This would need to be implemented based on your specific Chroma setup + # You might need to query the underlying collection directly + collection = vectorstore._collection + all_data = collection.get() + + if all_data and 'metadatas' in all_data: + return { + 'total_documents': len(all_data['metadatas']), + 'unique_companies': set(meta.get('company_name') for meta in all_data['metadatas'] if meta.get('company_name')), + 'file_types': set(meta.get('file_type') for meta in all_data['metadatas'] if meta.get('file_type')), + 'processing_dates': set(meta.get('processing_date') for meta in all_data['metadatas'] if meta.get('processing_date')) + } + except Exception as e: + print(f"Error getting metadata summary: {e}") + return None + +# --- Additional utility functions for specific file types --- + +def get_supported_file_types(): + """Return list of supported file types""" + return { + 'pdf': ['.pdf'], + 'powerpoint': ['.ppt', '.pptx'], + 'word_document': ['.doc', '.docx'], + 'images': ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp'] + } + +def validate_file_type(filepath: str): + """Validate if file type is supported""" + supported_types = get_supported_file_types() + file_extension = os.path.splitext(filepath)[1].lower() + + for file_type, extensions in supported_types.items(): + if file_extension in extensions: + return True, file_type + + return False, "unsupported" \ No newline at end of file diff --git a/GS_Sales_Proposal/Document_Upload_Vectordb/doc_xtraction_utils.py b/GS_Sales_Proposal/Document_Upload_Vectordb/doc_xtraction_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..9d0babbdf80cd8e22709c2c87f74434ed56fa3d6 --- /dev/null +++ b/GS_Sales_Proposal/Document_Upload_Vectordb/doc_xtraction_utils.py @@ -0,0 +1,14 @@ +def format_docs(docs): + return '\n\n'.join(doc.page_content for doc in docs) + +def clean_to_list(result:str) : + result = result.strip() + if result.startswith('```python'): + result = result[len('```python'):].strip() + elif result.startswith('```json'): + result = result[len('```json'):].strip() + elif result.startswith('```'): + result = result[len('```'):].strip() + if result.endswith('```'): + result = result[:-3].strip() + return result diff --git a/GS_Sales_Proposal/Document_Upload_Vectordb/pain_points_extractor.py b/GS_Sales_Proposal/Document_Upload_Vectordb/pain_points_extractor.py new file mode 100644 index 0000000000000000000000000000000000000000..10f899f6259a7c7432c2869d5c5f4124b5fa2830 --- /dev/null +++ b/GS_Sales_Proposal/Document_Upload_Vectordb/pain_points_extractor.py @@ -0,0 +1,44 @@ +from langchain_google_genai import ChatGoogleGenerativeAI +from dotenv import load_dotenv +from langchain_core.output_parsers import StrOutputParser +from langchain.prompts import ChatPromptTemplate +from langchain_core.runnables import RunnableLambda +load_dotenv() +import json + +llm = ChatGoogleGenerativeAI(model = 'gemini-1.5-flash') + +from .prompts import * + +from langchain_core.prompts import ChatPromptTemplate + +from .doc_vectorizer import vectorize + +from .doc_xtraction_utils import * + +def get_pain_points(file: str, company_name: str): + # Use a different variable name to avoid conflict with imported prompt + pain_point_template = ChatPromptTemplate.from_template(rfi_painpoint_prompt) + retriever = vectorize(file, company_name).as_retriever() + + # Extract the query string from input and pass to retriever + context_chain = ( + RunnableLambda(lambda x: x["query"]) # Extract just the query string + | retriever + | RunnableLambda(format_docs) + ) + + rag_chain = ( + {"context": context_chain} + | pain_point_template # Use the renamed variable + | llm + | StrOutputParser() + ) + + try: + result = rag_chain.invoke({"query": "Extract key business concerns and pain points from this RFI."}) + print(type(json.loads(clean_to_list(result)))) + return json.loads(clean_to_list(result)) + except Exception as e: + print(f"Error in get_pain_points: {e}") + return [] \ No newline at end of file diff --git a/GS_Sales_Proposal/Document_Upload_Vectordb/prompts.py b/GS_Sales_Proposal/Document_Upload_Vectordb/prompts.py new file mode 100644 index 0000000000000000000000000000000000000000..a7add80222efbd9b54d86cef9c36576a2252ba7a --- /dev/null +++ b/GS_Sales_Proposal/Document_Upload_Vectordb/prompts.py @@ -0,0 +1,39 @@ +image_prompt = """You are a highly meticulous AI assistant that extracts and summarizes every possible piece of visual information from an image without omitting any detail. + Your task is to generate an exhaustive, structured summary of the image that captures all the text, visual elements, layout, colors (if relevant), numbers, figures, and any context or formatting that might be useful. + Do not generalize or paraphrase — capture the content exactly as it appears. Use bullet points, lists, or structured sections (e.g., titles, tables, headers, footnotes) to organize your summary. + + Be especially attentive to: + - All visible text, including headers, footnotes, and marginal notes + - Tables: Capture each row and column verbatim including headers and cell values + - Graphs/Charts: Explain all axes, labels, legends, data points, patterns, and conclusions + - Visual layout and structure: Describe how content is arranged (e.g., two-column layout, centered title, left-aligned figure) + - Icons, logos, or images embedded within the image: Describe them accurately + - Fonts, colors, and emphasis (e.g., bold, italic, underlined) if they seem meaningful + - Dates, numbers, symbols, or special formatting exactly as shown + - If the image is a document or scanned page, preserve hierarchy and document structure + + Output the result in structured markdown with clear section headers (e.g., "Header", "Table 1", "Figure Description", "Text Body", "Footnotes"). + Your goal is to allow someone to fully understand the image without seeing it, preserving maximum detail for use in downstream AI models or search systems.""" + + + + +rfi_painpoint_prompt = """ +You are a highly capable business analyst AI with deep expertise in sales, technology, and market research. Your task is to analyze an RFI (Request for Information) document from a client who is seeking digital or technology solutions. + +From this document, extract and synthesize **three key insights or business pain points** that the client organization is implicitly or explicitly concerned about. Each pain point should be labeled under a relevant category, followed by a brief, insightful summary. + +Here is the context of the sales proposal: +{context} + +Respond with **only** a valid JSON dictionary using the following format: + +{{ + "Category 1": "Insightful and concise pain point summary.", + "Category 2": "Another brief and relevant pain point summary.", + "Category 3": "A third valuable insight from the RFI." +}} + +āŒ Do **not** add any explanation, text before or after the dictionary, markdown, comments, or labels. +āœ… Return **only** the raw JSON dictionary — nothing else. +""" diff --git a/GS_Sales_Proposal/Document_Upload_Vectordb/rfi2.pdf b/GS_Sales_Proposal/Document_Upload_Vectordb/rfi2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..859a08d74a324ae61d348b310c5ad1b165a22974 --- /dev/null +++ b/GS_Sales_Proposal/Document_Upload_Vectordb/rfi2.pdf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de4da8161b3c88fdaaeab1c8ed7f338e03e1f198ca28a48f1e44d0556560a27d +size 174219 diff --git a/GS_Sales_Proposal/LICENSE b/GS_Sales_Proposal/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..3b2ba5fc470b7b6ccf0bd8bc52ab2634f3ae3a5e --- /dev/null +++ b/GS_Sales_Proposal/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Amrutha-git-hub + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/GS_Sales_Proposal/Recommendation/__init__.py b/GS_Sales_Proposal/Recommendation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/GS_Sales_Proposal/Recommendation/__pycache__/__init__.cpython-313.pyc b/GS_Sales_Proposal/Recommendation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61c11ca3cfaf704f11bc3191ce8923af27963ba9 Binary files /dev/null and b/GS_Sales_Proposal/Recommendation/__pycache__/__init__.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Recommendation/__pycache__/prompts.cpython-313.pyc b/GS_Sales_Proposal/Recommendation/__pycache__/prompts.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dce428112834f2fb15a7c82cd7b5e61be51a8344 Binary files /dev/null and b/GS_Sales_Proposal/Recommendation/__pycache__/prompts.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Recommendation/__pycache__/recommendation_utils.cpython-313.pyc b/GS_Sales_Proposal/Recommendation/__pycache__/recommendation_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fada360be340e3ff6d21b5847d712c94d382808e Binary files /dev/null and b/GS_Sales_Proposal/Recommendation/__pycache__/recommendation_utils.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Recommendation/prompts.py b/GS_Sales_Proposal/Recommendation/prompts.py new file mode 100644 index 0000000000000000000000000000000000000000..5e96151568ac8cc5cdec71acf6f05c90d2c624be --- /dev/null +++ b/GS_Sales_Proposal/Recommendation/prompts.py @@ -0,0 +1,107 @@ +ai_suggetion_for_additional_req_prompt = '''You are a B2B Sales manager and innovation strategist. + +Your role is to review and enrich client requirements based on the following inputs: + +**Enterprise Details**: +{enterprise_details} + +**Current Client Requirements**: +{client_requirements} + +Your tasks: + +1. Based on the selected client_requirements suggest any additional points to be included in terms of in terms of payment , time , budget etc + +Respond in this format: + +--- +### āœ… Refined Client Requirements +[Improved version of the client requirements] + +--- + +### šŸ’” Innovative Suggestions +- [Idea 1 with rationale] +- [Idea 2 with rationale] + +--- + +### šŸ“Œ Best Practice Recommendations +- [What’s missing or could be enhanced] +- [Formatting, phrasing, or process suggestions] + +--- + +Ensure your language is professional, client-facing, and strategic. +''' +# ai_suggetion_for_additional_req_prompt = '''You are a senior solution consultant and innovation strategist. + +# Your role is to review and enrich client requirements based on the following inputs: + +# **Enterprise Details**: +# {enterprise_details} + +# **Current Client Requirements**: +# {client_requirements} + +# Your tasks: + +# 1. **Assess Alignment**: +# - Evaluate if the client requirements are aligned with the enterprise’s offerings and capabilities. +# - Identify gaps, redundancies, or missing technical/business aspects. + +# 2. **Recommend Improvements**: +# - Rewrite the client requirements for better clarity, completeness, and strategic fit. +# - Ensure inclusion of key components such as scope, deliverables, timelines, and measurable outcomes. + +# 3. **Suggest Innovations**: +# - Propose at least **2 innovative or differentiating additions** that could delight the client or increase project value. +# - These could be technology enhancements, automation opportunities, personalization, integrations, or unique service models. + +# 4. **Highlight Best Practices**: +# - Mention if anything is outdated, vague, or can be made more professional or efficient. +# - Share **best practices** relevant to the industry or solution area. + +# Respond in this format: + +# --- +# ### āœ… Refined Client Requirements +# [Improved version of the client requirements] + +# --- + +# ### šŸ’” Innovative Suggestions +# - [Idea 1 with rationale] +# - [Idea 2 with rationale] + +# --- + +# ### šŸ“Œ Best Practice Recommendations +# - [What’s missing or could be enhanced] +# - [Formatting, phrasing, or process suggestions] + +# --- + +# Ensure your language is professional, client-facing, and strategic. +# ''' + +business_priotiiry_recommendation_prompt = '''You are a B2B business strategy expert. + +Your task is to identify the top 3 current business priorities for a client stakeholder based on their role. + +**Client SPOC Role**: {client_spoc_role} + +Guidelines: +- Focus on strategic goals and KPIs relevant to that role. +- Consider current trends and business environments (e.g., digital transformation, efficiency, AI adoption, cost control). +- Keep the priorities concise, professional, and relevant to decision-making. + +Respond in the following format: + +[ + {{"title": "Strategic Growth and Vision", "icon": "šŸ“ˆ"}}, + {{"title": "Operational Efficiency", "icon": "āš™ļø"}}, + {{"title": "Customer Experience", "icon": "šŸ’”"}} +] + +''' \ No newline at end of file diff --git a/GS_Sales_Proposal/Recommendation/recommendation_utils.py b/GS_Sales_Proposal/Recommendation/recommendation_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..e37c65b3a6e55a37bc8535822d4be4ea4b4e0cba --- /dev/null +++ b/GS_Sales_Proposal/Recommendation/recommendation_utils.py @@ -0,0 +1,26 @@ +from langchain_core.prompts import ChatPromptTemplate +from .prompts import * +from langchain_google_genai import ChatGoogleGenerativeAI +from langchain_core.output_parsers import JsonOutputParser,StrOutputParser +from dotenv import load_dotenv +from Document_Upload_Vectordb.doc_xtraction_utils import clean_to_list +load_dotenv() +import json + +llm = ChatGoogleGenerativeAI(model = 'gemini-1.5-flash') + + + + +def get_ai_client_requirements(enterprise_details,client_requirements): + template = ChatPromptTemplate.from_template(ai_suggetion_for_additional_req_prompt) + chain = template | llm | StrOutputParser() + result = chain.invoke({'enterprise_details':enterprise_details,'client_requirements':client_requirements}) + return result + +def get_ai_business_priorities(spoc_role="CEO"): + template = ChatPromptTemplate.from_template(business_priotiiry_recommendation_prompt) + chain = template | llm | JsonOutputParser() + result = chain.invoke({'client_spoc_role':spoc_role}) + print(result) + return result diff --git a/GS_Sales_Proposal/Search/Linkedin/__pycache__/linkedin_serp.cpython-313.pyc b/GS_Sales_Proposal/Search/Linkedin/__pycache__/linkedin_serp.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8cdc524d3761fce35c6bda0c3965f6b21288bc19 Binary files /dev/null and b/GS_Sales_Proposal/Search/Linkedin/__pycache__/linkedin_serp.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Search/Linkedin/linkedin_agent_runner_unused.py b/GS_Sales_Proposal/Search/Linkedin/linkedin_agent_runner_unused.py new file mode 100644 index 0000000000000000000000000000000000000000..86ae10e91c67de7ff00040d7d0f02b8b4a75a967 --- /dev/null +++ b/GS_Sales_Proposal/Search/Linkedin/linkedin_agent_runner_unused.py @@ -0,0 +1,66 @@ +from google.adk.agents import Agent +from google.adk.tools import google_search +from pydantic import BaseModel,Field +from dotenv import load_dotenv +from google.adk.sessions import InMemorySessionService +from google.adk.runners import Runner +from google.genai import types +import ast +import re +from Search.LinkedIN.linkedin_agent_unused import * + + +# Setup session and runner +session_service = InMemorySessionService() +SESSION_ID = 'sess' +USER_ID = 'user' + +session = session_service.create_session( + app_name="APP", + user_id=USER_ID, + session_id=SESSION_ID +) + +runner = Runner( + app_name="APP", + session_service=session_service, + agent=search_agent +) +def extract_list_from_string(s): + # Remove any prefix like 'json' and extract the JSON array part + match = re.search(r"\[.*\]", s, re.DOTALL) + if match: + try: + return json.loads(match.group()) + except json.JSONDecodeError: + print("Failed to parse list.") + else: + print("No list found.") + return None + + +import json +async def get_linkedin(user_name: str, runner=runner, user_id=USER_ID, session_id=SESSION_ID): + content = types.Content(role='user', parts=[types.Part(text=user_name)]) + final_msg = "" + + async for event in runner.run_async(user_id=user_id, session_id=session_id, new_message=content): + if event.is_final_response(): + if event.content and event.content.parts: + final_msg = event.content.parts[0].text + elif event.actions and event.actions.escalate: + final_msg = event.error_message + result = final_msg + result = result.strip() + if result.startswith('```python'): + result = result[len('```python'):].strip() + elif result.startswith('```json'): + result = result[len('```json'):].strip() + elif result.startswith('```'): + result = result[len('```'):].strip() + if result.endswith('```'): + result = result[:-3].strip() + final_msg = result + print(final_msg) + return json.loads(final_msg) + diff --git a/GS_Sales_Proposal/Search/Linkedin/linkedin_agent_unused.py b/GS_Sales_Proposal/Search/Linkedin/linkedin_agent_unused.py new file mode 100644 index 0000000000000000000000000000000000000000..15df0c253cbebbb44725fc1d02bc3f4bb5d04472 --- /dev/null +++ b/GS_Sales_Proposal/Search/Linkedin/linkedin_agent_unused.py @@ -0,0 +1,63 @@ +from google.adk.agents import Agent +from google.adk.tools import google_search +from pydantic import BaseModel,Field +from dotenv import load_dotenv +from google.adk.sessions import InMemorySessionService +from google.adk.runners import Runner +from google.genai import types +import ast +import re + + +load_dotenv() +class WebSite(BaseModel): + website_name : str = Field(description="Website name") + website_url : str = Field(description="Website url") + + +search_agent = Agent( + model='gemini-2.0-flash-001', + name='linkedin_profile_agent', + description=( + "You are an intelligent assistant that finds the most accurate and official LinkedIn profiles " + "of people and analyzes their current job roles to generate sales insights." + ), + instruction=''' +Given the name of a person, your task is to find and return **exactly 5 people** for whom: + +- A valid and official **LinkedIn profile URL** (`linkedin.com/in/...`) can be found. +- The **current job title/role** is either extracted from search preview or inferred based on their LinkedIn snippet. +- You can intelligently infer the **top 3 job priorities** relevant to a sales proposal (i.e., what matters to this person in a B2B sale). + +āš ļø STRICT RULES: +- DO NOT return any result without a valid LinkedIn URL. +- DO NOT invent or guess URLs — only use actual `linkedin.com/in/...` links found via search. +- Use `site:linkedin.com/in "Full Name"` on Google to identify results. +- Skip people for whom no real LinkedIn result is found. +- Return fewer than 5 results if necessary, but never include fake or placeholder data. + +šŸ“Œ Response format MUST be a Python-style list of JSON objects like this: + +[ + { + "name": "Shreyank Isiri", + "linkedin_url": "https://www.linkedin.com/in/shreyankisiri/", + "role": "Solutions Architect at XYZ Corp", + "top_3_priorities": [ + "Understanding client infrastructure needs", + "Designing scalable and secure systems", + "Supporting sales through technical expertise" + ] + }, + ... +] + +IMPORTANT: +- Use temperature = 0 +- DO NOT include explanations or markdown. Just return the list. +- Always ensure the LinkedIn URL is real and not hallucinated. +''', + tools=[google_search], +) + + diff --git a/GS_Sales_Proposal/Search/Linkedin/linkedin_serp.py b/GS_Sales_Proposal/Search/Linkedin/linkedin_serp.py new file mode 100644 index 0000000000000000000000000000000000000000..9b3fffc07c6dd82b9820000a8ae84aca73e4f2e3 --- /dev/null +++ b/GS_Sales_Proposal/Search/Linkedin/linkedin_serp.py @@ -0,0 +1,40 @@ + +import requests +from dotenv import load_dotenv +load_dotenv() +import os +import streamlit as st + +def infer_priorities(title): + # Placeholder function: replace with your actual priority inference logic + return [" Scalability & Risk Mitigation","Operational Efficiency","Scalability & Risk Mitigation"] + +def search_linkedin_serpapi(name): + params = { + "q": f'site:linkedin.com/in "{name}"', + "api_key": os.getenv("SERP_API_KEY"), + "engine": "google", + "num": 5 + } + + try: + response = requests.get("https://serpapi.com/search", params=params).json() + results = {} + + for res in response.get("organic_results", []): + link = res.get("link", "") + title = res.get("title", "") + if "linkedin.com/in" in link: + results[link] = { + "name": name, + "role": title, + "top_3_priorities": infer_priorities(title) + } + if len(results) == 5: + break + + print(results) + return results + except Exception as e: + st.error(f"Error searching LinkedIn profiles: {e}") + return {} diff --git a/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent.cpython-312.pyc b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03a0e92d3d85909b0f2e41fbcf82bcf361b23e8a Binary files /dev/null and b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent.cpython-312.pyc differ diff --git a/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent.cpython-313.pyc b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4e96d2ad5b1d3c793bc675b09f4ecdac581ca7c Binary files /dev/null and b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent_runner.cpython-313.pyc b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent_runner.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc384fda77e80f5c0bdc94e97578eb115c65538e Binary files /dev/null and b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/__pycache__/agent_runner.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Search/WebsiteUrl_Agent/agent.py b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/agent.py new file mode 100644 index 0000000000000000000000000000000000000000..19caf29f71c372dbb4efa8c26cfee5765dd432e8 --- /dev/null +++ b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/agent.py @@ -0,0 +1,67 @@ +from google.adk.agents import Agent +from google.adk.tools import google_search +from pydantic import BaseModel,Field +from dotenv import load_dotenv +from google.adk.sessions import InMemorySessionService +from google.adk.runners import Runner +from google.genai import types +import ast +import re + + +load_dotenv() +class WebSite(BaseModel): + website_name : str = Field(description="Website name") + website_url : str = Field(description="Website url") + + +search_agent = Agent( + model='gemini-2.0-flash-001', + name='url_agent', + description = ( + "You are an intelligent assistant specialized in finding official and relevant websites " + "associated with a given organization or company name. Your goal is to retrieve high-quality, " + "credible links that accurately represent the digital presence of the organization." +), + instruction = ''' + Given the name of a company or organization, your task is to search and return the top 7 most relevant and credible website URLs associated with it. + + These can include: + - The official company website try fetching this and if there are multiple then show all 7 + + + Your response must be a clean Python-style list of strings, where each string is a valid URL. + + Format your response exactly like this: + + [ + "https://google.com/", + "https://cloud.google.com", + "https://accounts.google.com" + ] + + Like this any 10 urls that are related to the given organization name + + Do not include explanations, only return the list of URLs. + + IMPORTANT : Just return me list of urls no additional text + + return like + + + ---- + [ + "https://google.com/", + "https://cloud.google.com", + "https://accounts.google.com" + ] + + ---- + + VERY IMPORTANT : TEMPERATURE OF THE MODEL BE ZEROOOO AND remember dont give me like the links of youtube or linkedin or any other platforms + THE LINK SHOULD BE OFFICIAL LINK OF THE ORGANIZATION + ''', + + tools = [google_search], +) + diff --git a/GS_Sales_Proposal/Search/WebsiteUrl_Agent/agent_runner.py b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/agent_runner.py new file mode 100644 index 0000000000000000000000000000000000000000..24571aa6242e5827c226ff5646679a73dffa211a --- /dev/null +++ b/GS_Sales_Proposal/Search/WebsiteUrl_Agent/agent_runner.py @@ -0,0 +1,68 @@ +from google.adk.agents import Agent +from google.adk.tools import google_search +from pydantic import BaseModel,Field +from dotenv import load_dotenv +from google.adk.sessions import InMemorySessionService +from google.adk.runners import Runner +from google.genai import types +import ast +import re +from WebsiteUrl_Agent.agent import * + + +# Setup session and runner +session_service = InMemorySessionService() +SESSION_ID = 'sess' +USER_ID = 'user' + +session = session_service.create_session( + app_name="APP", + user_id=USER_ID, + session_id=SESSION_ID +) + +runner = Runner( + app_name="APP", + session_service=session_service, + agent=search_agent +) +def extract_list_from_string(s): + # Remove any prefix like 'json' and extract the JSON array part + match = re.search(r"\[.*\]", s, re.DOTALL) + if match: + try: + return json.loads(match.group()) + except json.JSONDecodeError: + print("Failed to parse list.") + else: + print("No list found.") + return None + + +import json +async def get_urls(company_name: str, runner=runner, user_id=USER_ID, session_id=SESSION_ID): + content = types.Content(role='user', parts=[types.Part(text=company_name)]) + final_msg = "" + + async for event in runner.run_async(user_id=user_id, session_id=session_id, new_message=content): + if event.is_final_response(): + if event.content and event.content.parts: + final_msg = event.content.parts[0].text + elif event.actions and event.actions.escalate: + final_msg = event.error_message + result = final_msg + result = result.strip() + if result.startswith('```python'): + result = result[len('```python'):].strip() + elif result.startswith('```json'): + result = result[len('```json'):].strip() + elif result.startswith('```'): + result = result[len('```'):].strip() + if result.endswith('```'): + result = result[:-3].strip() + final_msg = result + print(final_msg) + return json.loads(final_msg) + +import asyncio +asyncio.run(get_urls("growth sutra")) \ No newline at end of file diff --git a/GS_Sales_Proposal/Search/__pycache__/linkedin_serp.cpython-313.pyc b/GS_Sales_Proposal/Search/__pycache__/linkedin_serp.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70560e8c394306a4ab1d7f32698404b4959d9b9a Binary files /dev/null and b/GS_Sales_Proposal/Search/__pycache__/linkedin_serp.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Seller/__pycache__/seller.cpython-313.pyc b/GS_Sales_Proposal/Seller/__pycache__/seller.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee5c54c82096e23603efa05b6b2d35bb32cfc68d Binary files /dev/null and b/GS_Sales_Proposal/Seller/__pycache__/seller.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Seller/__pycache__/seller_css.cpython-313.pyc b/GS_Sales_Proposal/Seller/__pycache__/seller_css.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a11744098fa780c799e8e9a61a18b43ed3a83ab4 Binary files /dev/null and b/GS_Sales_Proposal/Seller/__pycache__/seller_css.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Seller/__pycache__/seller_utils.cpython-313.pyc b/GS_Sales_Proposal/Seller/__pycache__/seller_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60a44d071657f73a11143f0f63da2abc24560186 Binary files /dev/null and b/GS_Sales_Proposal/Seller/__pycache__/seller_utils.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/Seller/seller.py b/GS_Sales_Proposal/Seller/seller.py new file mode 100644 index 0000000000000000000000000000000000000000..2e95616750bd2f4f3b3b3ad5c3d803a889bd69ea --- /dev/null +++ b/GS_Sales_Proposal/Seller/seller.py @@ -0,0 +1,463 @@ +import streamlit as st +from .seller_css import seller_css +from .seller_utils import * +from Search.Linkedin.linkedin_serp import * +from Recommendation.recommendation_utils import * +from t import * + +def seller_tab(): + # Re-apply CSS after every rerun to ensure persistence + st.markdown(seller_css, unsafe_allow_html=True) + + # Initialize validation trigger + if 'show_validation' not in st.session_state: + st.session_state.show_validation = False + + # Initialize enterprise details content in session state + if 'seller_enterprise_details_content' not in st.session_state: + st.session_state.seller_enterprise_details_content = "" + + # Initialize seller requirements content in session state + if 'seller_requirements_content' not in st.session_state: + st.session_state.seller_requirements_content = "" + + # Initialize URLs list in session state + if 'seller_website_urls_list' not in st.session_state: + st.session_state['seller_website_urls_list'] = [] + + # Initialize last company name to track changes + if 'last_seller_company_name' not in st.session_state: + st.session_state['last_seller_company_name'] = "" + + # Initialize uploaded file path in session state + if 'seller_uploaded_file_path' not in st.session_state: + st.session_state['seller_uploaded_file_path'] = None + + # Initialize RFI pain points items in session state + if 'seller_rfi_pain_points_items' not in st.session_state: + st.session_state['seller_rfi_pain_points_items'] = {} + + # Initialize document analysis status + if 'seller_document_analyzed' not in st.session_state: + st.session_state['seller_document_analyzed'] = False + + if 'seller_linkedin_profiles' not in st.session_state: + st.session_state['seller_linkedin_profiles'] = {} + if 'last_searched_seller_spoc' not in st.session_state: + st.session_state['last_searched_seller_spoc'] = "" + + # Initialize scraping states + if 'seller_scraping_in_progress' not in st.session_state: + st.session_state['seller_scraping_in_progress'] = False + if 'seller_pending_scrape_url' not in st.session_state: + st.session_state['seller_pending_scrape_url'] = None + + # Top section with seller name and URLs + col1, col2 = st.columns([1, 1]) + + with col1: + st.markdown(""" +
+ Seller Enterprise Name * +
ā“˜
+
+ """, unsafe_allow_html=True) + + # Create a sub-column layout for name input and find URLs button + name_col, button_col = st.columns([3, 1]) + + with name_col: + seller_enterprise_name = st.text_input( + label="Seller Enterprise Name", + placeholder="Enter seller enterprise name...", + key="seller_enterprise_name_input", + label_visibility="collapsed" + ) + + with button_col: + # Find URLs button - only enabled when seller name has more than 2 characters + find_urls_disabled = not (seller_enterprise_name and len(seller_enterprise_name.strip()) > 2) + + if st.button("šŸ” Find Website", + disabled=find_urls_disabled, + help="Find website URLs for this company", + key="find_seller_urls_button"): + # Add spinner while fetching URLs + with st.spinner(f"Finding Websites for '{seller_enterprise_name.strip()}'..."): + try: + st.session_state['seller_website_urls_list'] = get_urls_list(seller_enterprise_name.strip()) + st.session_state['last_seller_company_name'] = seller_enterprise_name + except Exception as e: + st.session_state['seller_website_urls_list'] = [] + st.error(f"Error finding URLs: {str(e)}") + + # Clear URLs if company name is cleared + if not seller_enterprise_name and st.session_state['last_seller_company_name']: + st.session_state['seller_website_urls_list'] = [] + st.session_state['last_seller_company_name'] = "" + + # Show validation warning if triggered and field is empty + if st.session_state.show_validation and check_field_validation("Seller Enterprise Name", seller_enterprise_name, True): + show_field_warning("Seller Enterprise Name") + + with col2: + # Label row with inline emoji and tooltip + st.markdown(''' +
+ Seller Website URL +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Create columns for dropdown and buttons - dropdown takes most space, buttons share remaining space + url_col, btn1_col, btn2_col, btn3_col = st.columns([7, 1, 1, 1]) + + with url_col: + # URL selection logic - Always show normal dropdown, just disable when no seller name + seller_name_provided = bool(seller_enterprise_name and seller_enterprise_name.strip()) + + if not st.session_state.get('seller_website_urls_list'): + # No URLs available - show default option + url_options = ["Select seller website URL"] + else: + # URLs available - show them in dropdown + url_options = ["Select seller website URL"] + st.session_state['seller_website_urls_list'] + + seller_website_url = st.selectbox( + label="Seller Website URL", + options=url_options, + key="seller_website_url_selector", + label_visibility="collapsed", + disabled=not seller_name_provided, + accept_new_options=True + ) + + # Reset to empty string if default option is selected + if seller_website_url == "Select seller website URL": + seller_website_url = "" + + # Each button in its own column for horizontal alignment + with btn1_col: + if seller_website_url: + st.link_button("🌐", seller_website_url, help="Visit website",use_container_width=True) + else: + st.button("🌐", help="Visit website", disabled=True,use_container_width=True) + with btn2_col: + # Button 2: Refresh URL List + refresh_clicked = st.button("šŸ”„", help="Refresh website URLs list", key="refresh_seller_urls_btn",use_container_width=True,disabled=not seller_website_url) + + with btn3_col: + # Button 3: Scrape Website - Set up pending scrape instead of immediate execution + scrape_clicked = st.button("šŸ“‘", help="Get enterprise details", key="scrape_seller_website_btn",use_container_width=True, disabled=not seller_website_url) + + # Handle scrape button click by setting up pending operation + if scrape_clicked and seller_website_url: + st.session_state['seller_pending_scrape_url'] = seller_website_url + st.session_state['seller_scraping_in_progress'] = True + st.rerun() + + # Handle refresh action outside columns for better UX + if refresh_clicked and seller_name_provided: + try: + with st.spinner("Refreshing website URLs..."): + st.session_state['seller_website_urls_list'] = get_urls_list(seller_enterprise_name) + st.success("Website URLs refreshed!") + st.rerun() # Refresh the page to show updated URLs + except Exception as e: + st.error(f"Error refreshing URLs: {str(e)}") + + # Handle pending scraping operation OUTSIDE of columns to prevent UI blocking + if st.session_state.get('seller_scraping_in_progress') and st.session_state.get('seller_pending_scrape_url'): + # Show full-width spinner + with st.spinner(f"Scraping website details from {st.session_state['seller_pending_scrape_url']}..."): + try: + # Perform the scraping operation + website_details = get_url_details(st.session_state['seller_pending_scrape_url']) + st.session_state.seller_enterprise_details_content = website_details + st.session_state['last_analyzed_seller_url'] = st.session_state['seller_pending_scrape_url'] + + # Clear pending operation + st.session_state['seller_scraping_in_progress'] = False + st.session_state['seller_pending_scrape_url'] = None + + st.success("Website details extracted successfully!") + st.rerun() # Refresh to show updated details + + except Exception as e: + # Clear pending operation on error + st.session_state['seller_scraping_in_progress'] = False + st.session_state['seller_pending_scrape_url'] = None + st.error(f"Error scraping website: {str(e)}") + + # Show validation warning if triggered and field is empty (optional) + if st.session_state.show_validation and check_field_validation("Seller Website URL", seller_website_url, False): + show_field_warning("Seller Website URL") + + +#------------------------------------------------------------------------------- + + st.markdown(''' +
+ Upload Seller Document +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Add custom CSS for file uploader and animations + st.markdown(""" + + """, unsafe_allow_html=True) + + # FILE UPLOAD - Always enabled, independent of seller name (multiple files) + seller_documents_upload = st.file_uploader( + label="Upload Seller Documents", + type=['pdf', 'docx', 'txt', 'csv','png','jpg','jpeg'], + key="seller_documents_uploader", + label_visibility="collapsed", + accept_multiple_files=True + ) + + # Initialize processing states and file tracking + if 'processing_all_seller_documents' not in st.session_state: + st.session_state['processing_all_seller_documents'] = False + if 'seller_uploaded_files_paths' not in st.session_state: + st.session_state['seller_uploaded_files_paths'] = {} + if 'seller_services_by_file' not in st.session_state: + st.session_state['seller_services_by_file'] = {} + + # Show file info and single analyze button for all files + if seller_documents_upload is not None and len(seller_documents_upload) > 0: + st.markdown("**Uploaded Documents:**") + + # Display all uploaded files + for idx, uploaded_file in enumerate(seller_documents_upload): + file_key = f"{uploaded_file.name}_{uploaded_file.size}" # Unique key for each file + + # Very compact single line display + file_size_kb = round(uploaded_file.size / 1024, 1) + file_size_display = f"{file_size_kb}KB" if file_size_kb < 1024 else f"{round(file_size_kb/1024, 1)}MB" + + # Check if this file has been processed + is_processed = file_key in st.session_state.get('seller_services_by_file', {}) + is_processing = st.session_state.get('processing_all_seller_documents', False) + + if is_processing: + # Show animated processing state + st.markdown(f""" +
+ + šŸ”„ {uploaded_file.name[:25]}{'...' if len(uploaded_file.name) > 25 else ''} (Analyzing...) + +
+ """, unsafe_allow_html=True) + else: + # Show normal file info with status + status_icon = "āœ…" if is_processed else "šŸ“„" + st.markdown(f"{status_icon} {uploaded_file.name[:30]}{'...' if len(uploaded_file.name) > 30 else ''} ({file_size_display})", + unsafe_allow_html=True) + + # Single button to process all files + st.markdown("---") # Separator line + + # Check if all files are already processed + all_processed = all( + f"{file.name}_{file.size}" in st.session_state.get('seller_services_by_file', {}) + for file in seller_documents_upload + ) + + is_processing = st.session_state.get('processing_all_seller_documents', False) + + # Button styling + if all_processed: + button_color = "#28a745" # Green for all processed + button_text = "All Documents Processed" + button_disabled = True + elif is_processing: + button_color = "#FF6B6B" # Red for processing + button_text = "Analyzing All Documents..." + button_disabled = True + else: + button_color = "#4CAF50" # Blue for ready to process + button_text = f"Get Services from All Documents ({len(seller_documents_upload)} files)" + button_disabled = False + + st.markdown(f""" + + """, unsafe_allow_html=True) + + # Single analyze button for all files + analyze_all_clicked = st.button( + button_text, + key="analyze_all_seller_documents_btn", + help="Process all seller documents" if not button_disabled else "All documents processed" if all_processed else "Processing in progress...", + type="secondary", + disabled=button_disabled, + use_container_width=True + ) + + # Handle analyze button click for all files + if analyze_all_clicked and not button_disabled: + if not seller_enterprise_name: + st.error("āŒ Please enter the Seller Enterprise Name first") + else: + # Set processing flag for all files + st.session_state['processing_all_seller_documents'] = True + st.rerun() # Refresh to show processing state + + # Handle processing for all files when button is clicked + if st.session_state.get('processing_all_seller_documents', False): + # Show overall processing indicator + with st.container(): + st.markdown("**šŸ” Processing all documents and extracting services...**") + + # Process each file + all_services = {} + processed_count = 0 + total_files = len(seller_documents_upload) + + for idx, uploaded_file in enumerate(seller_documents_upload): + file_key = f"{uploaded_file.name}_{uploaded_file.size}" + + # Show progress for current file + progress_text = f"Processing {uploaded_file.name} ({idx + 1}/{total_files})..." + with st.spinner(progress_text): + try: + # Save the file and get the path + file_path = save_uploaded_file_and_get_path(uploaded_file) + st.session_state['seller_uploaded_files_paths'][file_key] = file_path + + if file_path and seller_enterprise_name: + # Extract services using the file path and company name + file_services = get_seller_services(file_path, seller_enterprise_name) + + # Store services data for this specific file + st.session_state['seller_services_by_file'][file_key] = { + 'filename': uploaded_file.name, + 'services': file_services, + 'file_path': file_path + } + + # Combine services from this file + if isinstance(file_services, dict): + all_services.update(file_services) + + processed_count += 1 + st.success(f"āœ… {uploaded_file.name} processed successfully!") + + else: + st.error(f"āŒ Error saving {uploaded_file.name}") + + except Exception as e: + st.error(f"āŒ Error processing {uploaded_file.name}: {str(e)}") + + # Update combined services and reset processing flag + st.session_state['seller_services_items'] = all_services + st.session_state['seller_document_analyzed'] = True if processed_count > 0 else False + st.session_state['processing_all_seller_documents'] = False + + # Show final summary + if processed_count == total_files: + st.success(f"šŸŽ‰ All {total_files} documents processed successfully!") + elif processed_count > 0: + st.warning(f"āš ļø {processed_count} out of {total_files} documents processed successfully.") + else: + st.error("āŒ No documents could be processed.") + + st.rerun() # Refresh to update UI + + # Function call for Seller Services Offered selection + seller_enterprise_details, seller_enterprise_details_provided = render_three_column_selector_unified( + # Column configuration - Made wider to fill screen + column_ratio=(2, 2, 2), # Equal wider columns + column_gap="large", # Increased gap for better spacing + + # Left column (text area) configuration + left_title="Seller Enterprise Details", + left_tooltip="Define your enterprise details, services offered, company capabilities, core competencies, and business portfolio. This information helps clients understand your organizational strengths and service offerings.", + left_required=True, + textarea_height=200, # Increased height for better visibility + textarea_placeholder="Enter seller enterprise name first to enable this field", + textarea_session_key="seller_enterprise_content", + textarea_widget_key="seller_enterprise_textarea", + + # Unified right section (middle + right columns) configuration + unified_section_title="Available Services & Capabilities", + unified_section_tooltip="Select from available services and capabilities that represent your enterprise offerings. These can include technical services, consulting, products, or specialized business solutions.", + + # Session state keys for both sides + middle_selected_items_key="selected_services_offered", + middle_content_map_key="services_content_map", + right_selected_items_key="selected_additional_capabilities", + right_content_map_key="capabilities_content_map", + + # Single data source that will be displayed in both columns + default_data=None, # You would pass your services data dictionary here + split_ratio=(3, 3), # How many items go to middle vs right column + + # Enable/disable conditions + client_enabled_condition=True, + client_name_provided=True, + + # Styling configuration + button_column_width=2.5, # Button width within each column + content_column_width=6.5, # Content area width within each column + show_success_messages=False, + selected_color="#2e7d32", # Green color + selected_border_color="#4caf50", # Green border + unselected_color="#404040", + unselected_border_color="#404040", + text_color="#ffffff", + + # Title styling - Made normal size like left title + title_font_size="18px", # Same as other titles + title_color="#ffffff", + title_margin_bottom="10px" # Reduced margin + ) \ No newline at end of file diff --git a/GS_Sales_Proposal/Seller/seller_css.py b/GS_Sales_Proposal/Seller/seller_css.py new file mode 100644 index 0000000000000000000000000000000000000000..3b4c47effd93a309522bf10c396f1e9982127739 --- /dev/null +++ b/GS_Sales_Proposal/Seller/seller_css.py @@ -0,0 +1,430 @@ +seller_css = """ + +""" diff --git a/GS_Sales_Proposal/Seller/seller_utils.py b/GS_Sales_Proposal/Seller/seller_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..4a44090f5fcdbdd149a947c0f6991f09fc1e79c6 --- /dev/null +++ b/GS_Sales_Proposal/Seller/seller_utils.py @@ -0,0 +1,46 @@ +import streamlit as st +import pandas as pd +from typing import List +import os + +from WebsiteUrl_Agent.agent_runner import get_urls +import asyncio +from Document_Upload_Vectordb.pain_points_extractor import * +from WebScraper.scrape import get_data + + +# Function to get URLs (placeholder function) + +def get_urls_list(company_name) -> List[str]: + """ + Placeholder function that returns a list of URLs + Replace this with your actual function that fetches URLs + """ + return asyncio.run(get_urls(company_name)) + +def check_field_validation(field_name: str, field_value: str, is_mandatory: bool = False) -> bool: + """Check if field validation should show warning""" + if is_mandatory and not field_value.strip(): + return True + return False + +def show_field_warning(field_name: str): + """Show warning message for mandatory fields""" + st.markdown(f'
āš ļø {field_name} is mandatory and cannot be empty!
', unsafe_allow_html=True) + +def get_url_details(url:str): + """Use this if you want to run async function synchronously""" + try: + # Run the async function synchronously + website_details = asyncio.run(get_data(url)) + return website_details + except Exception as e: + print(f"Error: {e}") + return None + +def save_uploaded_file_and_get_path(file): + return "saved" + + +def get_seller_services(filename , filepath): + return "pain points" \ No newline at end of file diff --git a/GS_Sales_Proposal/WebScraper/__pycache__/scrape.cpython-313.pyc b/GS_Sales_Proposal/WebScraper/__pycache__/scrape.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..254a519cf54cba95b7d82bb04ebd7a855e570263 Binary files /dev/null and b/GS_Sales_Proposal/WebScraper/__pycache__/scrape.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/WebScraper/__pycache__/scrape_utils.cpython-313.pyc b/GS_Sales_Proposal/WebScraper/__pycache__/scrape_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e65739ef718fc659c05ab2868a0de58e2f4aa91 Binary files /dev/null and b/GS_Sales_Proposal/WebScraper/__pycache__/scrape_utils.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/WebScraper/__pycache__/state.cpython-313.pyc b/GS_Sales_Proposal/WebScraper/__pycache__/state.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f28ae4fb286e1f30fdac692d323740c3ab9745d Binary files /dev/null and b/GS_Sales_Proposal/WebScraper/__pycache__/state.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/WebScraper/main.py b/GS_Sales_Proposal/WebScraper/main.py new file mode 100644 index 0000000000000000000000000000000000000000..6ef448a936695ec5519895627eff81b6ef48f659 --- /dev/null +++ b/GS_Sales_Proposal/WebScraper/main.py @@ -0,0 +1,8 @@ +import asyncio +from scrape import get_data +async def main(): + result = await get_data('https://www.whatsapp.com/') + print(result.extracted_content) + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/GS_Sales_Proposal/WebScraper/scrape.py b/GS_Sales_Proposal/WebScraper/scrape.py new file mode 100644 index 0000000000000000000000000000000000000000..f2ccee8cc3c7235ae44bb54a324816da673f05a6 --- /dev/null +++ b/GS_Sales_Proposal/WebScraper/scrape.py @@ -0,0 +1,122 @@ +from typing import List +import json + +from WebScraper.state import User + + + +from crawl4ai import LLMConfig,AsyncWebCrawler,CacheMode,CrawlerRunConfig,BrowserConfig +from crawl4ai.extraction_strategy import LLMExtractionStrategy +import os +from dotenv import load_dotenv +load_dotenv() + +llm_strategy = LLMExtractionStrategy( + llm_config=LLMConfig( + provider="gemini/gemini-1.5-flash", + api_token=os.getenv("GOOGLE_API_KEY"), + ), + schema=User.model_json_schema(), + extraction_type="schema", + instruction=""" +You are analyzing a webpage to extract structured information about the organization behind it. + +Your goal is to extract the following: + +1. **Name**: The name of the organization or company. +2. **Logo**: The URL of the primary logo image (typically found in the header or near the company name). +3. **Detailed Description**: A clear and informative summary of what the organization does. + - This should come from the section of the page typically labeled or titled "About", "Who We Are", "Our Story", or similar. + - If the page does not have a heading, look for paragraphs or text blocks that describe the company's purpose, mission, background, or offerings. + - Do not include text that is clearly part of blog posts, testimonials, products, or contact details. + +Tips: +- Focus on sections that describe the identity, mission, background, or goals of the organization. +- If multiple descriptive sections exist, prioritize the one closest to the top of the page or under an "About"-like heading. +- Avoid generic filler content like navigation menus, service listings, or unrelated calls to action. + +Return the data in the format defined by the schema. +""" +, chunk_token_threshold=1000, + overlap_rate=0.0, + apply_chunking=True, + input_format="markdown", # or "html", "fit_markdown" + extra_args={"temperature": 0.0, "max_tokens": 800} +) + +crawl_config = CrawlerRunConfig( + extraction_strategy=llm_strategy, + cache_mode=CacheMode.BYPASS +) + + +browser_cfg = BrowserConfig(headless=True) + +import re + +import re +from collections import Counter +from typing import List + + + + +def aggregate_users(users: List[dict]) -> User: + print("šŸ” Starting aggregation of users...") + + # Filter out users with error=True + valid_users = [u for u in users if not u.get('error', False)] + + + # Most frequent name (non-empty and non-None) + names = [u.get('name', '') or '' for u in valid_users if u.get('name')] + name_counter = Counter(names) + name = name_counter.most_common(1)[0][0] if name_counter else (valid_users[0].get('name') or "Unknown") + logo = next( + ( + logo for u in valid_users + if (logo := u.get('logo')) and isinstance(logo, str) and re.search(r'logo', logo, re.IGNORECASE) + ), + "" + ) + + + # Longest non-empty description + descriptions = [u.get('description', '') or '' for u in valid_users] + description = max(descriptions, key=len, default="") + + # Services list from user with the longest list (non-None) + all_service_lists = [ + (u.get('name', 'Unknown'), u.get('services') or []) for u in valid_users + ] + services = max((s for _, s in all_service_lists), key=len, default=[]) + return User( + name=name, + logo=logo, + description=description, + services=services + ) + +def format_enterprise_details(details_obj: User): + return f"""Name: {details_obj.name} +Description: {details_obj.description} +Services: +- {'\n- '.join(details_obj.services)} +""" + +async def get_data(url:str): + + async with AsyncWebCrawler(config= browser_cfg) as crawler: + result = await crawler.arun( + url = url, + config = crawl_config) + + if result.success: + print(f"Successfully scraped : '\n\n\n {result.extracted_content}") + lists = json.loads(result.extracted_content) # here instead of returning the last we may refine the one we need + #print(lists) + print(aggregate_users(lists)) + return format_enterprise_details(aggregate_users(lists)) + + else: + print(f"The code exited with eroor {result.error_message}") diff --git a/GS_Sales_Proposal/WebScraper/scrape_utils.py b/GS_Sales_Proposal/WebScraper/scrape_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..6384ef74de156d03326b5fda7b0ed8ea2da12d1f --- /dev/null +++ b/GS_Sales_Proposal/WebScraper/scrape_utils.py @@ -0,0 +1,32 @@ +import re +import requests +from bs4 import BeautifulSoup +from urllib.parse import urljoin + +def extract_hex_colors(url: str, limit: int = 5) -> list: + try: + response = requests.get(url, timeout=10) + soup = BeautifulSoup(response.text, 'html.parser') + + # Find inline styles + inline_styles = [tag.get('style', '') for tag in soup.find_all(style=True)] + css_text = ' '.join(inline_styles) + + # Find linked stylesheets + css_links = [link['href'] for link in soup.find_all('link', rel='stylesheet') if 'href' in link.attrs] + + for href in css_links: + full_url = urljoin(url, href) + try: + css_response = requests.get(full_url, timeout=5) + css_text += ' ' + css_response.text + except: + continue + + # Extract hex codes + hex_colors = re.findall(r'#[0-9a-fA-F]{3,6}', css_text) + hex_colors = list(dict.fromkeys(hex_colors)) # remove duplicates, preserve order + return hex_colors[:limit] # return top `limit` hex codes + except Exception as e: + print(f"Error extracting hex colors: {e}") + return [] diff --git a/GS_Sales_Proposal/WebScraper/state.py b/GS_Sales_Proposal/WebScraper/state.py new file mode 100644 index 0000000000000000000000000000000000000000..2ce2a204fc608b168f832338c813bacc0e472af2 --- /dev/null +++ b/GS_Sales_Proposal/WebScraper/state.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel,Field +from typing import List + +class User(BaseModel): + name : str + logo : str + description : str + services: List[str] \ No newline at end of file diff --git a/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent.cpython-312.pyc b/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03a0e92d3d85909b0f2e41fbcf82bcf361b23e8a Binary files /dev/null and b/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent.cpython-312.pyc differ diff --git a/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent.cpython-313.pyc b/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4e96d2ad5b1d3c793bc675b09f4ecdac581ca7c Binary files /dev/null and b/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent_runner.cpython-313.pyc b/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent_runner.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5977c703a6f74d6186e26653c096ea1979596a3b Binary files /dev/null and b/GS_Sales_Proposal/WebsiteUrl_Agent/__pycache__/agent_runner.cpython-313.pyc differ diff --git a/GS_Sales_Proposal/WebsiteUrl_Agent/agent.py b/GS_Sales_Proposal/WebsiteUrl_Agent/agent.py new file mode 100644 index 0000000000000000000000000000000000000000..19caf29f71c372dbb4efa8c26cfee5765dd432e8 --- /dev/null +++ b/GS_Sales_Proposal/WebsiteUrl_Agent/agent.py @@ -0,0 +1,67 @@ +from google.adk.agents import Agent +from google.adk.tools import google_search +from pydantic import BaseModel,Field +from dotenv import load_dotenv +from google.adk.sessions import InMemorySessionService +from google.adk.runners import Runner +from google.genai import types +import ast +import re + + +load_dotenv() +class WebSite(BaseModel): + website_name : str = Field(description="Website name") + website_url : str = Field(description="Website url") + + +search_agent = Agent( + model='gemini-2.0-flash-001', + name='url_agent', + description = ( + "You are an intelligent assistant specialized in finding official and relevant websites " + "associated with a given organization or company name. Your goal is to retrieve high-quality, " + "credible links that accurately represent the digital presence of the organization." +), + instruction = ''' + Given the name of a company or organization, your task is to search and return the top 7 most relevant and credible website URLs associated with it. + + These can include: + - The official company website try fetching this and if there are multiple then show all 7 + + + Your response must be a clean Python-style list of strings, where each string is a valid URL. + + Format your response exactly like this: + + [ + "https://google.com/", + "https://cloud.google.com", + "https://accounts.google.com" + ] + + Like this any 10 urls that are related to the given organization name + + Do not include explanations, only return the list of URLs. + + IMPORTANT : Just return me list of urls no additional text + + return like + + + ---- + [ + "https://google.com/", + "https://cloud.google.com", + "https://accounts.google.com" + ] + + ---- + + VERY IMPORTANT : TEMPERATURE OF THE MODEL BE ZEROOOO AND remember dont give me like the links of youtube or linkedin or any other platforms + THE LINK SHOULD BE OFFICIAL LINK OF THE ORGANIZATION + ''', + + tools = [google_search], +) + diff --git a/GS_Sales_Proposal/WebsiteUrl_Agent/agent_runner.py b/GS_Sales_Proposal/WebsiteUrl_Agent/agent_runner.py new file mode 100644 index 0000000000000000000000000000000000000000..45954ea57e77e91007095e48871a6214f91eb40c --- /dev/null +++ b/GS_Sales_Proposal/WebsiteUrl_Agent/agent_runner.py @@ -0,0 +1,66 @@ +from google.adk.agents import Agent +from google.adk.tools import google_search +from pydantic import BaseModel,Field +from dotenv import load_dotenv +from google.adk.sessions import InMemorySessionService +from google.adk.runners import Runner +from google.genai import types +import ast +import re +from WebsiteUrl_Agent.agent import * + + +# Setup session and runner +session_service = InMemorySessionService() +SESSION_ID = 'sess' +USER_ID = 'user' + +session = session_service.create_session( + app_name="APP", + user_id=USER_ID, + session_id=SESSION_ID +) + +runner = Runner( + app_name="APP", + session_service=session_service, + agent=search_agent +) +def extract_list_from_string(s): + # Remove any prefix like 'json' and extract the JSON array part + match = re.search(r"\[.*\]", s, re.DOTALL) + if match: + try: + return json.loads(match.group()) + except json.JSONDecodeError: + print("Failed to parse list.") + else: + print("No list found.") + return None + + +import json +async def get_urls(company_name: str, runner=runner, user_id=USER_ID, session_id=SESSION_ID): + content = types.Content(role='user', parts=[types.Part(text=company_name)]) + final_msg = "" + + async for event in runner.run_async(user_id=user_id, session_id=session_id, new_message=content): + if event.is_final_response(): + if event.content and event.content.parts: + final_msg = event.content.parts[0].text + elif event.actions and event.actions.escalate: + final_msg = event.error_message + result = final_msg + result = result.strip() + if result.startswith('```python'): + result = result[len('```python'):].strip() + elif result.startswith('```json'): + result = result[len('```json'):].strip() + elif result.startswith('```'): + result = result[len('```'):].strip() + if result.endswith('```'): + result = result[:-3].strip() + final_msg = result + print(final_msg) + return json.loads(final_msg) + diff --git a/GS_Sales_Proposal/app.py b/GS_Sales_Proposal/app.py new file mode 100644 index 0000000000000000000000000000000000000000..0f94dc23a771fae92a3cfb7806fcad3f46ccf601 --- /dev/null +++ b/GS_Sales_Proposal/app.py @@ -0,0 +1,280 @@ +import streamlit as st +import time +from Client.client import client_tab,validate_client_mandatory_fields +from Seller.seller import seller_tab + +def get_sample_extracted_text(): + return """Key Requirements Extracted: + +• Project Type: Enterprise Software Development +• Timeline: 6-8 months +• Budget Range: $150,000 - $200,000 +• Team Size: 5-7 developers +• Technologies: React, Node.js, PostgreSQL +• Deployment: AWS Cloud Infrastructure +• Security: SOC 2 compliance required +• Integration: Salesforce, HubSpot APIs +• Support: 24/7 monitoring and maintenance + +Additional Notes: +- Client prefers agile methodology +- Weekly progress reports required +- UAT phase: 4 weeks +- Go-live date: Q3 2024""" + +def refresh_all_data(): + """Clear all session state and form data""" + # Clear all session state variables + keys_to_clear = [ + 'client_name_input', 'url_selector', 'pain_points', 'pain_points_extracted', + 'pain_points_placeholder', 'editable_content_area', 'pain_points_summary', + 'selected_roles', 'selected_priorities', 'problem_statement' + ] + + for key in keys_to_clear: + if key in st.session_state: + del st.session_state[key] + + # Clear any other dynamic keys (role and priority related) + keys_to_remove = [] + for key in st.session_state.keys(): + if (key.startswith('role_edit_input_') or + key.startswith('remove_role_btn_') or + key.startswith('priority_checkbox_')): + keys_to_remove.append(key) + + for key in keys_to_remove: + del st.session_state[key] + + st.success("All data has been cleared!") + st.rerun() + + +def validate_seller_mandatory_fields(): + """Validate seller mandatory fields""" + # Add your seller validation logic here + # For now, returning True as placeholder + return True + +def validate_project_mandatory_fields(): + """Validate project specification mandatory fields""" + # Add your project validation logic here + # For now, returning True as placeholder + return True + +def show_validation_popup(missing_tab_name, missing_fields=None): + """Show validation error popup""" + st.error(f"āš ļø Please complete all mandatory fields in {missing_tab_name} tab first!") + if missing_fields: + st.error(f"Missing required fields: {missing_fields}") + +def validate_mandatory_fields(): + """Validate mandatory fields and return validation results""" + errors = [] + + # Check client name + client_name = st.session_state.get('client_name_input', '').strip() + if not client_name: + errors.append("Client Name") + + # Check problem statement + problem_statement = st.session_state.get('problem_statement', '').strip() + if not problem_statement: + errors.append("Problem Statement") + + return errors + +def generate_presentation(): + """Generate presentation after validating mandatory fields""" + validation_errors = validate_mandatory_fields() + + if validation_errors: + # Trigger validation display in client tab + st.session_state.trigger_validation = True + st.session_state.show_validation = True + + # Show error message + st.error("āš ļø Please fill in all mandatory fields before generating presentation!") + + # Show specific missing fields + missing_fields = ", ".join(validation_errors) + st.error(f"Missing required fields: {missing_fields}") + + # Force rerun to show validation warnings + st.rerun() + return False + else: + st.success("āœ… All mandatory fields are filled! Generating presentation...") + with st.spinner("Generating presentation..."): + import time + time.sleep(2) # Simulate processing time + st.success("šŸŽ‰ Presentation generated successfully!") + + # You can add your actual presentation generation logic here + # For example: + # - Create PowerPoint slides + # - Generate PDF report + # - Send to external API + # - Save to database + + return True + +# NOTE: Add this line at the very top of your main script (before any other Streamlit commands): +# st.set_page_config(page_title="Sales Proposal Generator", page_icon="šŸ“Š", layout="wide", initial_sidebar_state="collapsed") + +from main_css import * +st.markdown(app_css, unsafe_allow_html=True) + +# Initialize session state for active tab - ENSURE CLIENT TAB IS DEFAULT +if 'active_tab' not in st.session_state: + st.session_state.active_tab = 0 + +# Tab buttons +tab_names = ["Client Information", "Seller Information", "Project Specifications", "Generate Proposal"] + +# Create tab buttons with full width +cols = st.columns(4, gap="large") +for i, tab_name in enumerate(tab_names): + with cols[i]: + is_active = (i == st.session_state.active_tab) + + # Determine if tab should be clickable based on validation + tab_enabled = True + if i == 1 and not validate_client_mandatory_fields(): + tab_enabled = False + elif i == 2 and (not validate_client_mandatory_fields() or not validate_seller_mandatory_fields()): + tab_enabled = False + elif i == 3 and (not validate_client_mandatory_fields() or not validate_seller_mandatory_fields() or not validate_project_mandatory_fields()): + tab_enabled = False + + if tab_enabled: + if st.button(tab_name, key=f"tab_{i}", use_container_width=True, type="primary" if is_active else "secondary"): + st.session_state.active_tab = i + st.rerun() + else: + st.button(tab_name, key=f"tab_{i}", use_container_width=True, disabled=True) + +# Force active tab to stay highlighted +# Force active tab to stay highlighted with blue color +# Force active tab (Client or any valid tab) to stay blue +tab_highlight_css = f""" + +""" +st.markdown(tab_highlight_css, unsafe_allow_html=True) + +# Set is_active flag for current tab +st.session_state.is_active = True + +# Content area with validation-aware tab switching +if st.session_state.active_tab == 0: + client_tab(st) + +elif st.session_state.active_tab == 1: + # Double-check validation before showing seller tab + if validate_client_mandatory_fields(): + seller_tab() + else: + st.session_state.active_tab = 0 # Force back to client tab + show_validation_popup("Client Information") + st.rerun() + +elif st.session_state.active_tab == 2: + # Check both client and seller validations + if not validate_client_mandatory_fields(): + st.session_state.active_tab = 0 # Force back to client tab + show_validation_popup("Client Information") + st.rerun() + elif not validate_seller_mandatory_fields(): + st.session_state.active_tab = 1 # Force back to seller tab + show_validation_popup("Seller Information") + st.rerun() + else: + st.markdown('## šŸ‘„ Project Specifications') + st.markdown("Define your project requirements and specifications.") + + col1, col2, col3 = st.columns([2, 2, 2], gap="large") + + with col1: + st.markdown(""" + **Recent Clients** + - Acme Corporation + - TechStart Inc + - Global Solutions Ltd + - Innovation Labs + - Digital Dynamics + - Future Systems Co + """) + + with col2: + st.metric("Total Proposals", "47", "+12%") + + with col3: + st.metric("Success Rate", "73%", "+5%") + +else: # Generate Proposal Tab + # Check all validations + if not validate_client_mandatory_fields(): + st.session_state.active_tab = 0 + show_validation_popup("Client Information") + st.rerun() + elif not validate_seller_mandatory_fields(): + st.session_state.active_tab = 1 + show_validation_popup("Seller Information") + st.rerun() + elif not validate_project_mandatory_fields(): + st.session_state.active_tab = 2 + show_validation_popup("Project Specifications") + st.rerun() + else: + st.markdown('## šŸ“Š Generate Proposal') + st.markdown("Review and generate your final proposal.") + + # Metrics row + col1, col2, col3, col4 = st.columns(4, gap="large") + + with col1: + st.metric("This Month", "$125K", "+15%") + + with col2: + st.metric("Proposals Sent", "23", "+3") + + with col3: + st.metric("Conversion Rate", "68%", "+12%") + + with col4: + st.metric("Average Value", "$18.5K", "+8%") + + st.markdown("---") + + st.markdown(""" + ### šŸ“ˆ Performance Trends + Your proposal success rate has improved by 12% this quarter, with the highest performance in software development projects. The average deal size has increased significantly, and client satisfaction scores are at an all-time high. + + **Key insights:** Enterprise clients show 85% higher conversion rates, and proposals with detailed technical specifications convert 40% better than generic templates. + """) + +col1, col2 = st.columns(2, gap="large") + +with col1: + if st.button("šŸ”„ Refresh All Data", key="refresh_btn", use_container_width=True): + refresh_all_data() + +with col2: + if st.button("šŸ“Š Generate Presentation", key="generate_btn", use_container_width=True): + generate_presentation() \ No newline at end of file diff --git a/GS_Sales_Proposal/environment.yml b/GS_Sales_Proposal/environment.yml new file mode 100644 index 0000000000000000000000000000000000000000..b335b41ba56fe8aa024fdec5463e7f851bdd371a --- /dev/null +++ b/GS_Sales_Proposal/environment.yml @@ -0,0 +1,332 @@ +name: adk +channels: + - conda-forge + - defaults +dependencies: + - _libgcc_mutex=0.1=main + - _openmp_mutex=5.1=1_gnu + - asttokens=3.0.0=pyhd8ed1ab_1 + - bzip2=1.0.8=h5eee18b_6 + - ca-certificates=2025.4.26=hbd8a1cb_0 + - comm=0.2.2=pyhd8ed1ab_1 + - debugpy=1.8.14=py313h46c70d0_0 + - decorator=5.2.1=pyhd8ed1ab_0 + - exceptiongroup=1.3.0=pyhd8ed1ab_0 + - executing=2.2.0=pyhd8ed1ab_0 + - expat=2.7.1=h6a678d5_0 + - importlib-metadata=8.6.1=pyha770c72_0 + - ipykernel=6.29.5=pyh3099207_0 + - ipython_pygments_lexers=1.1.1=pyhd8ed1ab_0 + - jedi=0.19.2=pyhd8ed1ab_1 + - jupyter_client=8.6.3=pyhd8ed1ab_1 + - jupyter_core=5.7.2=pyh31011fe_1 + - krb5=1.21.3=h143b758_0 + - ld_impl_linux-64=2.40=h12ee557_0 + - libedit=3.1.20230828=h5eee18b_0 + - libffi=3.4.4=h6a678d5_1 + - libgcc=15.1.0=h767d61c_2 + - libgcc-ng=15.1.0=h69a702a_2 + - libgomp=15.1.0=h767d61c_2 + - libmpdec=4.0.0=h5eee18b_0 + - libsodium=1.0.20=h4ab18f5_0 + - libstdcxx=15.1.0=h8f9b012_2 + - libstdcxx-ng=11.2.0=h1234567_1 + - libuuid=1.41.5=h5eee18b_0 + - matplotlib-inline=0.1.7=pyhd8ed1ab_1 + - ncurses=6.4=h6a678d5_0 + - nest-asyncio=1.6.0=pyhd8ed1ab_1 + - openssl=3.5.0=h7b32b05_1 + - parso=0.8.4=pyhd8ed1ab_1 + - pexpect=4.9.0=pyhd8ed1ab_1 + - pickleshare=0.7.5=pyhd8ed1ab_1004 + - platformdirs=4.3.8=pyhe01879c_0 + - prompt-toolkit=3.0.51=pyha770c72_0 + - psutil=7.0.0=py313h536fd9c_0 + - ptyprocess=0.7.0=pyhd8ed1ab_1 + - pure_eval=0.2.3=pyhd8ed1ab_1 + - pygments=2.19.1=pyhd8ed1ab_0 + - python=3.13.2=hf623796_100_cp313 + - python-dateutil=2.9.0.post0=pyhff2d567_1 + - python_abi=3.13=0_cp313 + - pyzmq=26.4.0=py313h8e95178_0 + - readline=8.2=h5eee18b_0 + - six=1.17.0=pyhd8ed1ab_0 + - sqlite=3.45.3=h5eee18b_0 + - stack_data=0.6.3=pyhd8ed1ab_1 + - tk=8.6.14=h39e8969_0 + - tornado=6.5=py313h536fd9c_0 + - traitlets=5.14.3=pyhd8ed1ab_1 + - wcwidth=0.2.13=pyhd8ed1ab_1 + - wheel=0.45.1=py313h06a4308_0 + - xz=5.6.4=h5eee18b_1 + - zeromq=4.3.5=h3b0a872_7 + - zipp=3.21.0=pyhd8ed1ab_1 + - zlib=1.2.13=h5eee18b_1 + - pip: + - aiofiles==24.1.0 + - aiohappyeyeballs==2.6.1 + - aiohttp==3.11.18 + - aiosignal==1.3.2 + - aiosqlite==0.21.0 + - altair==5.5.0 + - annotated-types==0.7.0 + - anyio==4.9.0 + - asgiref==3.8.1 + - attrs==25.3.0 + - authlib==1.5.2 + - backcall==0.2.0 + - backoff==2.2.1 + - bcrypt==4.3.0 + - beautifulsoup4==4.13.4 + - bleach==6.2.0 + - blinker==1.9.0 + - brotli==1.1.0 + - build==1.2.2.post1 + - cachetools==5.5.2 + - certifi==2025.4.26 + - cffi==1.17.1 + - chardet==5.2.0 + - charset-normalizer==3.4.2 + - chromadb==1.0.12 + - click==8.2.0 + - colorama==0.4.6 + - coloredlogs==15.0.1 + - colorthief==0.2.1 + - crawl4ai==0.6.3 + - cryptography==44.0.3 + - cssselect==1.3.0 + - cssselect2==0.8.0 + - curl-cffi==0.11.1 + - dataclasses-json==0.6.7 + - defusedxml==0.7.1 + - deprecated==1.2.18 + - distro==1.9.0 + - docopt==0.6.2 + - docstring-parser==0.16 + - dotenv==0.9.9 + - durationpy==0.10 + - fake-http-header==0.3.5 + - fake-useragent==2.2.0 + - fastapi==0.115.9 + - fastjsonschema==2.21.1 + - filelock==3.18.0 + - filetype==1.2.0 + - flatbuffers==25.2.10 + - fonttools==4.58.2 + - frozendict==2.4.6 + - frozenlist==1.6.0 + - fsspec==2025.5.1 + - gitdb==4.0.12 + - gitpython==3.1.44 + - google-adk==0.5.0 + - google-ai-generativelanguage==0.6.15 + - google-api-core==2.24.2 + - google-api-python-client==2.169.0 + - google-auth==2.40.1 + - google-auth-httplib2==0.2.0 + - google-cloud-aiplatform==1.92.0 + - google-cloud-bigquery==3.32.0 + - google-cloud-core==2.4.3 + - google-cloud-resource-manager==1.14.2 + - google-cloud-secret-manager==2.23.3 + - google-cloud-speech==2.32.0 + - google-cloud-storage==2.19.0 + - google-cloud-trace==1.16.1 + - google-crc32c==1.7.1 + - google-genai==1.15.0 + - google-generativeai==0.8.5 + - google-resumable-media==2.7.2 + - googleapis-common-protos==1.70.0 + - grandalf==0.8 + - greenlet==3.2.2 + - groq==0.28.0 + - grpc-google-iam-v1==0.14.2 + - grpcio==1.71.0 + - grpcio-status==1.71.0 + - h11==0.16.0 + - httpcore==1.0.9 + - httplib2==0.22.0 + - httptools==0.6.4 + - httpx==0.28.1 + - httpx-sse==0.4.0 + - huggingface-hub==0.31.2 + - humanfriendly==10.0 + - humanize==4.12.3 + - idna==3.10 + - importlib-resources==6.5.2 + - ipython==8.12.3 + - jinja2==3.1.6 + - jiter==0.9.0 + - joblib==1.5.1 + - jsonpatch==1.33 + - jsonpointer==3.0.0 + - jsonschema==4.23.0 + - jsonschema-specifications==2025.4.1 + - jupyterlab-pygments==0.3.0 + - kubernetes==33.1.0 + - langchain==0.3.25 + - langchain-chroma==0.2.4 + - langchain-community==0.3.25 + - langchain-core==0.3.65 + - langchain-google-genai==2.0.10 + - langchain-groq==0.3.2 + - langchain-huggingface==0.3.0 + - langchain-text-splitters==0.3.8 + - langgraph==0.4.8 + - langgraph-checkpoint==2.0.26 + - langgraph-cli==0.3.3 + - langgraph-prebuilt==0.2.2 + - langgraph-sdk==0.1.70 + - langsmith==0.3.45 + - litellm==1.69.3 + - lxml==5.4.0 + - markdown-it-py==3.0.0 + - markupsafe==3.0.2 + - marshmallow==3.26.1 + - mcp==1.8.1 + - mdurl==0.1.2 + - mistune==3.1.3 + - mmh3==5.1.0 + - mpmath==1.3.0 + - multidict==6.4.3 + - multitasking==0.0.11 + - mypy-extensions==1.1.0 + - narwhals==1.41.0 + - nbclient==0.10.2 + - nbconvert==7.16.6 + - nbformat==5.10.4 + - networkx==3.5 + - nltk==3.9.1 + - numpy==2.2.5 + - nvidia-cublas-cu12==12.6.4.1 + - nvidia-cuda-cupti-cu12==12.6.80 + - nvidia-cuda-nvrtc-cu12==12.6.77 + - nvidia-cuda-runtime-cu12==12.6.77 + - nvidia-cudnn-cu12==9.5.1.17 + - nvidia-cufft-cu12==11.3.0.4 + - nvidia-cufile-cu12==1.11.1.6 + - nvidia-curand-cu12==10.3.7.77 + - nvidia-cusolver-cu12==11.7.1.2 + - nvidia-cusparse-cu12==12.5.4.2 + - nvidia-cusparselt-cu12==0.6.3 + - nvidia-nccl-cu12==2.26.2 + - nvidia-nvjitlink-cu12==12.6.85 + - nvidia-nvtx-cu12==12.6.77 + - oauthlib==3.2.2 + - onnxruntime==1.22.0 + - openai==1.75.0 + - opentelemetry-api==1.33.0 + - opentelemetry-exporter-gcp-trace==1.9.0 + - opentelemetry-exporter-otlp-proto-common==1.33.0 + - opentelemetry-exporter-otlp-proto-grpc==1.33.0 + - opentelemetry-instrumentation==0.54b0 + - opentelemetry-instrumentation-asgi==0.54b0 + - opentelemetry-instrumentation-fastapi==0.54b0 + - opentelemetry-proto==1.33.0 + - opentelemetry-resourcedetector-gcp==1.9.0a0 + - opentelemetry-sdk==1.33.0 + - opentelemetry-semantic-conventions==0.54b0 + - opentelemetry-util-http==0.54b0 + - orjson==3.10.18 + - ormsgpack==1.10.0 + - overrides==7.7.0 + - packaging==24.2 + - pandas==2.2.3 + - pandocfilters==1.5.1 + - pdf2image==1.17.0 + - pdfkit==1.0.0 + - peewee==3.18.1 + - pillow==10.4.0 + - pip==25.1.1 + - pipreqs==0.4.13 + - playwright==1.52.0 + - posthog==4.10.0 + - propcache==0.3.1 + - proto-plus==1.26.1 + - protobuf==5.29.4 + - pyarrow==20.0.0 + - pyasn1==0.6.1 + - pyasn1-modules==0.4.2 + - pycparser==2.22 + - pydantic==2.11.4 + - pydantic-core==2.33.2 + - pydantic-settings==2.9.1 + - pydeck==0.9.1 + - pydyf==0.11.0 + - pyee==13.0.0 + - pyopenssl==25.1.0 + - pyparsing==3.2.3 + - pypdf==5.6.0 + - pyperclip==1.9.0 + - pyphen==0.17.2 + - pypika==0.48.9 + - pyproject-hooks==1.2.0 + - python-dotenv==1.1.0 + - python-graphviz==0.20.3 + - python-multipart==0.0.20 + - python-pptx==1.0.2 + - pytz==2025.2 + - pyyaml==6.0.2 + - rank-bm25==0.2.2 + - referencing==0.36.2 + - regex==2024.11.6 + - requests==2.32.3 + - requests-oauthlib==2.0.0 + - requests-toolbelt==1.0.0 + - rich==14.0.0 + - rpds-py==0.25.0 + - rsa==4.9.1 + - safetensors==0.5.3 + - scikit-learn==1.7.0 + - scipy==1.15.3 + - sentence-transformers==4.1.0 + - setuptools==80.9.0 + - shapely==2.1.0 + - shellingham==1.5.4 + - smmap==5.0.2 + - sniffio==1.3.1 + - snowballstemmer==2.2.0 + - soupsieve==2.7 + - sqlalchemy==2.0.41 + - sse-starlette==2.3.5 + - starlette==0.45.3 + - streamlit==1.45.1 + - sympy==1.14.0 + - tenacity==9.1.2 + - tf-playwright-stealth==1.1.2 + - threadpoolctl==3.6.0 + - tiktoken==0.9.0 + - tinycss2==1.4.0 + - tinyhtml5==2.0.0 + - tokenizers==0.21.1 + - toml==0.10.2 + - torch==2.7.1 + - tqdm==4.67.1 + - transformers==4.52.4 + - triton==3.3.1 + - typer==0.16.0 + - typing-extensions==4.14.0 + - typing-inspect==0.9.0 + - typing-inspection==0.4.0 + - tzdata==2025.2 + - tzlocal==5.3.1 + - uritemplate==4.1.1 + - urllib3==2.4.0 + - uv==0.7.3 + - uvicorn==0.34.2 + - uvloop==0.21.0 + - watchdog==6.0.0 + - watchfiles==1.1.0 + - weasyprint==65.1 + - webencodings==0.5.1 + - websocket-client==1.8.0 + - websockets==15.0.1 + - wrapt==1.17.2 + - xlsxwriter==3.2.3 + - xxhash==3.5.0 + - yarg==0.1.9 + - yarl==1.20.0 + - yfinance==0.2.61 + - zopfli==0.2.3.post1 + - zstandard==0.23.0 +prefix: /home/shreyank/anaconda3/envs/adk diff --git a/GS_Sales_Proposal/logs/client_logs.log b/GS_Sales_Proposal/logs/client_logs.log new file mode 100644 index 0000000000000000000000000000000000000000..11789dc421235873f45201e7b86965643e5e3941 --- /dev/null +++ b/GS_Sales_Proposal/logs/client_logs.log @@ -0,0 +1,1840 @@ +2025-06-27 08:19:48,240 - client_module - INFO - validate_client_mandatory_fields:103 - Starting client mandatory fields validation +2025-06-27 08:19:48,241 - client_module - DEBUG - validate_client_mandatory_fields:107 - Retrieved client data for validation +2025-06-27 08:19:48,241 - client_module - INFO - validate_client_mandatory_fields:110 - Validation bypassed - returning True +2025-06-27 08:19:48,241 - client_module - INFO - validate_client_mandatory_fields:103 - Starting client mandatory fields validation +2025-06-27 08:19:48,241 - client_module - DEBUG - validate_client_mandatory_fields:107 - Retrieved client data for validation +2025-06-27 08:19:48,241 - client_module - INFO - validate_client_mandatory_fields:110 - Validation bypassed - returning True +2025-06-27 08:19:48,242 - client_module - INFO - validate_client_mandatory_fields:103 - Starting client mandatory fields validation +2025-06-27 08:19:48,243 - client_module - DEBUG - validate_client_mandatory_fields:107 - Retrieved client data for validation +2025-06-27 08:19:48,243 - client_module - INFO - validate_client_mandatory_fields:110 - Validation bypassed - returning True +2025-06-27 08:19:48,245 - client_module - INFO - client_tab:124 - Starting client_tab function +2025-06-27 08:19:48,245 - client_module - DEBUG - client_tab:129 - Retrieved client data from dataclass manager +2025-06-27 08:19:48,245 - client_module - DEBUG - client_tab:134 - Applying CSS for the first time +2025-06-27 08:19:48,246 - client_module - INFO - client_tab:137 - CSS applied and updated in client data +2025-06-27 08:19:48,248 - client_module - DEBUG - client_tab:141 - CSS re-applied for persistence +2025-06-27 08:19:48,248 - client_module - DEBUG - client_tab:148 - Creating top section with client name and URLs +2025-06-27 08:19:48,249 - client_module - DEBUG - client_tab:153 - Processing client enterprise name section +2025-06-27 08:19:48,250 - client_module - DEBUG - client_tab:186 - Find URLs button disabled: True +2025-06-27 08:19:48,251 - client_module - DEBUG - client_tab:241 - Processing client website URL section +2025-06-27 08:19:48,253 - client_module - DEBUG - client_tab:258 - Client name provided: False +2025-06-27 08:19:48,253 - client_module - DEBUG - client_tab:265 - URL options count: 1 +2025-06-27 08:19:48,255 - client_module - DEBUG - client_tab:386 - Creating file upload and enterprise details section +2025-06-27 08:19:48,256 - client_module - DEBUG - client_tab:391 - Processing file upload section +2025-06-27 08:19:48,257 - client_module - DEBUG - client_tab:563 - Processing enterprise details section +2025-06-27 08:19:48,258 - client_module - DEBUG - client_tab:601 - Creating client requirements and pain points section +2025-06-27 08:19:48,259 - client_module - DEBUG - client_tab:606 - Processing client requirements section +2025-06-27 08:19:48,260 - client_module - DEBUG - client_tab:635 - Client requirements provided: False +2025-06-27 08:19:48,260 - client_module - INFO - client_tab:650 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:19:48,261 - client_module - INFO - client_tab:674 - Using dummy pain points data as fallback +2025-06-27 08:19:48,261 - client_module - DEBUG - client_tab:684 - Rendering 3 pain point items +2025-06-27 08:19:48,261 - client_module - DEBUG - client_tab:689 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:19:48,262 - client_module - DEBUG - client_tab:694 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:19:48,264 - client_module - DEBUG - client_tab:893 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:19:48,265 - client_module - DEBUG - client_tab:689 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:19:48,265 - client_module - DEBUG - client_tab:694 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:19:48,267 - client_module - DEBUG - client_tab:893 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:19:48,267 - client_module - DEBUG - client_tab:689 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:19:48,267 - client_module - DEBUG - client_tab:694 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:19:48,270 - client_module - DEBUG - client_tab:893 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:19:48,270 - client_module - INFO - client_tab:913 - Starting SPOC Row rendering +2025-06-27 08:19:48,271 - client_module - DEBUG - client_tab:918 - Rendering SPOC Name section +2025-06-27 08:19:48,273 - client_module - DEBUG - client_tab:937 - SPOC name input value: '' +2025-06-27 08:19:48,273 - client_module - DEBUG - client_tab:979 - Rendering SPOC LinkedIn Profile section +2025-06-27 08:19:48,275 - client_module - DEBUG - client_tab:984 - SPOC name provided status: False +2025-06-27 08:19:48,276 - client_module - DEBUG - client_tab:1066 - LinkedIn profile selector in disabled state +2025-06-27 08:19:48,277 - client_module - INFO - client_tab:1200 - SPOC section completed successfully +2025-06-27 08:23:17,535 - client_module - INFO - validate_client_mandatory_fields:103 - Starting client mandatory fields validation +2025-06-27 08:23:17,536 - client_module - DEBUG - validate_client_mandatory_fields:107 - Retrieved client data for validation +2025-06-27 08:23:17,537 - client_module - INFO - validate_client_mandatory_fields:110 - Validation bypassed - returning True +2025-06-27 08:23:17,538 - client_module - INFO - validate_client_mandatory_fields:103 - Starting client mandatory fields validation +2025-06-27 08:23:17,540 - client_module - DEBUG - validate_client_mandatory_fields:107 - Retrieved client data for validation +2025-06-27 08:23:17,540 - client_module - INFO - validate_client_mandatory_fields:110 - Validation bypassed - returning True +2025-06-27 08:23:17,542 - client_module - INFO - validate_client_mandatory_fields:103 - Starting client mandatory fields validation +2025-06-27 08:23:17,543 - client_module - DEBUG - validate_client_mandatory_fields:107 - Retrieved client data for validation +2025-06-27 08:23:17,544 - client_module - INFO - validate_client_mandatory_fields:110 - Validation bypassed - returning True +2025-06-27 08:23:17,547 - client_module - INFO - client_tab:124 - Starting client_tab function +2025-06-27 08:23:17,548 - client_module - DEBUG - client_tab:129 - Retrieved client data from dataclass manager +2025-06-27 08:23:17,550 - client_module - DEBUG - client_tab:141 - CSS re-applied for persistence +2025-06-27 08:23:17,551 - client_module - DEBUG - client_tab:148 - Creating top section with client name and URLs +2025-06-27 08:23:17,552 - client_module - DEBUG - client_tab:153 - Processing client enterprise name section +2025-06-27 08:23:17,555 - client_module - INFO - client_tab:178 - Updated enterprise name: jhjh +2025-06-27 08:23:17,556 - client_module - DEBUG - client_tab:186 - Find URLs button disabled: False +2025-06-27 08:23:17,557 - client_module - DEBUG - client_tab:241 - Processing client website URL section +2025-06-27 08:23:17,560 - client_module - DEBUG - client_tab:258 - Client name provided: True +2025-06-27 08:23:17,562 - client_module - DEBUG - client_tab:265 - URL options count: 1 +2025-06-27 08:23:17,567 - client_module - DEBUG - client_tab:386 - Creating file upload and enterprise details section +2025-06-27 08:23:17,568 - client_module - DEBUG - client_tab:391 - Processing file upload section +2025-06-27 08:23:17,570 - client_module - DEBUG - client_tab:563 - Processing enterprise details section +2025-06-27 08:23:17,573 - client_module - DEBUG - client_tab:601 - Creating client requirements and pain points section +2025-06-27 08:23:17,574 - client_module - DEBUG - client_tab:606 - Processing client requirements section +2025-06-27 08:23:17,575 - client_module - DEBUG - client_tab:630 - Updated client requirements content +2025-06-27 08:23:17,576 - client_module - DEBUG - client_tab:635 - Client requirements provided: False +2025-06-27 08:23:17,576 - client_module - INFO - client_tab:650 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:23:17,577 - client_module - INFO - client_tab:674 - Using dummy pain points data as fallback +2025-06-27 08:23:17,577 - client_module - DEBUG - client_tab:684 - Rendering 3 pain point items +2025-06-27 08:23:17,578 - client_module - DEBUG - client_tab:689 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:23:17,578 - client_module - DEBUG - client_tab:694 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:23:17,581 - client_module - DEBUG - client_tab:893 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:23:17,582 - client_module - DEBUG - client_tab:689 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:23:17,582 - client_module - DEBUG - client_tab:694 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:23:17,585 - client_module - DEBUG - client_tab:893 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:23:17,585 - client_module - DEBUG - client_tab:689 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:23:17,586 - client_module - DEBUG - client_tab:694 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:23:17,588 - client_module - DEBUG - client_tab:893 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:23:17,589 - client_module - INFO - client_tab:913 - Starting SPOC Row rendering +2025-06-27 08:23:17,591 - client_module - DEBUG - client_tab:918 - Rendering SPOC Name section +2025-06-27 08:23:17,592 - client_module - DEBUG - client_tab:937 - SPOC name input value: '' +2025-06-27 08:23:17,593 - client_module - DEBUG - client_tab:979 - Rendering SPOC LinkedIn Profile section +2025-06-27 08:23:17,593 - client_module - DEBUG - client_tab:984 - SPOC name provided status: False +2025-06-27 08:23:17,595 - client_module - DEBUG - client_tab:1066 - LinkedIn profile selector in disabled state +2025-06-27 08:23:17,595 - client_module - INFO - client_tab:1200 - SPOC section completed successfully +2025-06-27 08:37:06,444 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:06,444 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:06,445 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:06,445 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:06,445 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:06,445 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:06,445 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:06,445 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:06,446 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:06,446 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:06,446 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:06,446 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:37:06,447 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:37:06,447 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:06,447 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:06,447 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:06,448 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: True +2025-06-27 08:37:06,448 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:06,449 - client_module - DEBUG - client_tab:840 - Client name provided: False +2025-06-27 08:37:06,449 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:06,449 - client_module - DEBUG - client_tab:968 - Creating file upload and enterprise details section +2025-06-27 08:37:06,450 - client_module - DEBUG - client_tab:973 - Processing file upload section +2025-06-27 08:37:06,450 - client_module - DEBUG - client_tab:1145 - Processing enterprise details section +2025-06-27 08:37:06,450 - client_module - DEBUG - client_tab:1183 - Creating client requirements and pain points section +2025-06-27 08:37:06,451 - client_module - DEBUG - client_tab:1188 - Processing client requirements section +2025-06-27 08:37:06,451 - client_module - DEBUG - client_tab:1217 - Client requirements provided: False +2025-06-27 08:37:06,451 - client_module - INFO - client_tab:1232 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:37:06,451 - client_module - INFO - client_tab:1256 - Using dummy pain points data as fallback +2025-06-27 08:37:06,452 - client_module - DEBUG - client_tab:1266 - Rendering 3 pain point items +2025-06-27 08:37:06,452 - client_module - DEBUG - client_tab:1271 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:37:06,452 - client_module - DEBUG - client_tab:1276 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:37:06,453 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:37:06,453 - client_module - DEBUG - client_tab:1271 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:37:06,453 - client_module - DEBUG - client_tab:1276 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:37:06,453 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:37:06,454 - client_module - DEBUG - client_tab:1271 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:37:06,454 - client_module - DEBUG - client_tab:1276 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:37:06,454 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:37:06,455 - client_module - INFO - client_tab:1506 - Rendered SPOC name tooltip +2025-06-27 08:37:06,455 - client_module - INFO - client_tab:1519 - SPOC name input rendered with value: +2025-06-27 08:37:06,455 - client_module - INFO - client_tab:1551 - SPOC name provided status: False +2025-06-27 08:37:06,456 - client_module - INFO - client_tab:1563 - Rendered LinkedIn profile tooltip +2025-06-27 08:37:06,456 - client_module - INFO - client_tab:1619 - LinkedIn profile selector in disabled state +2025-06-27 08:37:06,456 - client_module - INFO - client_tab:1700 - Created roles and priorities columns +2025-06-27 08:37:06,457 - client_module - INFO - client_tab:1712 - Rendered SPOC role tooltip +2025-06-27 08:37:06,457 - client_module - INFO - client_tab:1722 - Retrieved 10 default roles +2025-06-27 08:37:06,458 - client_module - INFO - client_tab:1781 - Role selector rendered with selection: Select a role... +2025-06-27 08:37:06,458 - client_module - INFO - client_tab:1796 - Cleared selected target role +2025-06-27 08:37:06,458 - client_module - INFO - client_tab:1809 - Rendered business priorities tooltip +2025-06-27 08:37:06,458 - client_module - INFO - client_tab:1838 - Using 3 business priorities +2025-06-27 08:37:06,459 - client_module - INFO - client_tab:1888 - Created additional requirements columns +2025-06-27 08:37:06,459 - client_module - INFO - client_tab:1900 - Rendered additional client requirements tooltip +2025-06-27 08:37:06,460 - client_module - INFO - client_tab:1915 - Additional requirements text area rendered with 0 characters +2025-06-27 08:37:06,460 - client_module - INFO - client_tab:1931 - Additional requirements provided status: False +2025-06-27 08:37:06,460 - client_module - INFO - client_tab:1945 - Rendered additional specifications tooltip +2025-06-27 08:37:06,460 - client_module - INFO - client_tab:1963 - Using default additional specs items +2025-06-27 08:37:20,950 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:20,950 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:20,951 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:20,952 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:20,952 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:20,952 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:20,953 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:20,953 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:20,953 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:20,953 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:20,954 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:20,954 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:37:20,955 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:37:20,956 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:20,956 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:20,957 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:20,958 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: True +2025-06-27 08:37:20,959 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:20,959 - client_module - DEBUG - client_tab:840 - Client name provided: False +2025-06-27 08:37:20,960 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:20,961 - client_module - DEBUG - client_tab:968 - Creating file upload and enterprise details section +2025-06-27 08:37:20,961 - client_module - DEBUG - client_tab:973 - Processing file upload section +2025-06-27 08:37:20,962 - client_module - DEBUG - client_tab:1145 - Processing enterprise details section +2025-06-27 08:37:20,962 - client_module - DEBUG - client_tab:1183 - Creating client requirements and pain points section +2025-06-27 08:37:20,962 - client_module - DEBUG - client_tab:1188 - Processing client requirements section +2025-06-27 08:37:20,963 - client_module - DEBUG - client_tab:1217 - Client requirements provided: False +2025-06-27 08:37:20,963 - client_module - INFO - client_tab:1232 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:37:20,964 - client_module - INFO - client_tab:1256 - Using dummy pain points data as fallback +2025-06-27 08:37:20,964 - client_module - DEBUG - client_tab:1266 - Rendering 3 pain point items +2025-06-27 08:37:20,964 - client_module - DEBUG - client_tab:1271 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:37:20,964 - client_module - DEBUG - client_tab:1276 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:37:20,965 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:37:20,965 - client_module - DEBUG - client_tab:1271 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:37:20,965 - client_module - DEBUG - client_tab:1276 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:37:20,966 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:37:20,966 - client_module - DEBUG - client_tab:1271 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:37:20,966 - client_module - DEBUG - client_tab:1276 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:37:20,967 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:37:20,967 - client_module - INFO - client_tab:1506 - Rendered SPOC name tooltip +2025-06-27 08:37:20,968 - client_module - INFO - client_tab:1519 - SPOC name input rendered with value: +2025-06-27 08:37:20,968 - client_module - INFO - client_tab:1551 - SPOC name provided status: False +2025-06-27 08:37:20,968 - client_module - INFO - client_tab:1563 - Rendered LinkedIn profile tooltip +2025-06-27 08:37:20,969 - client_module - INFO - client_tab:1619 - LinkedIn profile selector in disabled state +2025-06-27 08:37:20,971 - client_module - INFO - client_tab:1700 - Created roles and priorities columns +2025-06-27 08:37:20,971 - client_module - INFO - client_tab:1712 - Rendered SPOC role tooltip +2025-06-27 08:37:20,971 - client_module - INFO - client_tab:1722 - Retrieved 10 default roles +2025-06-27 08:37:20,972 - client_module - INFO - client_tab:1781 - Role selector rendered with selection: Select a role... +2025-06-27 08:37:20,972 - client_module - INFO - client_tab:1796 - Cleared selected target role +2025-06-27 08:37:20,972 - client_module - INFO - client_tab:1809 - Rendered business priorities tooltip +2025-06-27 08:37:20,973 - client_module - INFO - client_tab:1838 - Using 3 business priorities +2025-06-27 08:37:20,974 - client_module - INFO - client_tab:1888 - Created additional requirements columns +2025-06-27 08:37:20,974 - client_module - INFO - client_tab:1900 - Rendered additional client requirements tooltip +2025-06-27 08:37:20,975 - client_module - INFO - client_tab:1915 - Additional requirements text area rendered with 0 characters +2025-06-27 08:37:20,975 - client_module - INFO - client_tab:1931 - Additional requirements provided status: False +2025-06-27 08:37:20,975 - client_module - INFO - client_tab:1945 - Rendered additional specifications tooltip +2025-06-27 08:37:20,975 - client_module - INFO - client_tab:1963 - Using default additional specs items +2025-06-27 08:37:29,460 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:29,460 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:29,460 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:29,461 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:29,461 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:29,461 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:29,462 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:29,463 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:29,463 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:29,465 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:29,465 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:29,468 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:29,468 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:29,470 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:29,472 - client_module - INFO - client_tab:760 - Updated enterprise name: sdgsdf +2025-06-27 08:37:29,473 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:37:29,474 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:29,477 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:37:29,477 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:29,481 - client_module - DEBUG - client_tab:968 - Creating file upload and enterprise details section +2025-06-27 08:37:29,482 - client_module - DEBUG - client_tab:973 - Processing file upload section +2025-06-27 08:37:29,483 - client_module - DEBUG - client_tab:1145 - Processing enterprise details section +2025-06-27 08:37:29,484 - client_module - DEBUG - client_tab:1183 - Creating client requirements and pain points section +2025-06-27 08:37:29,485 - client_module - DEBUG - client_tab:1188 - Processing client requirements section +2025-06-27 08:37:29,486 - client_module - DEBUG - client_tab:1212 - Updated client requirements content +2025-06-27 08:37:29,486 - client_module - DEBUG - client_tab:1217 - Client requirements provided: False +2025-06-27 08:37:29,486 - client_module - INFO - client_tab:1232 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:37:29,487 - client_module - INFO - client_tab:1256 - Using dummy pain points data as fallback +2025-06-27 08:37:29,488 - client_module - DEBUG - client_tab:1266 - Rendering 3 pain point items +2025-06-27 08:37:29,488 - client_module - DEBUG - client_tab:1271 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:37:29,488 - client_module - DEBUG - client_tab:1276 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:37:29,490 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:37:29,490 - client_module - DEBUG - client_tab:1271 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:37:29,490 - client_module - DEBUG - client_tab:1276 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:37:29,492 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:37:29,492 - client_module - DEBUG - client_tab:1271 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:37:29,492 - client_module - DEBUG - client_tab:1276 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:37:29,494 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:37:29,495 - client_module - INFO - client_tab:1506 - Rendered SPOC name tooltip +2025-06-27 08:37:29,495 - client_module - INFO - client_tab:1519 - SPOC name input rendered with value: +2025-06-27 08:37:29,496 - client_module - INFO - client_tab:1551 - SPOC name provided status: False +2025-06-27 08:37:29,496 - client_module - INFO - client_tab:1563 - Rendered LinkedIn profile tooltip +2025-06-27 08:37:29,497 - client_module - INFO - client_tab:1619 - LinkedIn profile selector in disabled state +2025-06-27 08:37:29,497 - client_module - INFO - client_tab:1700 - Created roles and priorities columns +2025-06-27 08:37:29,499 - client_module - INFO - client_tab:1712 - Rendered SPOC role tooltip +2025-06-27 08:37:29,499 - client_module - INFO - client_tab:1722 - Retrieved 10 default roles +2025-06-27 08:37:29,499 - client_module - INFO - client_tab:1781 - Role selector rendered with selection: Select a role... +2025-06-27 08:37:29,500 - client_module - INFO - client_tab:1796 - Cleared selected target role +2025-06-27 08:37:29,500 - client_module - INFO - client_tab:1809 - Rendered business priorities tooltip +2025-06-27 08:37:29,500 - client_module - INFO - client_tab:1838 - Using 3 business priorities +2025-06-27 08:37:29,500 - client_module - INFO - client_tab:1888 - Created additional requirements columns +2025-06-27 08:37:29,501 - client_module - INFO - client_tab:1900 - Rendered additional client requirements tooltip +2025-06-27 08:37:29,501 - client_module - INFO - client_tab:1915 - Additional requirements text area rendered with 0 characters +2025-06-27 08:37:29,501 - client_module - INFO - client_tab:1931 - Additional requirements provided status: False +2025-06-27 08:37:29,501 - client_module - INFO - client_tab:1945 - Rendered additional specifications tooltip +2025-06-27 08:37:29,501 - client_module - INFO - client_tab:1963 - Using default additional specs items +2025-06-27 08:37:32,163 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:32,164 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:32,164 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:32,165 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:32,165 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:32,165 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:32,166 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:32,166 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:32,166 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:32,167 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:32,167 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:32,168 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:32,168 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:32,168 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:32,169 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:37:32,170 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:32,171 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:37:32,171 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:32,173 - client_module - DEBUG - client_tab:968 - Creating file upload and enterprise details section +2025-06-27 08:37:32,174 - client_module - DEBUG - client_tab:973 - Processing file upload section +2025-06-27 08:37:32,175 - client_module - DEBUG - client_tab:1145 - Processing enterprise details section +2025-06-27 08:37:32,176 - client_module - DEBUG - client_tab:1183 - Creating client requirements and pain points section +2025-06-27 08:37:32,177 - client_module - DEBUG - client_tab:1188 - Processing client requirements section +2025-06-27 08:37:32,177 - client_module - DEBUG - client_tab:1212 - Updated client requirements content +2025-06-27 08:37:32,178 - client_module - DEBUG - client_tab:1217 - Client requirements provided: False +2025-06-27 08:37:32,178 - client_module - INFO - client_tab:1232 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:37:32,178 - client_module - INFO - client_tab:1256 - Using dummy pain points data as fallback +2025-06-27 08:37:32,179 - client_module - DEBUG - client_tab:1266 - Rendering 3 pain point items +2025-06-27 08:37:32,179 - client_module - DEBUG - client_tab:1271 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:37:32,179 - client_module - DEBUG - client_tab:1276 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:37:32,180 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:37:32,180 - client_module - DEBUG - client_tab:1271 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:37:32,180 - client_module - DEBUG - client_tab:1276 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:37:32,181 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:37:32,181 - client_module - DEBUG - client_tab:1271 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:37:32,181 - client_module - DEBUG - client_tab:1276 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:37:32,182 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:37:32,182 - client_module - INFO - client_tab:1506 - Rendered SPOC name tooltip +2025-06-27 08:37:32,183 - client_module - INFO - client_tab:1519 - SPOC name input rendered with value: +2025-06-27 08:37:32,183 - client_module - INFO - client_tab:1551 - SPOC name provided status: False +2025-06-27 08:37:32,183 - client_module - INFO - client_tab:1563 - Rendered LinkedIn profile tooltip +2025-06-27 08:37:32,184 - client_module - INFO - client_tab:1619 - LinkedIn profile selector in disabled state +2025-06-27 08:37:32,184 - client_module - INFO - client_tab:1700 - Created roles and priorities columns +2025-06-27 08:37:32,184 - client_module - INFO - client_tab:1712 - Rendered SPOC role tooltip +2025-06-27 08:37:32,184 - client_module - INFO - client_tab:1722 - Retrieved 10 default roles +2025-06-27 08:37:32,185 - client_module - INFO - client_tab:1781 - Role selector rendered with selection: Select a role... +2025-06-27 08:37:32,185 - client_module - INFO - client_tab:1796 - Cleared selected target role +2025-06-27 08:37:32,185 - client_module - INFO - client_tab:1809 - Rendered business priorities tooltip +2025-06-27 08:37:32,185 - client_module - INFO - client_tab:1838 - Using 3 business priorities +2025-06-27 08:37:32,186 - client_module - INFO - client_tab:1888 - Created additional requirements columns +2025-06-27 08:37:32,186 - client_module - INFO - client_tab:1900 - Rendered additional client requirements tooltip +2025-06-27 08:37:32,187 - client_module - INFO - client_tab:1915 - Additional requirements text area rendered with 0 characters +2025-06-27 08:37:32,187 - client_module - INFO - client_tab:1931 - Additional requirements provided status: False +2025-06-27 08:37:32,187 - client_module - INFO - client_tab:1945 - Rendered additional specifications tooltip +2025-06-27 08:37:32,187 - client_module - INFO - client_tab:1963 - Using default additional specs items +2025-06-27 08:37:42,398 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:42,399 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:42,399 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:42,399 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:42,399 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:42,400 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:42,400 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:42,400 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:42,400 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:42,401 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:42,401 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:42,402 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:42,402 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:42,403 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:42,404 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:37:42,404 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:42,405 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:37:42,406 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:42,406 - client_module - INFO - client_tab:872 - Updated website URL: https://sfdskfhdsf.com +2025-06-27 08:37:42,408 - client_module - DEBUG - client_tab:968 - Creating file upload and enterprise details section +2025-06-27 08:37:42,409 - client_module - DEBUG - client_tab:973 - Processing file upload section +2025-06-27 08:37:42,410 - client_module - DEBUG - client_tab:1145 - Processing enterprise details section +2025-06-27 08:37:42,410 - client_module - DEBUG - client_tab:1183 - Creating client requirements and pain points section +2025-06-27 08:37:42,411 - client_module - DEBUG - client_tab:1188 - Processing client requirements section +2025-06-27 08:37:42,411 - client_module - DEBUG - client_tab:1212 - Updated client requirements content +2025-06-27 08:37:42,411 - client_module - DEBUG - client_tab:1217 - Client requirements provided: False +2025-06-27 08:37:42,411 - client_module - INFO - client_tab:1232 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:37:42,411 - client_module - INFO - client_tab:1256 - Using dummy pain points data as fallback +2025-06-27 08:37:42,412 - client_module - DEBUG - client_tab:1266 - Rendering 3 pain point items +2025-06-27 08:37:42,412 - client_module - DEBUG - client_tab:1271 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:37:42,412 - client_module - DEBUG - client_tab:1276 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:37:42,412 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:37:42,412 - client_module - DEBUG - client_tab:1271 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:37:42,413 - client_module - DEBUG - client_tab:1276 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:37:42,413 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:37:42,413 - client_module - DEBUG - client_tab:1271 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:37:42,413 - client_module - DEBUG - client_tab:1276 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:37:42,414 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:37:42,414 - client_module - INFO - client_tab:1506 - Rendered SPOC name tooltip +2025-06-27 08:37:42,415 - client_module - INFO - client_tab:1519 - SPOC name input rendered with value: +2025-06-27 08:37:42,416 - client_module - INFO - client_tab:1551 - SPOC name provided status: False +2025-06-27 08:37:42,416 - client_module - INFO - client_tab:1563 - Rendered LinkedIn profile tooltip +2025-06-27 08:37:42,417 - client_module - INFO - client_tab:1619 - LinkedIn profile selector in disabled state +2025-06-27 08:37:42,417 - client_module - INFO - client_tab:1700 - Created roles and priorities columns +2025-06-27 08:37:42,418 - client_module - INFO - client_tab:1712 - Rendered SPOC role tooltip +2025-06-27 08:37:42,418 - client_module - INFO - client_tab:1722 - Retrieved 10 default roles +2025-06-27 08:37:42,418 - client_module - INFO - client_tab:1781 - Role selector rendered with selection: Select a role... +2025-06-27 08:37:42,419 - client_module - INFO - client_tab:1796 - Cleared selected target role +2025-06-27 08:37:42,420 - client_module - INFO - client_tab:1809 - Rendered business priorities tooltip +2025-06-27 08:37:42,421 - client_module - INFO - client_tab:1838 - Using 3 business priorities +2025-06-27 08:37:42,422 - client_module - INFO - client_tab:1888 - Created additional requirements columns +2025-06-27 08:37:42,423 - client_module - INFO - client_tab:1900 - Rendered additional client requirements tooltip +2025-06-27 08:37:42,423 - client_module - INFO - client_tab:1915 - Additional requirements text area rendered with 0 characters +2025-06-27 08:37:42,423 - client_module - INFO - client_tab:1931 - Additional requirements provided status: False +2025-06-27 08:37:42,423 - client_module - INFO - client_tab:1945 - Rendered additional specifications tooltip +2025-06-27 08:37:42,423 - client_module - INFO - client_tab:1963 - Using default additional specs items +2025-06-27 08:37:46,354 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:46,354 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:46,354 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:46,354 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:46,354 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:46,354 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:46,355 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:46,355 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:46,355 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:46,355 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:46,355 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:46,356 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:46,356 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:46,356 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:46,357 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:37:46,357 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:46,358 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:37:46,359 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:46,360 - client_module - INFO - client_tab:904 - Scrape button clicked for URL: https://sfdskfhdsf.com +2025-06-27 08:37:46,527 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:46,528 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:46,528 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:46,528 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:46,528 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:46,528 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:46,528 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:46,528 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:46,528 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:46,529 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:46,529 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:46,529 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:46,529 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:46,530 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:46,530 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:37:46,530 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:46,531 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:37:46,531 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:46,531 - client_module - INFO - client_tab:931 - Starting website scraping for: https://sfdskfhdsf.com +2025-06-27 08:37:53,278 - client_module - INFO - client_tab:935 - Successfully scraped website details, length: 0 +2025-06-27 08:37:53,443 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:53,445 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:53,445 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:53,445 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:53,445 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:53,445 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:53,446 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:37:53,446 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:37:53,446 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:37:53,446 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:37:53,446 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:37:53,446 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:37:53,446 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:37:53,447 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:37:53,447 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:37:53,447 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:37:53,448 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:37:53,448 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:37:53,449 - client_module - DEBUG - client_tab:968 - Creating file upload and enterprise details section +2025-06-27 08:37:53,449 - client_module - DEBUG - client_tab:973 - Processing file upload section +2025-06-27 08:37:53,450 - client_module - DEBUG - client_tab:1145 - Processing enterprise details section +2025-06-27 08:37:53,450 - client_module - DEBUG - client_tab:1183 - Creating client requirements and pain points section +2025-06-27 08:37:53,450 - client_module - DEBUG - client_tab:1188 - Processing client requirements section +2025-06-27 08:37:53,451 - client_module - DEBUG - client_tab:1212 - Updated client requirements content +2025-06-27 08:37:53,451 - client_module - DEBUG - client_tab:1217 - Client requirements provided: False +2025-06-27 08:37:53,451 - client_module - INFO - client_tab:1232 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:37:53,451 - client_module - INFO - client_tab:1256 - Using dummy pain points data as fallback +2025-06-27 08:37:53,452 - client_module - DEBUG - client_tab:1266 - Rendering 3 pain point items +2025-06-27 08:37:53,452 - client_module - DEBUG - client_tab:1271 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:37:53,452 - client_module - DEBUG - client_tab:1276 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:37:53,452 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:37:53,452 - client_module - DEBUG - client_tab:1271 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:37:53,452 - client_module - DEBUG - client_tab:1276 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:37:53,453 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:37:53,453 - client_module - DEBUG - client_tab:1271 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:37:53,453 - client_module - DEBUG - client_tab:1276 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:37:53,454 - client_module - DEBUG - client_tab:1475 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:37:53,454 - client_module - INFO - client_tab:1506 - Rendered SPOC name tooltip +2025-06-27 08:37:53,454 - client_module - INFO - client_tab:1519 - SPOC name input rendered with value: +2025-06-27 08:37:53,455 - client_module - INFO - client_tab:1551 - SPOC name provided status: False +2025-06-27 08:37:53,455 - client_module - INFO - client_tab:1563 - Rendered LinkedIn profile tooltip +2025-06-27 08:37:53,455 - client_module - INFO - client_tab:1619 - LinkedIn profile selector in disabled state +2025-06-27 08:37:53,456 - client_module - INFO - client_tab:1700 - Created roles and priorities columns +2025-06-27 08:37:53,456 - client_module - INFO - client_tab:1712 - Rendered SPOC role tooltip +2025-06-27 08:37:53,456 - client_module - INFO - client_tab:1722 - Retrieved 10 default roles +2025-06-27 08:37:53,456 - client_module - INFO - client_tab:1781 - Role selector rendered with selection: Select a role... +2025-06-27 08:37:53,456 - client_module - INFO - client_tab:1796 - Cleared selected target role +2025-06-27 08:37:53,457 - client_module - INFO - client_tab:1809 - Rendered business priorities tooltip +2025-06-27 08:37:53,457 - client_module - INFO - client_tab:1838 - Using 3 business priorities +2025-06-27 08:37:53,457 - client_module - INFO - client_tab:1888 - Created additional requirements columns +2025-06-27 08:37:53,458 - client_module - INFO - client_tab:1900 - Rendered additional client requirements tooltip +2025-06-27 08:37:53,458 - client_module - INFO - client_tab:1915 - Additional requirements text area rendered with 0 characters +2025-06-27 08:37:53,458 - client_module - INFO - client_tab:1931 - Additional requirements provided status: False +2025-06-27 08:37:53,458 - client_module - INFO - client_tab:1945 - Rendered additional specifications tooltip +2025-06-27 08:37:53,459 - client_module - INFO - client_tab:1963 - Using default additional specs items +2025-06-27 08:42:17,414 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:17,415 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:17,415 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:17,415 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:17,415 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:17,415 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:17,415 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:17,416 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:17,416 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:17,416 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:42:17,417 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:42:17,417 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:42:17,417 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:42:17,418 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:42:17,418 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:42:17,418 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:42:17,419 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: True +2025-06-27 08:42:17,419 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:42:17,419 - client_module - DEBUG - client_tab:840 - Client name provided: False +2025-06-27 08:42:17,419 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:42:17,420 - client_module - DEBUG - client_tab:970 - Creating file upload and enterprise details section +2025-06-27 08:42:17,420 - client_module - DEBUG - client_tab:975 - Processing file upload section +2025-06-27 08:42:17,421 - client_module - DEBUG - client_tab:1147 - Processing enterprise details section +2025-06-27 08:42:17,421 - client_module - DEBUG - client_tab:1185 - Creating client requirements and pain points section +2025-06-27 08:42:17,421 - client_module - DEBUG - client_tab:1190 - Processing client requirements section +2025-06-27 08:42:17,422 - client_module - DEBUG - client_tab:1219 - Client requirements provided: False +2025-06-27 08:42:17,422 - client_module - INFO - client_tab:1234 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:42:17,422 - client_module - INFO - client_tab:1258 - Using dummy pain points data as fallback +2025-06-27 08:42:17,422 - client_module - DEBUG - client_tab:1268 - Rendering 3 pain point items +2025-06-27 08:42:17,422 - client_module - DEBUG - client_tab:1273 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:42:17,422 - client_module - DEBUG - client_tab:1278 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:42:17,423 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:42:17,423 - client_module - DEBUG - client_tab:1273 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:42:17,423 - client_module - DEBUG - client_tab:1278 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:42:17,424 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:42:17,424 - client_module - DEBUG - client_tab:1273 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:42:17,424 - client_module - DEBUG - client_tab:1278 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:42:17,425 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:42:17,425 - client_module - INFO - client_tab:1508 - Rendered SPOC name tooltip +2025-06-27 08:42:17,425 - client_module - INFO - client_tab:1521 - SPOC name input rendered with value: +2025-06-27 08:42:17,426 - client_module - INFO - client_tab:1553 - SPOC name provided status: False +2025-06-27 08:42:17,426 - client_module - INFO - client_tab:1565 - Rendered LinkedIn profile tooltip +2025-06-27 08:42:17,427 - client_module - INFO - client_tab:1621 - LinkedIn profile selector in disabled state +2025-06-27 08:42:17,429 - client_module - INFO - client_tab:1702 - Created roles and priorities columns +2025-06-27 08:42:17,429 - client_module - INFO - client_tab:1714 - Rendered SPOC role tooltip +2025-06-27 08:42:17,430 - client_module - INFO - client_tab:1724 - Retrieved 10 default roles +2025-06-27 08:42:17,431 - client_module - INFO - client_tab:1783 - Role selector rendered with selection: Select a role... +2025-06-27 08:42:17,431 - client_module - INFO - client_tab:1798 - Cleared selected target role +2025-06-27 08:42:17,431 - client_module - INFO - client_tab:1811 - Rendered business priorities tooltip +2025-06-27 08:42:17,432 - client_module - INFO - client_tab:1840 - Using 3 business priorities +2025-06-27 08:42:17,433 - client_module - INFO - client_tab:1890 - Created additional requirements columns +2025-06-27 08:42:17,434 - client_module - INFO - client_tab:1902 - Rendered additional client requirements tooltip +2025-06-27 08:42:17,434 - client_module - INFO - client_tab:1917 - Additional requirements text area rendered with 0 characters +2025-06-27 08:42:17,435 - client_module - INFO - client_tab:1933 - Additional requirements provided status: False +2025-06-27 08:42:17,435 - client_module - INFO - client_tab:1947 - Rendered additional specifications tooltip +2025-06-27 08:42:17,435 - client_module - INFO - client_tab:1965 - Using default additional specs items +2025-06-27 08:42:25,013 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:25,013 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:25,013 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:25,014 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:25,014 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:25,014 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:25,014 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:25,014 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:25,014 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:25,015 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:42:25,015 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:42:25,016 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:42:25,016 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:42:25,017 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:42:25,017 - client_module - INFO - client_tab:760 - Updated enterprise name: ngfjf +2025-06-27 08:42:25,017 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:42:25,018 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:42:25,018 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:42:25,018 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:42:25,019 - client_module - DEBUG - client_tab:970 - Creating file upload and enterprise details section +2025-06-27 08:42:25,019 - client_module - DEBUG - client_tab:975 - Processing file upload section +2025-06-27 08:42:25,019 - client_module - DEBUG - client_tab:1147 - Processing enterprise details section +2025-06-27 08:42:25,020 - client_module - DEBUG - client_tab:1185 - Creating client requirements and pain points section +2025-06-27 08:42:25,021 - client_module - DEBUG - client_tab:1190 - Processing client requirements section +2025-06-27 08:42:25,021 - client_module - DEBUG - client_tab:1214 - Updated client requirements content +2025-06-27 08:42:25,021 - client_module - DEBUG - client_tab:1219 - Client requirements provided: False +2025-06-27 08:42:25,022 - client_module - INFO - client_tab:1234 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:42:25,022 - client_module - INFO - client_tab:1258 - Using dummy pain points data as fallback +2025-06-27 08:42:25,022 - client_module - DEBUG - client_tab:1268 - Rendering 3 pain point items +2025-06-27 08:42:25,022 - client_module - DEBUG - client_tab:1273 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:42:25,022 - client_module - DEBUG - client_tab:1278 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:42:25,024 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:42:25,024 - client_module - DEBUG - client_tab:1273 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:42:25,025 - client_module - DEBUG - client_tab:1278 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:42:25,026 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:42:25,026 - client_module - DEBUG - client_tab:1273 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:42:25,026 - client_module - DEBUG - client_tab:1278 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:42:25,027 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:42:25,027 - client_module - INFO - client_tab:1508 - Rendered SPOC name tooltip +2025-06-27 08:42:25,028 - client_module - INFO - client_tab:1521 - SPOC name input rendered with value: +2025-06-27 08:42:25,028 - client_module - INFO - client_tab:1553 - SPOC name provided status: False +2025-06-27 08:42:25,029 - client_module - INFO - client_tab:1565 - Rendered LinkedIn profile tooltip +2025-06-27 08:42:25,030 - client_module - INFO - client_tab:1621 - LinkedIn profile selector in disabled state +2025-06-27 08:42:25,030 - client_module - INFO - client_tab:1702 - Created roles and priorities columns +2025-06-27 08:42:25,030 - client_module - INFO - client_tab:1714 - Rendered SPOC role tooltip +2025-06-27 08:42:25,030 - client_module - INFO - client_tab:1724 - Retrieved 10 default roles +2025-06-27 08:42:25,031 - client_module - INFO - client_tab:1783 - Role selector rendered with selection: Select a role... +2025-06-27 08:42:25,031 - client_module - INFO - client_tab:1798 - Cleared selected target role +2025-06-27 08:42:25,031 - client_module - INFO - client_tab:1811 - Rendered business priorities tooltip +2025-06-27 08:42:25,031 - client_module - INFO - client_tab:1840 - Using 3 business priorities +2025-06-27 08:42:25,032 - client_module - INFO - client_tab:1890 - Created additional requirements columns +2025-06-27 08:42:25,032 - client_module - INFO - client_tab:1902 - Rendered additional client requirements tooltip +2025-06-27 08:42:25,032 - client_module - INFO - client_tab:1917 - Additional requirements text area rendered with 0 characters +2025-06-27 08:42:25,032 - client_module - INFO - client_tab:1933 - Additional requirements provided status: False +2025-06-27 08:42:25,032 - client_module - INFO - client_tab:1947 - Rendered additional specifications tooltip +2025-06-27 08:42:25,032 - client_module - INFO - client_tab:1965 - Using default additional specs items +2025-06-27 08:42:35,809 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:35,809 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:35,809 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:35,810 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:35,811 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:35,811 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:35,811 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:35,811 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:35,811 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:35,812 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:42:35,813 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:42:35,813 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:42:35,813 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:42:35,814 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:42:35,815 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:42:35,816 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:42:35,817 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:42:35,817 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:42:35,818 - client_module - INFO - client_tab:872 - Updated website URL: https://gsdfdsfds.com +2025-06-27 08:42:35,819 - client_module - DEBUG - client_tab:970 - Creating file upload and enterprise details section +2025-06-27 08:42:35,820 - client_module - DEBUG - client_tab:975 - Processing file upload section +2025-06-27 08:42:35,820 - client_module - DEBUG - client_tab:1147 - Processing enterprise details section +2025-06-27 08:42:35,821 - client_module - DEBUG - client_tab:1185 - Creating client requirements and pain points section +2025-06-27 08:42:35,821 - client_module - DEBUG - client_tab:1190 - Processing client requirements section +2025-06-27 08:42:35,822 - client_module - DEBUG - client_tab:1214 - Updated client requirements content +2025-06-27 08:42:35,822 - client_module - DEBUG - client_tab:1219 - Client requirements provided: False +2025-06-27 08:42:35,822 - client_module - INFO - client_tab:1234 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:42:35,822 - client_module - INFO - client_tab:1258 - Using dummy pain points data as fallback +2025-06-27 08:42:35,822 - client_module - DEBUG - client_tab:1268 - Rendering 3 pain point items +2025-06-27 08:42:35,822 - client_module - DEBUG - client_tab:1273 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:42:35,822 - client_module - DEBUG - client_tab:1278 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:42:35,823 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:42:35,823 - client_module - DEBUG - client_tab:1273 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:42:35,823 - client_module - DEBUG - client_tab:1278 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:42:35,824 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:42:35,824 - client_module - DEBUG - client_tab:1273 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:42:35,824 - client_module - DEBUG - client_tab:1278 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:42:35,825 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:42:35,825 - client_module - INFO - client_tab:1508 - Rendered SPOC name tooltip +2025-06-27 08:42:35,826 - client_module - INFO - client_tab:1521 - SPOC name input rendered with value: +2025-06-27 08:42:35,826 - client_module - INFO - client_tab:1553 - SPOC name provided status: False +2025-06-27 08:42:35,826 - client_module - INFO - client_tab:1565 - Rendered LinkedIn profile tooltip +2025-06-27 08:42:35,827 - client_module - INFO - client_tab:1621 - LinkedIn profile selector in disabled state +2025-06-27 08:42:35,827 - client_module - INFO - client_tab:1702 - Created roles and priorities columns +2025-06-27 08:42:35,827 - client_module - INFO - client_tab:1714 - Rendered SPOC role tooltip +2025-06-27 08:42:35,827 - client_module - INFO - client_tab:1724 - Retrieved 10 default roles +2025-06-27 08:42:35,828 - client_module - INFO - client_tab:1783 - Role selector rendered with selection: Select a role... +2025-06-27 08:42:35,828 - client_module - INFO - client_tab:1798 - Cleared selected target role +2025-06-27 08:42:35,829 - client_module - INFO - client_tab:1811 - Rendered business priorities tooltip +2025-06-27 08:42:35,829 - client_module - INFO - client_tab:1840 - Using 3 business priorities +2025-06-27 08:42:35,830 - client_module - INFO - client_tab:1890 - Created additional requirements columns +2025-06-27 08:42:35,831 - client_module - INFO - client_tab:1902 - Rendered additional client requirements tooltip +2025-06-27 08:42:35,831 - client_module - INFO - client_tab:1917 - Additional requirements text area rendered with 0 characters +2025-06-27 08:42:35,832 - client_module - INFO - client_tab:1933 - Additional requirements provided status: False +2025-06-27 08:42:35,834 - client_module - INFO - client_tab:1947 - Rendered additional specifications tooltip +2025-06-27 08:42:35,834 - client_module - INFO - client_tab:1965 - Using default additional specs items +2025-06-27 08:42:38,044 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:38,045 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:38,045 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:38,045 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:38,046 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:38,046 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:38,047 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:38,047 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:38,047 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:38,048 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:42:38,048 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:42:38,049 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:42:38,049 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:42:38,049 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:42:38,050 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:42:38,051 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:42:38,052 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:42:38,052 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:42:38,053 - client_module - INFO - client_tab:904 - Scrape button clicked for URL: https://gsdfdsfds.com +2025-06-27 08:42:38,226 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:38,227 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:38,227 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:38,227 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:38,227 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:38,228 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:38,228 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:38,228 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:38,228 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:38,229 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:42:38,230 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:42:38,230 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:42:38,230 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:42:38,231 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:42:38,232 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:42:38,232 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:42:38,234 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:42:38,234 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:42:38,235 - client_module - INFO - client_tab:931 - Starting website scraping for: https://gsdfdsfds.com +2025-06-27 08:42:39,137 - client_module - WARNING - client_tab:938 - Website scraping returned empty data for: https://gsdfdsfds.com +2025-06-27 08:42:39,303 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:39,304 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:39,305 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:39,305 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:39,305 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:39,305 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:39,305 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:42:39,305 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:42:39,305 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:42:39,306 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:42:39,306 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:42:39,307 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:42:39,307 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:42:39,307 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:42:39,308 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:42:39,308 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:42:39,309 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:42:39,309 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:42:39,309 - client_module - DEBUG - client_tab:970 - Creating file upload and enterprise details section +2025-06-27 08:42:39,310 - client_module - DEBUG - client_tab:975 - Processing file upload section +2025-06-27 08:42:39,310 - client_module - DEBUG - client_tab:1147 - Processing enterprise details section +2025-06-27 08:42:39,310 - client_module - DEBUG - client_tab:1185 - Creating client requirements and pain points section +2025-06-27 08:42:39,311 - client_module - DEBUG - client_tab:1190 - Processing client requirements section +2025-06-27 08:42:39,311 - client_module - DEBUG - client_tab:1214 - Updated client requirements content +2025-06-27 08:42:39,311 - client_module - DEBUG - client_tab:1219 - Client requirements provided: False +2025-06-27 08:42:39,311 - client_module - INFO - client_tab:1234 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:42:39,311 - client_module - INFO - client_tab:1258 - Using dummy pain points data as fallback +2025-06-27 08:42:39,311 - client_module - DEBUG - client_tab:1268 - Rendering 3 pain point items +2025-06-27 08:42:39,311 - client_module - DEBUG - client_tab:1273 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:42:39,311 - client_module - DEBUG - client_tab:1278 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:42:39,312 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:42:39,312 - client_module - DEBUG - client_tab:1273 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:42:39,312 - client_module - DEBUG - client_tab:1278 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:42:39,313 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:42:39,313 - client_module - DEBUG - client_tab:1273 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:42:39,313 - client_module - DEBUG - client_tab:1278 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:42:39,313 - client_module - DEBUG - client_tab:1477 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:42:39,314 - client_module - INFO - client_tab:1508 - Rendered SPOC name tooltip +2025-06-27 08:42:39,314 - client_module - INFO - client_tab:1521 - SPOC name input rendered with value: +2025-06-27 08:42:39,314 - client_module - INFO - client_tab:1553 - SPOC name provided status: False +2025-06-27 08:42:39,314 - client_module - INFO - client_tab:1565 - Rendered LinkedIn profile tooltip +2025-06-27 08:42:39,314 - client_module - INFO - client_tab:1621 - LinkedIn profile selector in disabled state +2025-06-27 08:42:39,315 - client_module - INFO - client_tab:1702 - Created roles and priorities columns +2025-06-27 08:42:39,315 - client_module - INFO - client_tab:1714 - Rendered SPOC role tooltip +2025-06-27 08:42:39,315 - client_module - INFO - client_tab:1724 - Retrieved 10 default roles +2025-06-27 08:42:39,315 - client_module - INFO - client_tab:1783 - Role selector rendered with selection: Select a role... +2025-06-27 08:42:39,315 - client_module - INFO - client_tab:1798 - Cleared selected target role +2025-06-27 08:42:39,315 - client_module - INFO - client_tab:1811 - Rendered business priorities tooltip +2025-06-27 08:42:39,315 - client_module - INFO - client_tab:1840 - Using 3 business priorities +2025-06-27 08:42:39,316 - client_module - INFO - client_tab:1890 - Created additional requirements columns +2025-06-27 08:42:39,316 - client_module - INFO - client_tab:1902 - Rendered additional client requirements tooltip +2025-06-27 08:42:39,317 - client_module - INFO - client_tab:1917 - Additional requirements text area rendered with 0 characters +2025-06-27 08:42:39,317 - client_module - INFO - client_tab:1933 - Additional requirements provided status: False +2025-06-27 08:42:39,317 - client_module - INFO - client_tab:1947 - Rendered additional specifications tooltip +2025-06-27 08:42:39,317 - client_module - INFO - client_tab:1965 - Using default additional specs items +2025-06-27 08:43:12,459 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:12,460 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:12,460 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:12,461 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:12,461 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:12,461 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:12,461 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:12,462 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:12,462 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:12,463 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:12,463 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:12,463 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:43:12,464 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:43:12,464 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:12,464 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:12,465 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:12,466 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: True +2025-06-27 08:43:12,466 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:12,467 - client_module - DEBUG - client_tab:840 - Client name provided: False +2025-06-27 08:43:12,468 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:12,469 - client_module - DEBUG - client_tab:971 - Creating file upload and enterprise details section +2025-06-27 08:43:12,469 - client_module - DEBUG - client_tab:976 - Processing file upload section +2025-06-27 08:43:12,470 - client_module - DEBUG - client_tab:1148 - Processing enterprise details section +2025-06-27 08:43:12,471 - client_module - DEBUG - client_tab:1186 - Creating client requirements and pain points section +2025-06-27 08:43:12,472 - client_module - DEBUG - client_tab:1191 - Processing client requirements section +2025-06-27 08:43:12,473 - client_module - DEBUG - client_tab:1220 - Client requirements provided: False +2025-06-27 08:43:12,473 - client_module - INFO - client_tab:1235 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:43:12,473 - client_module - INFO - client_tab:1259 - Using dummy pain points data as fallback +2025-06-27 08:43:12,474 - client_module - DEBUG - client_tab:1269 - Rendering 3 pain point items +2025-06-27 08:43:12,474 - client_module - DEBUG - client_tab:1274 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:43:12,474 - client_module - DEBUG - client_tab:1279 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:43:12,476 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:43:12,476 - client_module - DEBUG - client_tab:1274 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:43:12,476 - client_module - DEBUG - client_tab:1279 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:43:12,478 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:43:12,478 - client_module - DEBUG - client_tab:1274 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:43:12,479 - client_module - DEBUG - client_tab:1279 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:43:12,480 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:43:12,481 - client_module - INFO - client_tab:1509 - Rendered SPOC name tooltip +2025-06-27 08:43:12,482 - client_module - INFO - client_tab:1522 - SPOC name input rendered with value: +2025-06-27 08:43:12,482 - client_module - INFO - client_tab:1554 - SPOC name provided status: False +2025-06-27 08:43:12,483 - client_module - INFO - client_tab:1566 - Rendered LinkedIn profile tooltip +2025-06-27 08:43:12,484 - client_module - INFO - client_tab:1622 - LinkedIn profile selector in disabled state +2025-06-27 08:43:12,485 - client_module - INFO - client_tab:1703 - Created roles and priorities columns +2025-06-27 08:43:12,485 - client_module - INFO - client_tab:1715 - Rendered SPOC role tooltip +2025-06-27 08:43:12,486 - client_module - INFO - client_tab:1725 - Retrieved 10 default roles +2025-06-27 08:43:12,486 - client_module - INFO - client_tab:1784 - Role selector rendered with selection: Select a role... +2025-06-27 08:43:12,487 - client_module - INFO - client_tab:1799 - Cleared selected target role +2025-06-27 08:43:12,487 - client_module - INFO - client_tab:1812 - Rendered business priorities tooltip +2025-06-27 08:43:12,488 - client_module - INFO - client_tab:1841 - Using 3 business priorities +2025-06-27 08:43:12,489 - client_module - INFO - client_tab:1891 - Created additional requirements columns +2025-06-27 08:43:12,490 - client_module - INFO - client_tab:1903 - Rendered additional client requirements tooltip +2025-06-27 08:43:12,490 - client_module - INFO - client_tab:1918 - Additional requirements text area rendered with 0 characters +2025-06-27 08:43:12,490 - client_module - INFO - client_tab:1934 - Additional requirements provided status: False +2025-06-27 08:43:12,491 - client_module - INFO - client_tab:1948 - Rendered additional specifications tooltip +2025-06-27 08:43:12,491 - client_module - INFO - client_tab:1966 - Using default additional specs items +2025-06-27 08:43:14,751 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:14,751 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:14,752 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:14,752 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:14,752 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:14,752 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:14,753 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:14,753 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:14,753 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:14,753 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:14,753 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:14,754 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:14,754 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:14,754 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:14,755 - client_module - INFO - client_tab:760 - Updated enterprise name: ghhfgh +2025-06-27 08:43:14,755 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:14,756 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:14,756 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:14,756 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:14,757 - client_module - DEBUG - client_tab:971 - Creating file upload and enterprise details section +2025-06-27 08:43:14,758 - client_module - DEBUG - client_tab:976 - Processing file upload section +2025-06-27 08:43:14,758 - client_module - DEBUG - client_tab:1148 - Processing enterprise details section +2025-06-27 08:43:14,759 - client_module - DEBUG - client_tab:1186 - Creating client requirements and pain points section +2025-06-27 08:43:14,760 - client_module - DEBUG - client_tab:1191 - Processing client requirements section +2025-06-27 08:43:14,760 - client_module - DEBUG - client_tab:1215 - Updated client requirements content +2025-06-27 08:43:14,760 - client_module - DEBUG - client_tab:1220 - Client requirements provided: False +2025-06-27 08:43:14,760 - client_module - INFO - client_tab:1235 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:43:14,760 - client_module - INFO - client_tab:1259 - Using dummy pain points data as fallback +2025-06-27 08:43:14,760 - client_module - DEBUG - client_tab:1269 - Rendering 3 pain point items +2025-06-27 08:43:14,761 - client_module - DEBUG - client_tab:1274 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:43:14,761 - client_module - DEBUG - client_tab:1279 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:43:14,761 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:43:14,761 - client_module - DEBUG - client_tab:1274 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:43:14,761 - client_module - DEBUG - client_tab:1279 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:43:14,762 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:43:14,762 - client_module - DEBUG - client_tab:1274 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:43:14,762 - client_module - DEBUG - client_tab:1279 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:43:14,763 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:43:14,763 - client_module - INFO - client_tab:1509 - Rendered SPOC name tooltip +2025-06-27 08:43:14,764 - client_module - INFO - client_tab:1522 - SPOC name input rendered with value: +2025-06-27 08:43:14,764 - client_module - INFO - client_tab:1554 - SPOC name provided status: False +2025-06-27 08:43:14,765 - client_module - INFO - client_tab:1566 - Rendered LinkedIn profile tooltip +2025-06-27 08:43:14,766 - client_module - INFO - client_tab:1622 - LinkedIn profile selector in disabled state +2025-06-27 08:43:14,767 - client_module - INFO - client_tab:1703 - Created roles and priorities columns +2025-06-27 08:43:14,768 - client_module - INFO - client_tab:1715 - Rendered SPOC role tooltip +2025-06-27 08:43:14,769 - client_module - INFO - client_tab:1725 - Retrieved 10 default roles +2025-06-27 08:43:14,769 - client_module - INFO - client_tab:1784 - Role selector rendered with selection: Select a role... +2025-06-27 08:43:14,770 - client_module - INFO - client_tab:1799 - Cleared selected target role +2025-06-27 08:43:14,770 - client_module - INFO - client_tab:1812 - Rendered business priorities tooltip +2025-06-27 08:43:14,770 - client_module - INFO - client_tab:1841 - Using 3 business priorities +2025-06-27 08:43:14,771 - client_module - INFO - client_tab:1891 - Created additional requirements columns +2025-06-27 08:43:14,771 - client_module - INFO - client_tab:1903 - Rendered additional client requirements tooltip +2025-06-27 08:43:14,772 - client_module - INFO - client_tab:1918 - Additional requirements text area rendered with 0 characters +2025-06-27 08:43:14,772 - client_module - INFO - client_tab:1934 - Additional requirements provided status: False +2025-06-27 08:43:14,772 - client_module - INFO - client_tab:1948 - Rendered additional specifications tooltip +2025-06-27 08:43:14,772 - client_module - INFO - client_tab:1966 - Using default additional specs items +2025-06-27 08:43:22,114 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:22,114 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:22,114 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:22,115 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:22,115 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:22,115 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:22,116 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:22,116 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:22,116 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:22,117 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:22,117 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:22,117 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:22,118 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:22,118 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:22,119 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:22,120 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:22,121 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:22,121 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:22,121 - client_module - INFO - client_tab:872 - Updated website URL: https://gsfjdfdjf.com +2025-06-27 08:43:22,122 - client_module - DEBUG - client_tab:971 - Creating file upload and enterprise details section +2025-06-27 08:43:22,123 - client_module - DEBUG - client_tab:976 - Processing file upload section +2025-06-27 08:43:22,124 - client_module - DEBUG - client_tab:1148 - Processing enterprise details section +2025-06-27 08:43:22,125 - client_module - DEBUG - client_tab:1186 - Creating client requirements and pain points section +2025-06-27 08:43:22,126 - client_module - DEBUG - client_tab:1191 - Processing client requirements section +2025-06-27 08:43:22,127 - client_module - DEBUG - client_tab:1215 - Updated client requirements content +2025-06-27 08:43:22,127 - client_module - DEBUG - client_tab:1220 - Client requirements provided: False +2025-06-27 08:43:22,127 - client_module - INFO - client_tab:1235 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:43:22,128 - client_module - INFO - client_tab:1259 - Using dummy pain points data as fallback +2025-06-27 08:43:22,128 - client_module - DEBUG - client_tab:1269 - Rendering 3 pain point items +2025-06-27 08:43:22,128 - client_module - DEBUG - client_tab:1274 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:43:22,128 - client_module - DEBUG - client_tab:1279 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:43:22,129 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:43:22,129 - client_module - DEBUG - client_tab:1274 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:43:22,129 - client_module - DEBUG - client_tab:1279 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:43:22,130 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:43:22,130 - client_module - DEBUG - client_tab:1274 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:43:22,130 - client_module - DEBUG - client_tab:1279 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:43:22,131 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:43:22,131 - client_module - INFO - client_tab:1509 - Rendered SPOC name tooltip +2025-06-27 08:43:22,131 - client_module - INFO - client_tab:1522 - SPOC name input rendered with value: +2025-06-27 08:43:22,131 - client_module - INFO - client_tab:1554 - SPOC name provided status: False +2025-06-27 08:43:22,131 - client_module - INFO - client_tab:1566 - Rendered LinkedIn profile tooltip +2025-06-27 08:43:22,132 - client_module - INFO - client_tab:1622 - LinkedIn profile selector in disabled state +2025-06-27 08:43:22,132 - client_module - INFO - client_tab:1703 - Created roles and priorities columns +2025-06-27 08:43:22,132 - client_module - INFO - client_tab:1715 - Rendered SPOC role tooltip +2025-06-27 08:43:22,132 - client_module - INFO - client_tab:1725 - Retrieved 10 default roles +2025-06-27 08:43:22,133 - client_module - INFO - client_tab:1784 - Role selector rendered with selection: Select a role... +2025-06-27 08:43:22,133 - client_module - INFO - client_tab:1799 - Cleared selected target role +2025-06-27 08:43:22,133 - client_module - INFO - client_tab:1812 - Rendered business priorities tooltip +2025-06-27 08:43:22,133 - client_module - INFO - client_tab:1841 - Using 3 business priorities +2025-06-27 08:43:22,134 - client_module - INFO - client_tab:1891 - Created additional requirements columns +2025-06-27 08:43:22,134 - client_module - INFO - client_tab:1903 - Rendered additional client requirements tooltip +2025-06-27 08:43:22,134 - client_module - INFO - client_tab:1918 - Additional requirements text area rendered with 0 characters +2025-06-27 08:43:22,134 - client_module - INFO - client_tab:1934 - Additional requirements provided status: False +2025-06-27 08:43:22,134 - client_module - INFO - client_tab:1948 - Rendered additional specifications tooltip +2025-06-27 08:43:22,134 - client_module - INFO - client_tab:1966 - Using default additional specs items +2025-06-27 08:43:24,079 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:24,079 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:24,079 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:24,080 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:24,080 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:24,080 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:24,081 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:24,081 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:24,081 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:24,082 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:24,082 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:24,083 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:24,083 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:24,084 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:24,088 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:24,089 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:24,091 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:24,092 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:24,094 - client_module - INFO - client_tab:904 - Scrape button clicked for URL: https://gsfjdfdjf.com +2025-06-27 08:43:24,266 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:24,266 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:24,267 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:24,267 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:24,267 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:24,267 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:24,267 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:24,267 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:24,267 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:24,267 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:24,267 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:24,268 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:24,268 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:24,268 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:24,269 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:24,269 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:24,269 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:24,269 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:24,270 - client_module - INFO - client_tab:931 - Starting website scraping for: https://gsfjdfdjf.com +2025-06-27 08:43:24,976 - client_module - WARNING - client_tab:938 - Website scraping returned empty data for: https://gsfjdfdjf.com +2025-06-27 08:43:28,146 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:28,146 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:28,146 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:28,147 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:28,147 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:28,147 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:28,147 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:28,148 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:28,148 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:28,148 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:28,148 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:28,149 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:28,149 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:28,149 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:28,149 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:28,150 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:28,150 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:28,150 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:28,151 - client_module - DEBUG - client_tab:971 - Creating file upload and enterprise details section +2025-06-27 08:43:28,151 - client_module - DEBUG - client_tab:976 - Processing file upload section +2025-06-27 08:43:28,152 - client_module - DEBUG - client_tab:1148 - Processing enterprise details section +2025-06-27 08:43:28,153 - client_module - DEBUG - client_tab:1186 - Creating client requirements and pain points section +2025-06-27 08:43:28,153 - client_module - DEBUG - client_tab:1191 - Processing client requirements section +2025-06-27 08:43:28,153 - client_module - DEBUG - client_tab:1215 - Updated client requirements content +2025-06-27 08:43:28,153 - client_module - DEBUG - client_tab:1220 - Client requirements provided: False +2025-06-27 08:43:28,153 - client_module - INFO - client_tab:1235 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:43:28,154 - client_module - INFO - client_tab:1259 - Using dummy pain points data as fallback +2025-06-27 08:43:28,154 - client_module - DEBUG - client_tab:1269 - Rendering 3 pain point items +2025-06-27 08:43:28,154 - client_module - DEBUG - client_tab:1274 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:43:28,154 - client_module - DEBUG - client_tab:1279 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:43:28,154 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:43:28,154 - client_module - DEBUG - client_tab:1274 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:43:28,155 - client_module - DEBUG - client_tab:1279 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:43:28,155 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:43:28,155 - client_module - DEBUG - client_tab:1274 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:43:28,155 - client_module - DEBUG - client_tab:1279 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:43:28,156 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:43:28,156 - client_module - INFO - client_tab:1509 - Rendered SPOC name tooltip +2025-06-27 08:43:28,157 - client_module - INFO - client_tab:1522 - SPOC name input rendered with value: +2025-06-27 08:43:28,157 - client_module - INFO - client_tab:1554 - SPOC name provided status: False +2025-06-27 08:43:28,157 - client_module - INFO - client_tab:1566 - Rendered LinkedIn profile tooltip +2025-06-27 08:43:28,157 - client_module - INFO - client_tab:1622 - LinkedIn profile selector in disabled state +2025-06-27 08:43:28,158 - client_module - INFO - client_tab:1703 - Created roles and priorities columns +2025-06-27 08:43:28,158 - client_module - INFO - client_tab:1715 - Rendered SPOC role tooltip +2025-06-27 08:43:28,158 - client_module - INFO - client_tab:1725 - Retrieved 10 default roles +2025-06-27 08:43:28,158 - client_module - INFO - client_tab:1784 - Role selector rendered with selection: Select a role... +2025-06-27 08:43:28,158 - client_module - INFO - client_tab:1799 - Cleared selected target role +2025-06-27 08:43:28,159 - client_module - INFO - client_tab:1812 - Rendered business priorities tooltip +2025-06-27 08:43:28,159 - client_module - INFO - client_tab:1841 - Using 3 business priorities +2025-06-27 08:43:28,160 - client_module - INFO - client_tab:1891 - Created additional requirements columns +2025-06-27 08:43:28,160 - client_module - INFO - client_tab:1903 - Rendered additional client requirements tooltip +2025-06-27 08:43:28,160 - client_module - INFO - client_tab:1918 - Additional requirements text area rendered with 0 characters +2025-06-27 08:43:28,160 - client_module - INFO - client_tab:1934 - Additional requirements provided status: False +2025-06-27 08:43:28,160 - client_module - INFO - client_tab:1948 - Rendered additional specifications tooltip +2025-06-27 08:43:28,160 - client_module - INFO - client_tab:1966 - Using default additional specs items +2025-06-27 08:43:46,886 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:46,886 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:46,886 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:46,887 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:46,887 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:46,887 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:46,888 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:46,888 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:46,888 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:46,888 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:46,888 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:46,889 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:46,889 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:46,889 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:46,890 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:46,890 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:46,891 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:46,891 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:46,891 - client_module - INFO - client_tab:872 - Updated website URL: https://benori.com +2025-06-27 08:43:46,891 - client_module - DEBUG - client_tab:971 - Creating file upload and enterprise details section +2025-06-27 08:43:46,892 - client_module - DEBUG - client_tab:976 - Processing file upload section +2025-06-27 08:43:46,892 - client_module - DEBUG - client_tab:1148 - Processing enterprise details section +2025-06-27 08:43:46,893 - client_module - DEBUG - client_tab:1186 - Creating client requirements and pain points section +2025-06-27 08:43:46,893 - client_module - DEBUG - client_tab:1191 - Processing client requirements section +2025-06-27 08:43:46,893 - client_module - DEBUG - client_tab:1215 - Updated client requirements content +2025-06-27 08:43:46,893 - client_module - DEBUG - client_tab:1220 - Client requirements provided: False +2025-06-27 08:43:46,893 - client_module - INFO - client_tab:1235 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:43:46,893 - client_module - INFO - client_tab:1259 - Using dummy pain points data as fallback +2025-06-27 08:43:46,894 - client_module - DEBUG - client_tab:1269 - Rendering 3 pain point items +2025-06-27 08:43:46,894 - client_module - DEBUG - client_tab:1274 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:43:46,894 - client_module - DEBUG - client_tab:1279 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:43:46,894 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:43:46,894 - client_module - DEBUG - client_tab:1274 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:43:46,894 - client_module - DEBUG - client_tab:1279 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:43:46,895 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:43:46,895 - client_module - DEBUG - client_tab:1274 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:43:46,895 - client_module - DEBUG - client_tab:1279 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:43:46,896 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:43:46,896 - client_module - INFO - client_tab:1509 - Rendered SPOC name tooltip +2025-06-27 08:43:46,896 - client_module - INFO - client_tab:1522 - SPOC name input rendered with value: +2025-06-27 08:43:46,896 - client_module - INFO - client_tab:1554 - SPOC name provided status: False +2025-06-27 08:43:46,897 - client_module - INFO - client_tab:1566 - Rendered LinkedIn profile tooltip +2025-06-27 08:43:46,897 - client_module - INFO - client_tab:1622 - LinkedIn profile selector in disabled state +2025-06-27 08:43:46,897 - client_module - INFO - client_tab:1703 - Created roles and priorities columns +2025-06-27 08:43:46,897 - client_module - INFO - client_tab:1715 - Rendered SPOC role tooltip +2025-06-27 08:43:46,897 - client_module - INFO - client_tab:1725 - Retrieved 10 default roles +2025-06-27 08:43:46,898 - client_module - INFO - client_tab:1784 - Role selector rendered with selection: Select a role... +2025-06-27 08:43:46,898 - client_module - INFO - client_tab:1799 - Cleared selected target role +2025-06-27 08:43:46,898 - client_module - INFO - client_tab:1812 - Rendered business priorities tooltip +2025-06-27 08:43:46,898 - client_module - INFO - client_tab:1841 - Using 3 business priorities +2025-06-27 08:43:46,898 - client_module - INFO - client_tab:1891 - Created additional requirements columns +2025-06-27 08:43:46,899 - client_module - INFO - client_tab:1903 - Rendered additional client requirements tooltip +2025-06-27 08:43:46,899 - client_module - INFO - client_tab:1918 - Additional requirements text area rendered with 0 characters +2025-06-27 08:43:46,899 - client_module - INFO - client_tab:1934 - Additional requirements provided status: False +2025-06-27 08:43:46,899 - client_module - INFO - client_tab:1948 - Rendered additional specifications tooltip +2025-06-27 08:43:46,899 - client_module - INFO - client_tab:1966 - Using default additional specs items +2025-06-27 08:43:48,471 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:48,472 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:48,473 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:48,473 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:48,473 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:48,473 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:48,474 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:48,474 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:48,474 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:48,475 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:48,475 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:48,475 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:48,475 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:48,476 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:48,476 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:48,477 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:48,477 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:48,477 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:48,478 - client_module - INFO - client_tab:904 - Scrape button clicked for URL: https://benori.com +2025-06-27 08:43:48,646 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:48,647 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:48,647 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:48,647 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:48,647 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:48,647 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:48,647 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:43:48,647 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:43:48,647 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:43:48,648 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:43:48,648 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:43:48,648 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:43:48,648 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:43:48,648 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:43:48,649 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:43:48,649 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:43:48,650 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:43:48,650 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:43:48,650 - client_module - INFO - client_tab:931 - Starting website scraping for: https://benori.com +2025-06-27 08:44:07,003 - client_module - INFO - client_tab:947 - Successfully scraped website details, length: 300 +2025-06-27 08:44:07,201 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:44:07,201 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:44:07,201 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:44:07,201 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:44:07,201 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:44:07,201 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:44:07,202 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:44:07,202 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:44:07,202 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:44:07,202 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:44:07,202 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:44:07,203 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:44:07,203 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:44:07,203 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:44:07,203 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: False +2025-06-27 08:44:07,204 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 08:44:07,204 - client_module - DEBUG - client_tab:840 - Client name provided: True +2025-06-27 08:44:07,204 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 08:44:07,205 - client_module - DEBUG - client_tab:971 - Creating file upload and enterprise details section +2025-06-27 08:44:07,205 - client_module - DEBUG - client_tab:976 - Processing file upload section +2025-06-27 08:44:07,205 - client_module - DEBUG - client_tab:1148 - Processing enterprise details section +2025-06-27 08:44:07,206 - client_module - DEBUG - client_tab:1186 - Creating client requirements and pain points section +2025-06-27 08:44:07,206 - client_module - DEBUG - client_tab:1191 - Processing client requirements section +2025-06-27 08:44:07,206 - client_module - DEBUG - client_tab:1215 - Updated client requirements content +2025-06-27 08:44:07,206 - client_module - DEBUG - client_tab:1220 - Client requirements provided: False +2025-06-27 08:44:07,206 - client_module - INFO - client_tab:1235 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:44:07,207 - client_module - INFO - client_tab:1259 - Using dummy pain points data as fallback +2025-06-27 08:44:07,207 - client_module - DEBUG - client_tab:1269 - Rendering 3 pain point items +2025-06-27 08:44:07,207 - client_module - DEBUG - client_tab:1274 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:44:07,207 - client_module - DEBUG - client_tab:1279 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:44:07,208 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:44:07,208 - client_module - DEBUG - client_tab:1274 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:44:07,208 - client_module - DEBUG - client_tab:1279 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:44:07,208 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:44:07,209 - client_module - DEBUG - client_tab:1274 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:44:07,209 - client_module - DEBUG - client_tab:1279 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:44:07,209 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:44:07,210 - client_module - INFO - client_tab:1509 - Rendered SPOC name tooltip +2025-06-27 08:44:07,210 - client_module - INFO - client_tab:1522 - SPOC name input rendered with value: +2025-06-27 08:44:07,210 - client_module - INFO - client_tab:1554 - SPOC name provided status: False +2025-06-27 08:44:07,211 - client_module - INFO - client_tab:1566 - Rendered LinkedIn profile tooltip +2025-06-27 08:44:07,211 - client_module - INFO - client_tab:1622 - LinkedIn profile selector in disabled state +2025-06-27 08:44:07,213 - client_module - INFO - client_tab:1703 - Created roles and priorities columns +2025-06-27 08:44:07,213 - client_module - INFO - client_tab:1715 - Rendered SPOC role tooltip +2025-06-27 08:44:07,214 - client_module - INFO - client_tab:1725 - Retrieved 10 default roles +2025-06-27 08:44:07,215 - client_module - INFO - client_tab:1784 - Role selector rendered with selection: Select a role... +2025-06-27 08:44:07,215 - client_module - INFO - client_tab:1799 - Cleared selected target role +2025-06-27 08:44:07,216 - client_module - INFO - client_tab:1812 - Rendered business priorities tooltip +2025-06-27 08:44:07,216 - client_module - INFO - client_tab:1841 - Using 3 business priorities +2025-06-27 08:44:07,218 - client_module - INFO - client_tab:1891 - Created additional requirements columns +2025-06-27 08:44:07,219 - client_module - INFO - client_tab:1903 - Rendered additional client requirements tooltip +2025-06-27 08:44:07,220 - client_module - INFO - client_tab:1918 - Additional requirements text area rendered with 0 characters +2025-06-27 08:44:07,220 - client_module - INFO - client_tab:1934 - Additional requirements provided status: False +2025-06-27 08:44:07,221 - client_module - INFO - client_tab:1948 - Rendered additional specifications tooltip +2025-06-27 08:44:07,221 - client_module - INFO - client_tab:1966 - Using default additional specs items +2025-06-27 08:46:33,484 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:33,485 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:33,485 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:33,486 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:33,486 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:33,487 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:33,487 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:33,487 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:33,488 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:33,489 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:46:33,489 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:46:33,489 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:46:33,490 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:46:33,491 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:46:33,491 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:46:33,492 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:46:33,493 - client_module - DEBUG - client_tab:836 - Find URLs button disabled: True +2025-06-27 08:46:33,494 - client_module - DEBUG - client_tab:882 - Processing client website URL section +2025-06-27 08:46:33,495 - client_module - DEBUG - client_tab:899 - Client name provided: False +2025-06-27 08:46:33,495 - client_module - DEBUG - client_tab:906 - URL options count: 1 +2025-06-27 08:46:33,496 - client_module - DEBUG - client_tab:1030 - Creating file upload and enterprise details section +2025-06-27 08:46:33,497 - client_module - DEBUG - client_tab:1035 - Processing file upload section +2025-06-27 08:46:33,498 - client_module - DEBUG - client_tab:1207 - Processing enterprise details section +2025-06-27 08:46:33,499 - client_module - DEBUG - client_tab:1245 - Creating client requirements and pain points section +2025-06-27 08:46:33,499 - client_module - DEBUG - client_tab:1250 - Processing client requirements section +2025-06-27 08:46:33,500 - client_module - DEBUG - client_tab:1279 - Client requirements provided: False +2025-06-27 08:46:33,500 - client_module - INFO - client_tab:1294 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:46:33,501 - client_module - INFO - client_tab:1318 - Using dummy pain points data as fallback +2025-06-27 08:46:33,501 - client_module - DEBUG - client_tab:1328 - Rendering 3 pain point items +2025-06-27 08:46:33,501 - client_module - DEBUG - client_tab:1333 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:46:33,501 - client_module - DEBUG - client_tab:1338 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:46:33,503 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:46:33,503 - client_module - DEBUG - client_tab:1333 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:46:33,504 - client_module - DEBUG - client_tab:1338 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:46:33,505 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:46:33,505 - client_module - DEBUG - client_tab:1333 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:46:33,505 - client_module - DEBUG - client_tab:1338 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:46:33,506 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:46:33,506 - client_module - INFO - client_tab:1568 - Rendered SPOC name tooltip +2025-06-27 08:46:33,507 - client_module - INFO - client_tab:1581 - SPOC name input rendered with value: +2025-06-27 08:46:33,507 - client_module - INFO - client_tab:1613 - SPOC name provided status: False +2025-06-27 08:46:33,508 - client_module - INFO - client_tab:1625 - Rendered LinkedIn profile tooltip +2025-06-27 08:46:33,509 - client_module - INFO - client_tab:1681 - LinkedIn profile selector in disabled state +2025-06-27 08:46:33,510 - client_module - INFO - client_tab:1762 - Created roles and priorities columns +2025-06-27 08:46:33,510 - client_module - INFO - client_tab:1774 - Rendered SPOC role tooltip +2025-06-27 08:46:33,511 - client_module - INFO - client_tab:1784 - Retrieved 10 default roles +2025-06-27 08:46:33,511 - client_module - INFO - client_tab:1843 - Role selector rendered with selection: Select a role... +2025-06-27 08:46:33,512 - client_module - INFO - client_tab:1858 - Cleared selected target role +2025-06-27 08:46:33,513 - client_module - INFO - client_tab:1871 - Rendered business priorities tooltip +2025-06-27 08:46:33,513 - client_module - INFO - client_tab:1900 - Using 3 business priorities +2025-06-27 08:46:33,515 - client_module - INFO - client_tab:1950 - Created additional requirements columns +2025-06-27 08:46:33,516 - client_module - INFO - client_tab:1962 - Rendered additional client requirements tooltip +2025-06-27 08:46:33,517 - client_module - INFO - client_tab:1977 - Additional requirements text area rendered with 0 characters +2025-06-27 08:46:33,518 - client_module - INFO - client_tab:1993 - Additional requirements provided status: False +2025-06-27 08:46:33,518 - client_module - INFO - client_tab:2007 - Rendered additional specifications tooltip +2025-06-27 08:46:33,519 - client_module - INFO - client_tab:2025 - Using default additional specs items +2025-06-27 08:46:40,108 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:40,108 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:40,108 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:40,109 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:40,109 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:40,109 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:40,110 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:40,110 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:40,110 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:40,110 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:46:40,110 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:46:40,111 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:46:40,111 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:46:40,111 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:46:40,112 - client_module - INFO - client_tab:828 - Updated enterprise name: Shreyank +2025-06-27 08:46:40,112 - client_module - DEBUG - client_tab:836 - Find URLs button disabled: False +2025-06-27 08:46:40,112 - client_module - DEBUG - client_tab:882 - Processing client website URL section +2025-06-27 08:46:40,112 - client_module - DEBUG - client_tab:899 - Client name provided: True +2025-06-27 08:46:40,113 - client_module - DEBUG - client_tab:906 - URL options count: 1 +2025-06-27 08:46:40,113 - client_module - DEBUG - client_tab:1030 - Creating file upload and enterprise details section +2025-06-27 08:46:40,114 - client_module - DEBUG - client_tab:1035 - Processing file upload section +2025-06-27 08:46:40,114 - client_module - DEBUG - client_tab:1207 - Processing enterprise details section +2025-06-27 08:46:40,114 - client_module - DEBUG - client_tab:1245 - Creating client requirements and pain points section +2025-06-27 08:46:40,115 - client_module - DEBUG - client_tab:1250 - Processing client requirements section +2025-06-27 08:46:40,117 - client_module - DEBUG - client_tab:1274 - Updated client requirements content +2025-06-27 08:46:40,117 - client_module - DEBUG - client_tab:1279 - Client requirements provided: False +2025-06-27 08:46:40,117 - client_module - INFO - client_tab:1294 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:46:40,117 - client_module - INFO - client_tab:1318 - Using dummy pain points data as fallback +2025-06-27 08:46:40,118 - client_module - DEBUG - client_tab:1328 - Rendering 3 pain point items +2025-06-27 08:46:40,118 - client_module - DEBUG - client_tab:1333 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:46:40,118 - client_module - DEBUG - client_tab:1338 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:46:40,120 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:46:40,120 - client_module - DEBUG - client_tab:1333 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:46:40,120 - client_module - DEBUG - client_tab:1338 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:46:40,122 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:46:40,122 - client_module - DEBUG - client_tab:1333 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:46:40,122 - client_module - DEBUG - client_tab:1338 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:46:40,124 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:46:40,124 - client_module - INFO - client_tab:1568 - Rendered SPOC name tooltip +2025-06-27 08:46:40,125 - client_module - INFO - client_tab:1581 - SPOC name input rendered with value: +2025-06-27 08:46:40,126 - client_module - INFO - client_tab:1613 - SPOC name provided status: False +2025-06-27 08:46:40,126 - client_module - INFO - client_tab:1625 - Rendered LinkedIn profile tooltip +2025-06-27 08:46:40,127 - client_module - INFO - client_tab:1681 - LinkedIn profile selector in disabled state +2025-06-27 08:46:40,127 - client_module - INFO - client_tab:1762 - Created roles and priorities columns +2025-06-27 08:46:40,128 - client_module - INFO - client_tab:1774 - Rendered SPOC role tooltip +2025-06-27 08:46:40,128 - client_module - INFO - client_tab:1784 - Retrieved 10 default roles +2025-06-27 08:46:40,128 - client_module - INFO - client_tab:1843 - Role selector rendered with selection: Select a role... +2025-06-27 08:46:40,129 - client_module - INFO - client_tab:1858 - Cleared selected target role +2025-06-27 08:46:40,129 - client_module - INFO - client_tab:1871 - Rendered business priorities tooltip +2025-06-27 08:46:40,130 - client_module - INFO - client_tab:1900 - Using 3 business priorities +2025-06-27 08:46:40,131 - client_module - INFO - client_tab:1950 - Created additional requirements columns +2025-06-27 08:46:40,131 - client_module - INFO - client_tab:1962 - Rendered additional client requirements tooltip +2025-06-27 08:46:40,132 - client_module - INFO - client_tab:1977 - Additional requirements text area rendered with 0 characters +2025-06-27 08:46:40,132 - client_module - INFO - client_tab:1993 - Additional requirements provided status: False +2025-06-27 08:46:40,132 - client_module - INFO - client_tab:2007 - Rendered additional specifications tooltip +2025-06-27 08:46:40,132 - client_module - INFO - client_tab:2025 - Using default additional specs items +2025-06-27 08:46:41,406 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:41,406 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:41,406 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:41,407 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:41,409 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:41,409 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:41,410 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:41,411 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:41,411 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:41,412 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:46:41,412 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:46:41,413 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:46:41,413 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:46:41,414 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:46:41,416 - client_module - DEBUG - client_tab:836 - Find URLs button disabled: False +2025-06-27 08:46:41,417 - client_module - INFO - client_tab:844 - Find URLs button clicked for: Shreyank +2025-06-27 08:46:41,603 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:41,603 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:41,603 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:41,603 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:41,603 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:41,603 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:41,603 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:41,604 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:41,604 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:41,604 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:46:41,605 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:46:41,605 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:46:41,605 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:46:41,605 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:46:44,279 - client_module - INFO - client_tab:780 - Found 4 URLs for Shreyank +2025-06-27 08:46:44,279 - client_module - DEBUG - client_tab:786 - Updated client data with URLs list +2025-06-27 08:46:44,475 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:44,477 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:44,477 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:44,478 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:44,478 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:44,478 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:44,478 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:46:44,478 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:46:44,478 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:46:44,478 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:46:44,478 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:46:44,479 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:46:44,479 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:46:44,479 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:46:44,480 - client_module - DEBUG - client_tab:836 - Find URLs button disabled: False +2025-06-27 08:46:44,480 - client_module - DEBUG - client_tab:882 - Processing client website URL section +2025-06-27 08:46:44,480 - client_module - DEBUG - client_tab:899 - Client name provided: True +2025-06-27 08:46:44,480 - client_module - DEBUG - client_tab:906 - URL options count: 5 +2025-06-27 08:46:44,481 - client_module - DEBUG - client_tab:1030 - Creating file upload and enterprise details section +2025-06-27 08:46:44,481 - client_module - DEBUG - client_tab:1035 - Processing file upload section +2025-06-27 08:46:44,482 - client_module - DEBUG - client_tab:1207 - Processing enterprise details section +2025-06-27 08:46:44,482 - client_module - DEBUG - client_tab:1245 - Creating client requirements and pain points section +2025-06-27 08:46:44,482 - client_module - DEBUG - client_tab:1250 - Processing client requirements section +2025-06-27 08:46:44,483 - client_module - DEBUG - client_tab:1274 - Updated client requirements content +2025-06-27 08:46:44,483 - client_module - DEBUG - client_tab:1279 - Client requirements provided: False +2025-06-27 08:46:44,483 - client_module - INFO - client_tab:1294 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:46:44,483 - client_module - INFO - client_tab:1318 - Using dummy pain points data as fallback +2025-06-27 08:46:44,483 - client_module - DEBUG - client_tab:1328 - Rendering 3 pain point items +2025-06-27 08:46:44,483 - client_module - DEBUG - client_tab:1333 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:46:44,484 - client_module - DEBUG - client_tab:1338 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:46:44,484 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:46:44,484 - client_module - DEBUG - client_tab:1333 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:46:44,484 - client_module - DEBUG - client_tab:1338 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:46:44,485 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:46:44,485 - client_module - DEBUG - client_tab:1333 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:46:44,485 - client_module - DEBUG - client_tab:1338 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:46:44,486 - client_module - DEBUG - client_tab:1537 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:46:44,486 - client_module - INFO - client_tab:1568 - Rendered SPOC name tooltip +2025-06-27 08:46:44,486 - client_module - INFO - client_tab:1581 - SPOC name input rendered with value: +2025-06-27 08:46:44,487 - client_module - INFO - client_tab:1613 - SPOC name provided status: False +2025-06-27 08:46:44,487 - client_module - INFO - client_tab:1625 - Rendered LinkedIn profile tooltip +2025-06-27 08:46:44,487 - client_module - INFO - client_tab:1681 - LinkedIn profile selector in disabled state +2025-06-27 08:46:44,488 - client_module - INFO - client_tab:1762 - Created roles and priorities columns +2025-06-27 08:46:44,488 - client_module - INFO - client_tab:1774 - Rendered SPOC role tooltip +2025-06-27 08:46:44,488 - client_module - INFO - client_tab:1784 - Retrieved 10 default roles +2025-06-27 08:46:44,488 - client_module - INFO - client_tab:1843 - Role selector rendered with selection: Select a role... +2025-06-27 08:46:44,488 - client_module - INFO - client_tab:1858 - Cleared selected target role +2025-06-27 08:46:44,488 - client_module - INFO - client_tab:1871 - Rendered business priorities tooltip +2025-06-27 08:46:44,488 - client_module - INFO - client_tab:1900 - Using 3 business priorities +2025-06-27 08:46:44,489 - client_module - INFO - client_tab:1950 - Created additional requirements columns +2025-06-27 08:46:44,489 - client_module - INFO - client_tab:1962 - Rendered additional client requirements tooltip +2025-06-27 08:46:44,490 - client_module - INFO - client_tab:1977 - Additional requirements text area rendered with 0 characters +2025-06-27 08:46:44,490 - client_module - INFO - client_tab:1993 - Additional requirements provided status: False +2025-06-27 08:46:44,490 - client_module - INFO - client_tab:2007 - Rendered additional specifications tooltip +2025-06-27 08:46:44,490 - client_module - INFO - client_tab:2025 - Using default additional specs items +2025-06-27 08:52:53,458 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:52:53,458 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:52:53,458 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:52:53,458 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:52:53,458 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:52:53,458 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:52:53,459 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:52:53,459 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:52:53,459 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:52:53,459 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:52:53,459 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:52:53,459 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:52:53,460 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:52:53,460 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:52:53,460 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:52:53,461 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:52:53,461 - client_module - DEBUG - client_tab:763 - Find URLs button disabled: True +2025-06-27 08:52:53,461 - client_module - DEBUG - client_tab:837 - Processing client website URL section +2025-06-27 08:52:53,462 - client_module - DEBUG - client_tab:854 - Client name provided: False +2025-06-27 08:52:53,462 - client_module - DEBUG - client_tab:861 - URL options count: 1 +2025-06-27 08:52:53,463 - client_module - DEBUG - client_tab:985 - Creating file upload and enterprise details section +2025-06-27 08:52:53,463 - client_module - DEBUG - client_tab:990 - Processing file upload section +2025-06-27 08:52:53,464 - client_module - DEBUG - client_tab:1162 - Processing enterprise details section +2025-06-27 08:52:53,464 - client_module - DEBUG - client_tab:1200 - Creating client requirements and pain points section +2025-06-27 08:52:53,465 - client_module - DEBUG - client_tab:1205 - Processing client requirements section +2025-06-27 08:52:53,465 - client_module - DEBUG - client_tab:1234 - Client requirements provided: False +2025-06-27 08:52:53,465 - client_module - INFO - client_tab:1249 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:52:53,465 - client_module - INFO - client_tab:1273 - Using dummy pain points data as fallback +2025-06-27 08:52:53,466 - client_module - DEBUG - client_tab:1283 - Rendering 3 pain point items +2025-06-27 08:52:53,466 - client_module - DEBUG - client_tab:1288 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:52:53,466 - client_module - DEBUG - client_tab:1293 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:52:53,466 - client_module - DEBUG - client_tab:1492 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:52:53,467 - client_module - DEBUG - client_tab:1288 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:52:53,467 - client_module - DEBUG - client_tab:1293 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:52:53,468 - client_module - DEBUG - client_tab:1492 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:52:53,468 - client_module - DEBUG - client_tab:1288 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:52:53,468 - client_module - DEBUG - client_tab:1293 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:52:53,468 - client_module - DEBUG - client_tab:1492 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:52:53,469 - client_module - INFO - client_tab:1523 - Rendered SPOC name tooltip +2025-06-27 08:52:53,469 - client_module - INFO - client_tab:1536 - SPOC name input rendered with value: +2025-06-27 08:52:53,469 - client_module - INFO - client_tab:1568 - SPOC name provided status: False +2025-06-27 08:52:53,470 - client_module - INFO - client_tab:1580 - Rendered LinkedIn profile tooltip +2025-06-27 08:52:53,470 - client_module - INFO - client_tab:1636 - LinkedIn profile selector in disabled state +2025-06-27 08:52:53,470 - client_module - INFO - client_tab:1717 - Created roles and priorities columns +2025-06-27 08:52:53,471 - client_module - INFO - client_tab:1729 - Rendered SPOC role tooltip +2025-06-27 08:52:53,471 - client_module - INFO - client_tab:1739 - Retrieved 10 default roles +2025-06-27 08:52:53,472 - client_module - INFO - client_tab:1798 - Role selector rendered with selection: Select a role... +2025-06-27 08:52:53,473 - client_module - INFO - client_tab:1813 - Cleared selected target role +2025-06-27 08:52:53,473 - client_module - INFO - client_tab:1826 - Rendered business priorities tooltip +2025-06-27 08:52:53,473 - client_module - INFO - client_tab:1855 - Using 3 business priorities +2025-06-27 08:52:53,475 - client_module - INFO - client_tab:1905 - Created additional requirements columns +2025-06-27 08:52:53,475 - client_module - INFO - client_tab:1917 - Rendered additional client requirements tooltip +2025-06-27 08:52:53,476 - client_module - INFO - client_tab:1932 - Additional requirements text area rendered with 0 characters +2025-06-27 08:52:53,476 - client_module - INFO - client_tab:1948 - Additional requirements provided status: False +2025-06-27 08:52:53,477 - client_module - INFO - client_tab:1962 - Rendered additional specifications tooltip +2025-06-27 08:52:53,477 - client_module - INFO - client_tab:1980 - Using default additional specs items +2025-06-27 08:53:40,851 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:53:40,851 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:53:40,852 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:53:40,852 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:53:40,852 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:53:40,852 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:53:40,852 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:53:40,853 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:53:40,853 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:53:40,853 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:53:40,853 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:53:40,853 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:53:40,854 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:53:40,854 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:53:40,854 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:53:40,855 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:53:40,855 - client_module - DEBUG - client_tab:760 - Find URLs button disabled: True +2025-06-27 08:53:40,855 - client_module - DEBUG - client_tab:834 - Processing client website URL section +2025-06-27 08:53:40,856 - client_module - DEBUG - client_tab:851 - Client name provided: False +2025-06-27 08:53:40,856 - client_module - DEBUG - client_tab:858 - URL options count: 1 +2025-06-27 08:53:40,856 - client_module - DEBUG - client_tab:982 - Creating file upload and enterprise details section +2025-06-27 08:53:40,857 - client_module - DEBUG - client_tab:987 - Processing file upload section +2025-06-27 08:53:40,857 - client_module - DEBUG - client_tab:1159 - Processing enterprise details section +2025-06-27 08:53:40,858 - client_module - DEBUG - client_tab:1197 - Creating client requirements and pain points section +2025-06-27 08:53:40,858 - client_module - DEBUG - client_tab:1202 - Processing client requirements section +2025-06-27 08:53:40,859 - client_module - DEBUG - client_tab:1231 - Client requirements provided: False +2025-06-27 08:53:40,859 - client_module - INFO - client_tab:1246 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:53:40,859 - client_module - INFO - client_tab:1270 - Using dummy pain points data as fallback +2025-06-27 08:53:40,859 - client_module - DEBUG - client_tab:1280 - Rendering 3 pain point items +2025-06-27 08:53:40,859 - client_module - DEBUG - client_tab:1285 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:53:40,859 - client_module - DEBUG - client_tab:1290 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:53:40,860 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:53:40,860 - client_module - DEBUG - client_tab:1285 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:53:40,860 - client_module - DEBUG - client_tab:1290 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:53:40,861 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:53:40,861 - client_module - DEBUG - client_tab:1285 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:53:40,861 - client_module - DEBUG - client_tab:1290 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:53:40,862 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:53:40,862 - client_module - INFO - client_tab:1520 - Rendered SPOC name tooltip +2025-06-27 08:53:40,863 - client_module - INFO - client_tab:1533 - SPOC name input rendered with value: +2025-06-27 08:53:40,863 - client_module - INFO - client_tab:1565 - SPOC name provided status: False +2025-06-27 08:53:40,863 - client_module - INFO - client_tab:1577 - Rendered LinkedIn profile tooltip +2025-06-27 08:53:40,863 - client_module - INFO - client_tab:1633 - LinkedIn profile selector in disabled state +2025-06-27 08:53:40,864 - client_module - INFO - client_tab:1714 - Created roles and priorities columns +2025-06-27 08:53:40,864 - client_module - INFO - client_tab:1726 - Rendered SPOC role tooltip +2025-06-27 08:53:40,865 - client_module - INFO - client_tab:1736 - Retrieved 10 default roles +2025-06-27 08:53:40,866 - client_module - INFO - client_tab:1795 - Role selector rendered with selection: Select a role... +2025-06-27 08:53:40,866 - client_module - INFO - client_tab:1810 - Cleared selected target role +2025-06-27 08:53:40,867 - client_module - INFO - client_tab:1823 - Rendered business priorities tooltip +2025-06-27 08:53:40,867 - client_module - INFO - client_tab:1852 - Using 3 business priorities +2025-06-27 08:53:40,867 - client_module - INFO - client_tab:1902 - Created additional requirements columns +2025-06-27 08:53:40,868 - client_module - INFO - client_tab:1914 - Rendered additional client requirements tooltip +2025-06-27 08:53:40,868 - client_module - INFO - client_tab:1929 - Additional requirements text area rendered with 0 characters +2025-06-27 08:53:40,868 - client_module - INFO - client_tab:1945 - Additional requirements provided status: False +2025-06-27 08:53:40,868 - client_module - INFO - client_tab:1959 - Rendered additional specifications tooltip +2025-06-27 08:53:40,868 - client_module - INFO - client_tab:1977 - Using default additional specs items +2025-06-27 08:55:29,224 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:55:29,225 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:55:29,225 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:55:29,225 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:55:29,225 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:55:29,225 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:55:29,226 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:55:29,226 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:55:29,226 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:55:29,226 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:55:29,226 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:55:29,226 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:55:29,227 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:55:29,227 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:55:29,228 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:55:29,228 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:55:29,228 - client_module - DEBUG - client_tab:760 - Find URLs button disabled: True +2025-06-27 08:55:29,228 - client_module - DEBUG - client_tab:834 - Processing client website URL section +2025-06-27 08:55:29,229 - client_module - DEBUG - client_tab:851 - Client name provided: False +2025-06-27 08:55:29,229 - client_module - DEBUG - client_tab:858 - URL options count: 1 +2025-06-27 08:55:29,230 - client_module - DEBUG - client_tab:982 - Creating file upload and enterprise details section +2025-06-27 08:55:29,230 - client_module - DEBUG - client_tab:987 - Processing file upload section +2025-06-27 08:55:29,230 - client_module - DEBUG - client_tab:1159 - Processing enterprise details section +2025-06-27 08:55:29,231 - client_module - DEBUG - client_tab:1197 - Creating client requirements and pain points section +2025-06-27 08:55:29,231 - client_module - DEBUG - client_tab:1202 - Processing client requirements section +2025-06-27 08:55:29,231 - client_module - DEBUG - client_tab:1231 - Client requirements provided: False +2025-06-27 08:55:29,231 - client_module - INFO - client_tab:1246 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:55:29,232 - client_module - INFO - client_tab:1270 - Using dummy pain points data as fallback +2025-06-27 08:55:29,232 - client_module - DEBUG - client_tab:1280 - Rendering 3 pain point items +2025-06-27 08:55:29,232 - client_module - DEBUG - client_tab:1285 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:55:29,232 - client_module - DEBUG - client_tab:1290 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:55:29,233 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:55:29,233 - client_module - DEBUG - client_tab:1285 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:55:29,233 - client_module - DEBUG - client_tab:1290 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:55:29,233 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:55:29,234 - client_module - DEBUG - client_tab:1285 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:55:29,234 - client_module - DEBUG - client_tab:1290 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:55:29,234 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:55:29,235 - client_module - INFO - client_tab:1520 - Rendered SPOC name tooltip +2025-06-27 08:55:29,235 - client_module - INFO - client_tab:1533 - SPOC name input rendered with value: +2025-06-27 08:55:29,235 - client_module - INFO - client_tab:1565 - SPOC name provided status: False +2025-06-27 08:55:29,235 - client_module - INFO - client_tab:1577 - Rendered LinkedIn profile tooltip +2025-06-27 08:55:29,235 - client_module - INFO - client_tab:1633 - LinkedIn profile selector in disabled state +2025-06-27 08:55:29,236 - client_module - INFO - client_tab:1714 - Created roles and priorities columns +2025-06-27 08:55:29,236 - client_module - INFO - client_tab:1726 - Rendered SPOC role tooltip +2025-06-27 08:55:29,236 - client_module - INFO - client_tab:1736 - Retrieved 10 default roles +2025-06-27 08:55:29,236 - client_module - INFO - client_tab:1795 - Role selector rendered with selection: Select a role... +2025-06-27 08:55:29,236 - client_module - INFO - client_tab:1810 - Cleared selected target role +2025-06-27 08:55:29,237 - client_module - INFO - client_tab:1823 - Rendered business priorities tooltip +2025-06-27 08:55:29,237 - client_module - INFO - client_tab:1852 - Using 3 business priorities +2025-06-27 08:55:29,238 - client_module - INFO - client_tab:1902 - Created additional requirements columns +2025-06-27 08:55:29,238 - client_module - INFO - client_tab:1914 - Rendered additional client requirements tooltip +2025-06-27 08:55:29,239 - client_module - INFO - client_tab:1929 - Additional requirements text area rendered with 0 characters +2025-06-27 08:55:29,239 - client_module - INFO - client_tab:1945 - Additional requirements provided status: False +2025-06-27 08:55:29,240 - client_module - INFO - client_tab:1959 - Rendered additional specifications tooltip +2025-06-27 08:55:29,240 - client_module - INFO - client_tab:1977 - Using default additional specs items +2025-06-27 08:59:32,010 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:59:32,010 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:59:32,010 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:59:32,011 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:59:32,011 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:59:32,011 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:59:32,011 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 08:59:32,011 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 08:59:32,011 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 08:59:32,012 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 08:59:32,012 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 08:59:32,012 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 08:59:32,013 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 08:59:32,014 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 08:59:32,014 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 08:59:32,014 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 08:59:32,015 - client_module - DEBUG - client_tab:760 - Find URLs button disabled: True +2025-06-27 08:59:32,016 - client_module - DEBUG - client_tab:834 - Processing client website URL section +2025-06-27 08:59:32,016 - client_module - DEBUG - client_tab:846 - Client name provided: False +2025-06-27 08:59:32,017 - client_module - DEBUG - client_tab:853 - URL options count: 1 +2025-06-27 08:59:32,018 - client_module - DEBUG - client_tab:976 - Creating file upload and enterprise details section +2025-06-27 08:59:32,018 - client_module - DEBUG - client_tab:981 - Processing file upload section +2025-06-27 08:59:32,019 - client_module - DEBUG - client_tab:1153 - Processing enterprise details section +2025-06-27 08:59:32,020 - client_module - DEBUG - client_tab:1191 - Creating client requirements and pain points section +2025-06-27 08:59:32,020 - client_module - DEBUG - client_tab:1196 - Processing client requirements section +2025-06-27 08:59:32,021 - client_module - DEBUG - client_tab:1225 - Client requirements provided: False +2025-06-27 08:59:32,021 - client_module - INFO - client_tab:1240 - Starting COL6 - Client Pain Points section rendering +2025-06-27 08:59:32,022 - client_module - INFO - client_tab:1264 - Using dummy pain points data as fallback +2025-06-27 08:59:32,022 - client_module - DEBUG - client_tab:1274 - Rendering 3 pain point items +2025-06-27 08:59:32,022 - client_module - DEBUG - client_tab:1279 - Processing pain point item 0: Revenue Challenges +2025-06-27 08:59:32,022 - client_module - DEBUG - client_tab:1284 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 08:59:32,024 - client_module - DEBUG - client_tab:1483 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 08:59:32,024 - client_module - DEBUG - client_tab:1279 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 08:59:32,024 - client_module - DEBUG - client_tab:1284 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 08:59:32,025 - client_module - DEBUG - client_tab:1483 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 08:59:32,026 - client_module - DEBUG - client_tab:1279 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 08:59:32,026 - client_module - DEBUG - client_tab:1284 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 08:59:32,027 - client_module - DEBUG - client_tab:1483 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 08:59:32,028 - client_module - INFO - client_tab:1514 - Rendered SPOC name tooltip +2025-06-27 08:59:32,029 - client_module - INFO - client_tab:1527 - SPOC name input rendered with value: +2025-06-27 08:59:32,029 - client_module - INFO - client_tab:1559 - SPOC name provided status: False +2025-06-27 08:59:32,030 - client_module - INFO - client_tab:1571 - Rendered LinkedIn profile tooltip +2025-06-27 08:59:32,030 - client_module - INFO - client_tab:1627 - LinkedIn profile selector in disabled state +2025-06-27 08:59:32,031 - client_module - INFO - client_tab:1708 - Created roles and priorities columns +2025-06-27 08:59:32,031 - client_module - INFO - client_tab:1720 - Rendered SPOC role tooltip +2025-06-27 08:59:32,031 - client_module - INFO - client_tab:1730 - Retrieved 10 default roles +2025-06-27 08:59:32,032 - client_module - INFO - client_tab:1789 - Role selector rendered with selection: Select a role... +2025-06-27 08:59:32,033 - client_module - INFO - client_tab:1804 - Cleared selected target role +2025-06-27 08:59:32,033 - client_module - INFO - client_tab:1817 - Rendered business priorities tooltip +2025-06-27 08:59:32,033 - client_module - INFO - client_tab:1846 - Using 3 business priorities +2025-06-27 08:59:32,034 - client_module - INFO - client_tab:1896 - Created additional requirements columns +2025-06-27 08:59:32,035 - client_module - INFO - client_tab:1908 - Rendered additional client requirements tooltip +2025-06-27 08:59:32,035 - client_module - INFO - client_tab:1923 - Additional requirements text area rendered with 0 characters +2025-06-27 08:59:32,036 - client_module - INFO - client_tab:1939 - Additional requirements provided status: False +2025-06-27 08:59:32,036 - client_module - INFO - client_tab:1953 - Rendered additional specifications tooltip +2025-06-27 08:59:32,037 - client_module - INFO - client_tab:1971 - Using default additional specs items +2025-06-27 09:00:44,516 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:00:44,517 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:00:44,517 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:00:44,518 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:00:44,518 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:00:44,518 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:00:44,518 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:00:44,519 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:00:44,519 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:00:44,519 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 09:00:44,520 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 09:00:44,520 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 09:00:44,521 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 09:00:44,522 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 09:00:44,522 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 09:00:44,523 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 09:00:44,524 - client_module - DEBUG - client_tab:761 - Find URLs button disabled: True +2025-06-27 09:00:44,524 - client_module - DEBUG - client_tab:835 - Processing client website URL section +2025-06-27 09:00:44,525 - client_module - DEBUG - client_tab:852 - Client name provided: False +2025-06-27 09:00:44,525 - client_module - DEBUG - client_tab:859 - URL options count: 1 +2025-06-27 09:00:44,526 - client_module - DEBUG - client_tab:983 - Creating file upload and enterprise details section +2025-06-27 09:00:44,527 - client_module - DEBUG - client_tab:988 - Processing file upload section +2025-06-27 09:00:44,528 - client_module - DEBUG - client_tab:1160 - Processing enterprise details section +2025-06-27 09:00:44,529 - client_module - DEBUG - client_tab:1198 - Creating client requirements and pain points section +2025-06-27 09:00:44,529 - client_module - DEBUG - client_tab:1203 - Processing client requirements section +2025-06-27 09:00:44,530 - client_module - DEBUG - client_tab:1232 - Client requirements provided: False +2025-06-27 09:00:44,530 - client_module - INFO - client_tab:1247 - Starting COL6 - Client Pain Points section rendering +2025-06-27 09:00:44,530 - client_module - INFO - client_tab:1271 - Using dummy pain points data as fallback +2025-06-27 09:00:44,531 - client_module - DEBUG - client_tab:1281 - Rendering 3 pain point items +2025-06-27 09:00:44,531 - client_module - DEBUG - client_tab:1286 - Processing pain point item 0: Revenue Challenges +2025-06-27 09:00:44,531 - client_module - DEBUG - client_tab:1291 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 09:00:44,533 - client_module - DEBUG - client_tab:1490 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 09:00:44,533 - client_module - DEBUG - client_tab:1286 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 09:00:44,533 - client_module - DEBUG - client_tab:1291 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 09:00:44,534 - client_module - DEBUG - client_tab:1490 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 09:00:44,535 - client_module - DEBUG - client_tab:1286 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 09:00:44,535 - client_module - DEBUG - client_tab:1291 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 09:00:44,536 - client_module - DEBUG - client_tab:1490 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 09:00:44,537 - client_module - INFO - client_tab:1521 - Rendered SPOC name tooltip +2025-06-27 09:00:44,539 - client_module - INFO - client_tab:1534 - SPOC name input rendered with value: +2025-06-27 09:00:44,540 - client_module - INFO - client_tab:1566 - SPOC name provided status: False +2025-06-27 09:00:44,541 - client_module - INFO - client_tab:1578 - Rendered LinkedIn profile tooltip +2025-06-27 09:00:44,542 - client_module - INFO - client_tab:1634 - LinkedIn profile selector in disabled state +2025-06-27 09:00:44,543 - client_module - INFO - client_tab:1715 - Created roles and priorities columns +2025-06-27 09:00:44,544 - client_module - INFO - client_tab:1727 - Rendered SPOC role tooltip +2025-06-27 09:00:44,544 - client_module - INFO - client_tab:1737 - Retrieved 10 default roles +2025-06-27 09:00:44,545 - client_module - INFO - client_tab:1796 - Role selector rendered with selection: Select a role... +2025-06-27 09:00:44,546 - client_module - INFO - client_tab:1811 - Cleared selected target role +2025-06-27 09:00:44,546 - client_module - INFO - client_tab:1824 - Rendered business priorities tooltip +2025-06-27 09:00:44,547 - client_module - INFO - client_tab:1853 - Using 3 business priorities +2025-06-27 09:00:44,549 - client_module - INFO - client_tab:1903 - Created additional requirements columns +2025-06-27 09:00:44,550 - client_module - INFO - client_tab:1915 - Rendered additional client requirements tooltip +2025-06-27 09:00:44,554 - client_module - INFO - client_tab:1930 - Additional requirements text area rendered with 0 characters +2025-06-27 09:00:44,554 - client_module - INFO - client_tab:1946 - Additional requirements provided status: False +2025-06-27 09:00:44,556 - client_module - INFO - client_tab:1960 - Rendered additional specifications tooltip +2025-06-27 09:00:44,556 - client_module - INFO - client_tab:1978 - Using default additional specs items +2025-06-27 09:01:04,535 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:04,535 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:04,536 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:04,536 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:04,537 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:04,537 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:04,538 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:04,538 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:04,538 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:04,539 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 09:01:04,539 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 09:01:04,540 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 09:01:04,541 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 09:01:04,541 - client_module - DEBUG - client_tab:722 - CSS re-applied for persistence +2025-06-27 09:01:04,541 - client_module - DEBUG - client_tab:729 - Creating top section with client name and URLs +2025-06-27 09:01:04,542 - client_module - DEBUG - client_tab:734 - Processing client enterprise name section +2025-06-27 09:01:04,543 - client_module - DEBUG - client_tab:760 - Find URLs button disabled: True +2025-06-27 09:01:04,544 - client_module - DEBUG - client_tab:834 - Processing client website URL section +2025-06-27 09:01:04,546 - client_module - DEBUG - client_tab:851 - Client name provided: False +2025-06-27 09:01:04,546 - client_module - DEBUG - client_tab:858 - URL options count: 1 +2025-06-27 09:01:04,548 - client_module - DEBUG - client_tab:982 - Creating file upload and enterprise details section +2025-06-27 09:01:04,549 - client_module - DEBUG - client_tab:987 - Processing file upload section +2025-06-27 09:01:04,551 - client_module - DEBUG - client_tab:1159 - Processing enterprise details section +2025-06-27 09:01:04,553 - client_module - DEBUG - client_tab:1197 - Creating client requirements and pain points section +2025-06-27 09:01:04,555 - client_module - DEBUG - client_tab:1202 - Processing client requirements section +2025-06-27 09:01:04,556 - client_module - DEBUG - client_tab:1231 - Client requirements provided: False +2025-06-27 09:01:04,556 - client_module - INFO - client_tab:1246 - Starting COL6 - Client Pain Points section rendering +2025-06-27 09:01:04,557 - client_module - INFO - client_tab:1270 - Using dummy pain points data as fallback +2025-06-27 09:01:04,558 - client_module - DEBUG - client_tab:1280 - Rendering 3 pain point items +2025-06-27 09:01:04,558 - client_module - DEBUG - client_tab:1285 - Processing pain point item 0: Revenue Challenges +2025-06-27 09:01:04,558 - client_module - DEBUG - client_tab:1290 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 09:01:04,561 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 09:01:04,562 - client_module - DEBUG - client_tab:1285 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 09:01:04,562 - client_module - DEBUG - client_tab:1290 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 09:01:04,565 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 09:01:04,565 - client_module - DEBUG - client_tab:1285 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 09:01:04,566 - client_module - DEBUG - client_tab:1290 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 09:01:04,568 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 09:01:04,569 - client_module - INFO - client_tab:1520 - Rendered SPOC name tooltip +2025-06-27 09:01:04,571 - client_module - INFO - client_tab:1533 - SPOC name input rendered with value: +2025-06-27 09:01:04,571 - client_module - INFO - client_tab:1565 - SPOC name provided status: False +2025-06-27 09:01:04,572 - client_module - INFO - client_tab:1577 - Rendered LinkedIn profile tooltip +2025-06-27 09:01:04,573 - client_module - INFO - client_tab:1633 - LinkedIn profile selector in disabled state +2025-06-27 09:01:04,574 - client_module - INFO - client_tab:1714 - Created roles and priorities columns +2025-06-27 09:01:04,575 - client_module - INFO - client_tab:1726 - Rendered SPOC role tooltip +2025-06-27 09:01:04,575 - client_module - INFO - client_tab:1736 - Retrieved 10 default roles +2025-06-27 09:01:04,577 - client_module - INFO - client_tab:1795 - Role selector rendered with selection: Select a role... +2025-06-27 09:01:04,577 - client_module - INFO - client_tab:1810 - Cleared selected target role +2025-06-27 09:01:04,578 - client_module - INFO - client_tab:1823 - Rendered business priorities tooltip +2025-06-27 09:01:04,578 - client_module - INFO - client_tab:1852 - Using 3 business priorities +2025-06-27 09:01:04,581 - client_module - INFO - client_tab:1902 - Created additional requirements columns +2025-06-27 09:01:04,583 - client_module - INFO - client_tab:1914 - Rendered additional client requirements tooltip +2025-06-27 09:01:04,584 - client_module - INFO - client_tab:1929 - Additional requirements text area rendered with 0 characters +2025-06-27 09:01:04,584 - client_module - INFO - client_tab:1945 - Additional requirements provided status: False +2025-06-27 09:01:04,585 - client_module - INFO - client_tab:1959 - Rendered additional specifications tooltip +2025-06-27 09:01:04,585 - client_module - INFO - client_tab:1977 - Using default additional specs items +2025-06-27 09:01:21,336 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:21,337 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:21,337 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:21,338 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:21,338 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:21,339 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:21,339 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:21,339 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:21,339 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:21,340 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 09:01:21,340 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 09:01:21,340 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 09:01:21,341 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 09:01:21,341 - client_module - DEBUG - client_tab:722 - CSS re-applied for persistence +2025-06-27 09:01:21,341 - client_module - DEBUG - client_tab:729 - Creating top section with client name and URLs +2025-06-27 09:01:21,342 - client_module - DEBUG - client_tab:734 - Processing client enterprise name section +2025-06-27 09:01:21,343 - client_module - DEBUG - client_tab:760 - Find URLs button disabled: True +2025-06-27 09:01:21,343 - client_module - DEBUG - client_tab:834 - Processing client website URL section +2025-06-27 09:01:21,344 - client_module - DEBUG - client_tab:851 - Client name provided: False +2025-06-27 09:01:21,344 - client_module - DEBUG - client_tab:858 - URL options count: 1 +2025-06-27 09:01:21,346 - client_module - DEBUG - client_tab:982 - Creating file upload and enterprise details section +2025-06-27 09:01:21,346 - client_module - DEBUG - client_tab:987 - Processing file upload section +2025-06-27 09:01:21,347 - client_module - DEBUG - client_tab:1159 - Processing enterprise details section +2025-06-27 09:01:21,348 - client_module - DEBUG - client_tab:1197 - Creating client requirements and pain points section +2025-06-27 09:01:21,349 - client_module - DEBUG - client_tab:1202 - Processing client requirements section +2025-06-27 09:01:21,350 - client_module - DEBUG - client_tab:1231 - Client requirements provided: False +2025-06-27 09:01:21,350 - client_module - INFO - client_tab:1246 - Starting COL6 - Client Pain Points section rendering +2025-06-27 09:01:21,351 - client_module - INFO - client_tab:1270 - Using dummy pain points data as fallback +2025-06-27 09:01:21,351 - client_module - DEBUG - client_tab:1280 - Rendering 3 pain point items +2025-06-27 09:01:21,352 - client_module - DEBUG - client_tab:1285 - Processing pain point item 0: Revenue Challenges +2025-06-27 09:01:21,352 - client_module - DEBUG - client_tab:1290 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 09:01:21,353 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 09:01:21,353 - client_module - DEBUG - client_tab:1285 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 09:01:21,353 - client_module - DEBUG - client_tab:1290 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 09:01:21,355 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 09:01:21,355 - client_module - DEBUG - client_tab:1285 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 09:01:21,355 - client_module - DEBUG - client_tab:1290 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 09:01:21,357 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 09:01:21,360 - client_module - INFO - client_tab:1520 - Rendered SPOC name tooltip +2025-06-27 09:01:21,361 - client_module - INFO - client_tab:1533 - SPOC name input rendered with value: +2025-06-27 09:01:21,362 - client_module - INFO - client_tab:1565 - SPOC name provided status: False +2025-06-27 09:01:21,365 - client_module - INFO - client_tab:1577 - Rendered LinkedIn profile tooltip +2025-06-27 09:01:21,366 - client_module - INFO - client_tab:1633 - LinkedIn profile selector in disabled state +2025-06-27 09:01:21,367 - client_module - INFO - client_tab:1714 - Created roles and priorities columns +2025-06-27 09:01:21,368 - client_module - INFO - client_tab:1726 - Rendered SPOC role tooltip +2025-06-27 09:01:21,368 - client_module - INFO - client_tab:1736 - Retrieved 10 default roles +2025-06-27 09:01:21,369 - client_module - INFO - client_tab:1795 - Role selector rendered with selection: Select a role... +2025-06-27 09:01:21,369 - client_module - INFO - client_tab:1810 - Cleared selected target role +2025-06-27 09:01:21,370 - client_module - INFO - client_tab:1823 - Rendered business priorities tooltip +2025-06-27 09:01:21,370 - client_module - INFO - client_tab:1852 - Using 3 business priorities +2025-06-27 09:01:21,372 - client_module - INFO - client_tab:1902 - Created additional requirements columns +2025-06-27 09:01:21,373 - client_module - INFO - client_tab:1914 - Rendered additional client requirements tooltip +2025-06-27 09:01:21,374 - client_module - INFO - client_tab:1929 - Additional requirements text area rendered with 0 characters +2025-06-27 09:01:21,376 - client_module - INFO - client_tab:1945 - Additional requirements provided status: False +2025-06-27 09:01:21,378 - client_module - INFO - client_tab:1959 - Rendered additional specifications tooltip +2025-06-27 09:01:21,378 - client_module - INFO - client_tab:1977 - Using default additional specs items +2025-06-27 09:01:29,271 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:29,272 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:29,272 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:29,274 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:29,274 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:29,274 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:29,275 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:01:29,275 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:01:29,275 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:01:29,276 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 09:01:29,277 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 09:01:29,277 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 09:01:29,278 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 09:01:29,278 - client_module - DEBUG - client_tab:722 - CSS re-applied for persistence +2025-06-27 09:01:29,279 - client_module - DEBUG - client_tab:729 - Creating top section with client name and URLs +2025-06-27 09:01:29,280 - client_module - DEBUG - client_tab:734 - Processing client enterprise name section +2025-06-27 09:01:29,282 - client_module - DEBUG - client_tab:760 - Find URLs button disabled: True +2025-06-27 09:01:29,282 - client_module - DEBUG - client_tab:834 - Processing client website URL section +2025-06-27 09:01:29,284 - client_module - DEBUG - client_tab:851 - Client name provided: False +2025-06-27 09:01:29,284 - client_module - DEBUG - client_tab:858 - URL options count: 1 +2025-06-27 09:01:29,286 - client_module - DEBUG - client_tab:982 - Creating file upload and enterprise details section +2025-06-27 09:01:29,287 - client_module - DEBUG - client_tab:987 - Processing file upload section +2025-06-27 09:01:29,288 - client_module - DEBUG - client_tab:1159 - Processing enterprise details section +2025-06-27 09:01:29,289 - client_module - DEBUG - client_tab:1197 - Creating client requirements and pain points section +2025-06-27 09:01:29,290 - client_module - DEBUG - client_tab:1202 - Processing client requirements section +2025-06-27 09:01:29,291 - client_module - DEBUG - client_tab:1231 - Client requirements provided: False +2025-06-27 09:01:29,291 - client_module - INFO - client_tab:1246 - Starting COL6 - Client Pain Points section rendering +2025-06-27 09:01:29,292 - client_module - INFO - client_tab:1270 - Using dummy pain points data as fallback +2025-06-27 09:01:29,292 - client_module - DEBUG - client_tab:1280 - Rendering 3 pain point items +2025-06-27 09:01:29,293 - client_module - DEBUG - client_tab:1285 - Processing pain point item 0: Revenue Challenges +2025-06-27 09:01:29,293 - client_module - DEBUG - client_tab:1290 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 09:01:29,296 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 09:01:29,296 - client_module - DEBUG - client_tab:1285 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 09:01:29,296 - client_module - DEBUG - client_tab:1290 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 09:01:29,299 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 09:01:29,299 - client_module - DEBUG - client_tab:1285 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 09:01:29,299 - client_module - DEBUG - client_tab:1290 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 09:01:29,305 - client_module - DEBUG - client_tab:1489 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 09:01:29,307 - client_module - INFO - client_tab:1520 - Rendered SPOC name tooltip +2025-06-27 09:01:29,311 - client_module - INFO - client_tab:1533 - SPOC name input rendered with value: +2025-06-27 09:01:29,314 - client_module - INFO - client_tab:1565 - SPOC name provided status: False +2025-06-27 09:01:29,316 - client_module - INFO - client_tab:1577 - Rendered LinkedIn profile tooltip +2025-06-27 09:01:29,319 - client_module - INFO - client_tab:1633 - LinkedIn profile selector in disabled state +2025-06-27 09:01:29,321 - client_module - INFO - client_tab:1714 - Created roles and priorities columns +2025-06-27 09:01:29,321 - client_module - INFO - client_tab:1726 - Rendered SPOC role tooltip +2025-06-27 09:01:29,325 - client_module - INFO - client_tab:1736 - Retrieved 10 default roles +2025-06-27 09:01:29,326 - client_module - INFO - client_tab:1795 - Role selector rendered with selection: Select a role... +2025-06-27 09:01:29,327 - client_module - INFO - client_tab:1810 - Cleared selected target role +2025-06-27 09:01:29,327 - client_module - INFO - client_tab:1823 - Rendered business priorities tooltip +2025-06-27 09:01:29,328 - client_module - INFO - client_tab:1852 - Using 3 business priorities +2025-06-27 09:01:29,333 - client_module - INFO - client_tab:1902 - Created additional requirements columns +2025-06-27 09:01:29,333 - client_module - INFO - client_tab:1914 - Rendered additional client requirements tooltip +2025-06-27 09:01:29,334 - client_module - INFO - client_tab:1929 - Additional requirements text area rendered with 0 characters +2025-06-27 09:01:29,334 - client_module - INFO - client_tab:1945 - Additional requirements provided status: False +2025-06-27 09:01:29,335 - client_module - INFO - client_tab:1959 - Rendered additional specifications tooltip +2025-06-27 09:01:29,335 - client_module - INFO - client_tab:1977 - Using default additional specs items +2025-06-27 09:02:26,850 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:02:26,852 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:02:26,852 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:02:26,855 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:02:26,855 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:02:26,855 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:02:26,857 - client_module - INFO - validate_client_mandatory_fields:685 - Starting client mandatory fields validation +2025-06-27 09:02:26,858 - client_module - DEBUG - validate_client_mandatory_fields:689 - Retrieved client data for validation +2025-06-27 09:02:26,858 - client_module - INFO - validate_client_mandatory_fields:692 - Validation bypassed - returning True +2025-06-27 09:02:26,860 - client_module - INFO - client_tab:706 - Starting client_tab function +2025-06-27 09:02:26,861 - client_module - DEBUG - client_tab:711 - Retrieved client data from dataclass manager +2025-06-27 09:02:26,861 - client_module - DEBUG - client_tab:716 - Applying CSS for the first time +2025-06-27 09:02:26,863 - client_module - INFO - client_tab:719 - CSS applied and updated in client data +2025-06-27 09:02:26,866 - client_module - DEBUG - client_tab:723 - CSS re-applied for persistence +2025-06-27 09:02:26,866 - client_module - DEBUG - client_tab:730 - Creating top section with client name and URLs +2025-06-27 09:02:26,868 - client_module - DEBUG - client_tab:735 - Processing client enterprise name section +2025-06-27 09:02:26,871 - client_module - DEBUG - client_tab:768 - Find URLs button disabled: True +2025-06-27 09:02:26,872 - client_module - DEBUG - client_tab:823 - Processing client website URL section +2025-06-27 09:02:26,876 - client_module - DEBUG - client_tab:840 - Client name provided: False +2025-06-27 09:02:26,877 - client_module - DEBUG - client_tab:847 - URL options count: 1 +2025-06-27 09:02:26,882 - client_module - DEBUG - client_tab:971 - Creating file upload and enterprise details section +2025-06-27 09:02:26,883 - client_module - DEBUG - client_tab:976 - Processing file upload section +2025-06-27 09:02:26,886 - client_module - DEBUG - client_tab:1148 - Processing enterprise details section +2025-06-27 09:02:26,889 - client_module - DEBUG - client_tab:1186 - Creating client requirements and pain points section +2025-06-27 09:02:26,891 - client_module - DEBUG - client_tab:1191 - Processing client requirements section +2025-06-27 09:02:26,896 - client_module - DEBUG - client_tab:1220 - Client requirements provided: False +2025-06-27 09:02:26,897 - client_module - INFO - client_tab:1235 - Starting COL6 - Client Pain Points section rendering +2025-06-27 09:02:26,899 - client_module - INFO - client_tab:1259 - Using dummy pain points data as fallback +2025-06-27 09:02:26,900 - client_module - DEBUG - client_tab:1269 - Rendering 3 pain point items +2025-06-27 09:02:26,900 - client_module - DEBUG - client_tab:1274 - Processing pain point item 0: Revenue Challenges +2025-06-27 09:02:26,900 - client_module - DEBUG - client_tab:1279 - Pain point 'Revenue Challenges' selection status: False +2025-06-27 09:02:26,905 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Revenue Challenges' +2025-06-27 09:02:26,906 - client_module - DEBUG - client_tab:1274 - Processing pain point item 1: Cost and Margin Pressure +2025-06-27 09:02:26,907 - client_module - DEBUG - client_tab:1279 - Pain point 'Cost and Margin Pressure' selection status: False +2025-06-27 09:02:26,910 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Cost and Margin Pressure' +2025-06-27 09:02:26,911 - client_module - DEBUG - client_tab:1274 - Processing pain point item 2: Market Expansion and Customer Acquisition +2025-06-27 09:02:26,911 - client_module - DEBUG - client_tab:1279 - Pain point 'Market Expansion and Customer Acquisition' selection status: False +2025-06-27 09:02:26,914 - client_module - DEBUG - client_tab:1478 - Successfully rendered content box for 'Market Expansion and Customer Acquisition' +2025-06-27 09:02:26,915 - client_module - INFO - client_tab:1509 - Rendered SPOC name tooltip +2025-06-27 09:02:26,919 - client_module - INFO - client_tab:1522 - SPOC name input rendered with value: +2025-06-27 09:02:26,920 - client_module - INFO - client_tab:1554 - SPOC name provided status: False +2025-06-27 09:02:26,922 - client_module - INFO - client_tab:1566 - Rendered LinkedIn profile tooltip +2025-06-27 09:02:26,925 - client_module - INFO - client_tab:1622 - LinkedIn profile selector in disabled state +2025-06-27 09:02:26,927 - client_module - INFO - client_tab:1703 - Created roles and priorities columns +2025-06-27 09:02:26,927 - client_module - INFO - client_tab:1715 - Rendered SPOC role tooltip +2025-06-27 09:02:26,928 - client_module - INFO - client_tab:1725 - Retrieved 10 default roles +2025-06-27 09:02:26,929 - client_module - INFO - client_tab:1784 - Role selector rendered with selection: Select a role... +2025-06-27 09:02:26,929 - client_module - INFO - client_tab:1799 - Cleared selected target role +2025-06-27 09:02:26,929 - client_module - INFO - client_tab:1812 - Rendered business priorities tooltip +2025-06-27 09:02:26,930 - client_module - INFO - client_tab:1841 - Using 3 business priorities +2025-06-27 09:02:26,932 - client_module - INFO - client_tab:1891 - Created additional requirements columns +2025-06-27 09:02:26,933 - client_module - INFO - client_tab:1903 - Rendered additional client requirements tooltip +2025-06-27 09:02:26,935 - client_module - INFO - client_tab:1918 - Additional requirements text area rendered with 0 characters +2025-06-27 09:02:26,935 - client_module - INFO - client_tab:1934 - Additional requirements provided status: False +2025-06-27 09:02:26,937 - client_module - INFO - client_tab:1948 - Rendered additional specifications tooltip +2025-06-27 09:02:26,939 - client_module - INFO - client_tab:1966 - Using default additional specs items diff --git a/GS_Sales_Proposal/main_css.py b/GS_Sales_Proposal/main_css.py new file mode 100644 index 0000000000000000000000000000000000000000..9eb2aa87165328f476ed94e4b93c3988bca159f7 --- /dev/null +++ b/GS_Sales_Proposal/main_css.py @@ -0,0 +1,740 @@ +import streamlit as st + +def add_professional_css(): + """Add professional CSS styling - BLACK AND WHITE THEME""" + st.markdown(""" + + """, unsafe_allow_html=True) + + +app_css = """ + +""" \ No newline at end of file diff --git a/GS_Sales_Proposal/requirements.txt b/GS_Sales_Proposal/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..9b42475ab47e9b1bc494be6e828ae6a83baa0bd8 --- /dev/null +++ b/GS_Sales_Proposal/requirements.txt @@ -0,0 +1,254 @@ +aiofiles==24.1.0 +aiohappyeyeballs==2.6.1 +aiohttp==3.11.18 +aiosignal==1.3.2 +aiosqlite==0.21.0 +altair==5.5.0 +annotated-types==0.7.0 +anyio==4.9.0 +asgiref==3.8.1 +attrs==25.3.0 +authlib==1.5.2 +backcall==0.2.0 +backoff==2.2.1 +bcrypt==4.3.0 +beautifulsoup4==4.13.4 +bleach==6.2.0 +blinker==1.9.0 +brotli==1.1.0 +build==1.2.2.post1 +cachetools==5.5.2 +certifi==2025.4.26 +cffi==1.17.1 +chardet==5.2.0 +charset-normalizer==3.4.2 +chromadb==1.0.12 +click==8.2.0 +colorama==0.4.6 +coloredlogs==15.0.1 +colorthief==0.2.1 +crawl4ai==0.6.3 +cryptography==44.0.3 +cssselect==1.3.0 +cssselect2==0.8.0 +curl-cffi==0.11.1 +dataclasses-json==0.6.7 +defusedxml==0.7.1 +deprecated==1.2.18 +distro==1.9.0 +docopt==0.6.2 +docstring-parser==0.16 +dotenv==0.9.9 +durationpy==0.10 +fake-http-header==0.3.5 +fake-useragent==2.2.0 +fastapi==0.115.9 +fastjsonschema==2.21.1 +filelock==3.18.0 +filetype==1.2.0 +flatbuffers==25.2.10 +fonttools==4.58.2 +frozendict==2.4.6 +frozenlist==1.6.0 +fsspec==2025.5.1 +gitdb==4.0.12 +gitpython==3.1.44 +google-adk==0.5.0 +google-ai-generativelanguage==0.6.15 +google-api-core==2.24.2 +google-api-python-client==2.169.0 +google-auth==2.40.1 +google-auth-httplib2==0.2.0 +google-cloud-aiplatform==1.92.0 +google-cloud-bigquery==3.32.0 +google-cloud-core==2.4.3 +google-cloud-resource-manager==1.14.2 +google-cloud-secret-manager==2.23.3 +google-cloud-speech==2.32.0 +google-cloud-storage==2.19.0 +google-cloud-trace==1.16.1 +google-crc32c==1.7.1 +google-genai==1.15.0 +google-generativeai==0.8.5 +google-resumable-media==2.7.2 +googleapis-common-protos==1.70.0 +grandalf==0.8 +greenlet==3.2.2 +groq==0.28.0 +grpc-google-iam-v1==0.14.2 +grpcio==1.71.0 +grpcio-status==1.71.0 +h11==0.16.0 +httpcore==1.0.9 +httplib2==0.22.0 +httptools==0.6.4 +httpx==0.28.1 +httpx-sse==0.4.0 +huggingface-hub==0.31.2 +humanfriendly==10.0 +humanize==4.12.3 +idna==3.10 +importlib-resources==6.5.2 +ipython==8.12.3 +jinja2==3.1.6 +jiter==0.9.0 +joblib==1.5.1 +jsonpatch==1.33 +jsonpointer==3.0.0 +jsonschema==4.23.0 +jsonschema-specifications==2025.4.1 +jupyterlab-pygments==0.3.0 +kubernetes==33.1.0 +langchain==0.3.25 +langchain-chroma==0.2.4 +langchain-community==0.3.25 +langchain-core==0.3.65 +langchain-google-genai==2.0.10 +langchain-groq==0.3.2 +langchain-huggingface==0.3.0 +langchain-text-splitters==0.3.8 +langgraph==0.4.8 +langgraph-checkpoint==2.0.26 +langgraph-cli==0.3.3 +langgraph-prebuilt==0.2.2 +langgraph-sdk==0.1.70 +langsmith==0.3.45 +litellm==1.69.3 +lxml==5.4.0 +markdown-it-py==3.0.0 +markupsafe==3.0.2 +marshmallow==3.26.1 +mcp==1.8.1 +mdurl==0.1.2 +mistune==3.1.3 +mmh3==5.1.0 +mpmath==1.3.0 +multidict==6.4.3 +multitasking==0.0.11 +mypy-extensions==1.1.0 +narwhals==1.41.0 +nbclient==0.10.2 +nbconvert==7.16.6 +nbformat==5.10.4 +networkx==3.5 +nltk==3.9.1 +numpy==2.2.5 +oauthlib==3.2.2 +onnxruntime==1.22.0 +openai==1.75.0 +opentelemetry-api==1.33.0 +opentelemetry-exporter-gcp-trace==1.9.0 +opentelemetry-exporter-otlp-proto-common==1.33.0 +opentelemetry-exporter-otlp-proto-grpc==1.33.0 +opentelemetry-instrumentation==0.54b0 +opentelemetry-instrumentation-asgi==0.54b0 +opentelemetry-instrumentation-fastapi==0.54b0 +opentelemetry-proto==1.33.0 +opentelemetry-resourcedetector-gcp==1.9.0a0 +opentelemetry-sdk==1.33.0 +opentelemetry-semantic-conventions==0.54b0 +opentelemetry-util-http==0.54b0 +orjson==3.10.18 +ormsgpack==1.10.0 +overrides==7.7.0 +packaging==24.2 +pandas==2.2.3 +pandocfilters==1.5.1 +pdf2image==1.17.0 +pdfkit==1.0.0 +peewee==3.18.1 +pillow==10.4.0 +pip==25.1.1 +pipreqs==0.4.13 +playwright==1.52.0 +posthog==4.10.0 +propcache==0.3.1 +proto-plus==1.26.1 +protobuf==5.29.4 +pyarrow==20.0.0 +pyasn1==0.6.1 +pyasn1-modules==0.4.2 +pycparser==2.22 +pydantic==2.11.4 +pydantic-core==2.33.2 +pydantic-settings==2.9.1 +pydeck==0.9.1 +pydyf==0.11.0 +pyee==13.0.0 +pyopenssl==25.1.0 +pyparsing==3.2.3 +pypdf==5.6.0 +pyperclip==1.9.0 +pyphen==0.17.2 +pypika==0.48.9 +pyproject-hooks==1.2.0 +python-dotenv==1.1.0 +python-graphviz==0.20.3 +python-multipart==0.0.20 +python-pptx==1.0.2 +pytz==2025.2 +pyyaml==6.0.2 +rank-bm25==0.2.2 +referencing==0.36.2 +regex==2024.11.6 +requests==2.32.3 +requests-oauthlib==2.0.0 +requests-toolbelt==1.0.0 +rich==14.0.0 +rpds-py==0.25.0 +rsa==4.9.1 +safetensors==0.5.3 +scikit-learn==1.7.0 +scipy==1.15.3 +sentence-transformers==4.1.0 +setuptools==80.9.0 +shapely==2.1.0 +shellingham==1.5.4 +smmap==5.0.2 +sniffio==1.3.1 +snowballstemmer==2.2.0 +soupsieve==2.7 +sqlalchemy==2.0.41 +sse-starlette==2.3.5 +starlette==0.45.3 +streamlit==1.45.1 +sympy==1.14.0 +tenacity==9.1.2 +tf-playwright-stealth==1.1.2 +threadpoolctl==3.6.0 +tiktoken==0.9.0 +tinycss2==1.4.0 +tinyhtml5==2.0.0 +tokenizers==0.21.1 +toml==0.10.2 +torch==2.7.1 +tqdm==4.67.1 +transformers==4.52.4 +triton==3.3.1 +typer==0.16.0 +typing-extensions==4.14.0 +typing-inspect==0.9.0 +typing-inspection==0.4.0 +tzdata==2025.2 +tzlocal==5.3.1 +uritemplate==4.1.1 +urllib3==2.4.0 +uv==0.7.3 +uvicorn==0.34.2 +uvloop==0.21.0 +watchdog==6.0.0 +watchfiles==1.1.0 +weasyprint==65.1 +webencodings==0.5.1 +websocket-client==1.8.0 +websockets==15.0.1 +wrapt==1.17.2 +xlsxwriter==3.2.3 +xxhash==3.5.0 +yarg==0.1.9 +yarl==1.20.0 +yfinance==0.2.61 +zopfli==0.2.3.post1 +zstandard==0.23.0 +python-docx==1.2.0 +docx2txt==0.9 \ No newline at end of file diff --git a/GS_Sales_Proposal/t.py b/GS_Sales_Proposal/t.py new file mode 100644 index 0000000000000000000000000000000000000000..11021fed7de9c3dacfc4682818432a3ecf829bf9 --- /dev/null +++ b/GS_Sales_Proposal/t.py @@ -0,0 +1,498 @@ +import streamlit as st +from typing import * + +def render_three_column_selector_unified( + # Column configuration - Made wider to fill screen + column_ratio: Tuple[float, float, float] = (2, 2, 2), # Equal wider columns + column_gap: str = "large", # Increased gap for better spacing + + # Left column (text area) configuration + left_title: str = "Client Requirements", + left_tooltip: str = "Define the core client requirements, technical specifications, project scope, deliverables, and expected outcomes. This forms the foundation of your proposal and helps ensure all client needs are addressed.", + left_required: bool = True, + textarea_height: int = 300, # Increased height for better visibility + textarea_placeholder: str = "Enter client name first to enable this field", + textarea_session_key: str = "client_requirements_content", + textarea_widget_key: str = "client_requirements_textarea", + + # Unified right section (middle + right columns) configuration + unified_section_title: str = "Client Pain Points & Options", + unified_section_tooltip: str = "Select from available pain points and options that will be added to your client requirements. These can be extracted from RFI documents or manually entered business challenges.", + + # Session state keys for both sides + middle_selected_items_key: str = "selected_pain_points", + middle_content_map_key: str = "pain_point_content_map", + right_selected_items_key: str = "selected_additional_options", + right_content_map_key: str = "additional_content_map", + + # Single data source that will be displayed in both columns + default_data: Optional[Dict[str, str]] = None, + split_ratio: Tuple[int, int] = (3, 3), # How many items go to middle vs right column + + # Enable/disable conditions + client_enabled_condition: bool = True, + client_name_provided: bool = True, + + # Styling configuration + button_column_width: float = 2.5, # Button width within each column + content_column_width: float = 6.5, # Content area width within each column + show_success_messages: bool = False, + selected_color: str = "#2e7d32", # Green color + selected_border_color: str = "#4caf50", # Green border + unselected_color: str = "#404040", + unselected_border_color: str = "#404040", + text_color: str = "#ffffff", + + # Title styling - Made normal size like left title + title_font_size: str = "18px", # Same as other titles + title_color: str = "#ffffff", + title_margin_bottom: str = "10px" # Reduced margin +) -> Tuple[str, bool]: + """ + Renders a three-column layout with text area on left, and a unified section on right + that spans both middle and right columns under one normal-sized title. + + Returns: + Tuple of (textarea_content, requirements_provided_bool) + """ + + # Default data if none provided + if default_data is None: + default_data = { + "Revenue Challenges": "**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\n• Average deal size decreased by 22% due to increased price competition\n\n", + + "Cost and Margin Pressure": "**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\n• Labor costs rose 18% while productivity remained flat\n• Raw material prices up 25% with limited ability to pass costs to customers\n\n", + + "Market Expansion": "**Market Expansion and Customer Acquisition**\n• Win rate on new business opportunities dropped from 42% to 28%\n• Customer acquisition cost increased 35% while customer lifetime value declined\n• Expansion into new geographic markets yielding only 40% of projected results\n\n", + + "Technology Modernization": "**Technology Modernization**\n• Legacy systems causing 40% slower processing times\n• Integration challenges between disparate systems\n• Security vulnerabilities in outdated infrastructure\n\n", + + "Workforce Development": "**Workforce Development**\n• Skills gap in emerging technologies affecting 60% of teams\n• Employee retention challenges with 25% annual turnover\n• Training programs yielding limited ROI\n\n", + + "Compliance & Risk": "**Compliance & Risk Management**\n• Regulatory compliance gaps creating audit risks\n• Data privacy requirements increasing operational complexity\n• Risk assessment processes outdated and manual\n\n" + } + + # Split the data into two sets + data_items = list(default_data.items()) + middle_count = split_ratio[0] + + # Split data + middle_data = dict(data_items[:middle_count]) + right_data = dict(data_items[middle_count:]) + + # Add CSS for styling + st.markdown(f""" + + """, unsafe_allow_html=True) + + # Initialize session state variables + for key in [middle_selected_items_key, right_selected_items_key]: + if key not in st.session_state: + st.session_state[key] = set() + + for key in [middle_content_map_key, right_content_map_key]: + if key not in st.session_state: + st.session_state[key] = {} + + if textarea_session_key not in st.session_state: + st.session_state[textarea_session_key] = "" + + # Create main THREE COLUMN layout - NO NESTING! + col_left, col_middle, col_right = st.columns(column_ratio, gap=column_gap) + + # LEFT COLUMN - Text Area + with col_left: + # Title with tooltip and required indicator + required_asterisk = ' *' if left_required else '' + st.markdown(f''' +
+ {left_title}{required_asterisk} +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Text area + client_requirements = st.text_area( + label=left_title, + value=st.session_state[textarea_session_key] if client_name_provided else "", + height=textarea_height, + key=textarea_widget_key, + label_visibility="collapsed", + disabled=not client_name_provided, + placeholder=textarea_placeholder if not client_name_provided else "" + ) + + # Update session state when text area changes + if client_name_provided: + st.session_state[textarea_session_key] = client_requirements + + client_requirements_provided = bool(client_name_provided and client_requirements.strip()) + + # MIDDLE COLUMN - First half of items + with col_middle: + # Show unified title only in middle column (spans conceptually) + st.markdown(f''' +
+ {unified_section_title} +
ā“˜
+
+ ''', unsafe_allow_html=True) + # Display middle column items + for i, (key, value) in enumerate(middle_data.items()): + # Check if this item is selected + is_selected = key in st.session_state[middle_selected_items_key] + + # Create button and content sub-columns WITHIN THIS COLUMN + btn_col, content_col = st.columns([1, 4], gap="small") + + with btn_col: + # Button + button_text = "āŒ" if is_selected else "āž•" + button_help = f"Remove '{key}'" if is_selected else f"Add '{key}'" + button_type = "secondary" + + if st.button(button_text, + key=f"toggle_middle_{middle_selected_items_key}_{i}", + help=button_help, + type=button_type, + disabled=not client_enabled_condition, + use_container_width=True): + + if is_selected: + # REMOVE FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + original_content = st.session_state[middle_content_map_key].get(key, value) + + # Remove content patterns + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + updated_content = updated_content.replace(pattern, "") + + # Clean up excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update session state + st.session_state[textarea_session_key] = updated_content + st.session_state[middle_selected_items_key].discard(key) + if key in st.session_state[middle_content_map_key]: + del st.session_state[middle_content_map_key][key] + + if show_success_messages: + st.success(f"šŸ—‘ļø '{key}' removed!") + + else: + # ADD FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + new_content = current_content + f"\n\n{value}" if current_content else value + + # Update session state + st.session_state[textarea_session_key] = new_content + st.session_state[middle_content_map_key][key] = value + st.session_state[middle_selected_items_key].add(key) + + if show_success_messages: + st.success(f"āœ… '{key}' added!") + + st.rerun() + + with content_col: + # Content card + if is_selected: + background_color = selected_color + border_color = selected_border_color + icon = "āœ…" + box_shadow = f"0 3px 8px rgba({int(selected_border_color[1:3], 16)}, {int(selected_border_color[3:5], 16)}, {int(selected_border_color[5:7], 16)}, 0.3)" + else: + background_color = unselected_color + border_color = unselected_border_color + icon = "šŸ“‹" + box_shadow = "0 2px 4px rgba(0,0,0,0.1)" + + # Apply disabled styling if not enabled + current_text_color_final = text_color + if not client_enabled_condition: + background_color = "#666666" + border_color = "#666666" + current_text_color_final = "#999999" + + st.markdown(f""" +
+ {icon} + {key} +
+ """, unsafe_allow_html=True) + + # RIGHT COLUMN - Second half of items + with col_right: + # Empty title space to align with middle column - use exact same height as title + st.markdown(f''' + + ''', unsafe_allow_html=True) + # Display right column items + for i, (key, value) in enumerate(right_data.items()): + # Check if this item is selected + is_selected = key in st.session_state[right_selected_items_key] + + # Create button and content sub-columns WITHIN THIS COLUMN + btn_col, content_col = st.columns([1, 4], gap="small") + + with btn_col: + # Button + button_text = "āŒ" if is_selected else "āž•" + button_help = f"Remove '{key}'" if is_selected else f"Add '{key}'" + button_type = "secondary" + + if st.button(button_text, + key=f"toggle_right_{right_selected_items_key}_{i}", + help=button_help, + type=button_type, + disabled=not client_enabled_condition, + use_container_width=True): + + if is_selected: + # REMOVE FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + original_content = st.session_state[right_content_map_key].get(key, value) + + # Remove content patterns + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + updated_content = updated_content.replace(pattern, "") + + # Clean up excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update session state + st.session_state[textarea_session_key] = updated_content + st.session_state[right_selected_items_key].discard(key) + if key in st.session_state[right_content_map_key]: + del st.session_state[right_content_map_key][key] + + if show_success_messages: + st.success(f"šŸ—‘ļø '{key}' removed!") + + else: + # ADD FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + new_content = current_content + f"\n\n{value}" if current_content else value + + # Update session state + st.session_state[textarea_session_key] = new_content + st.session_state[right_content_map_key][key] = value + st.session_state[right_selected_items_key].add(key) + + if show_success_messages: + st.success(f"āœ… '{key}' added!") + + st.rerun() + + with content_col: + # Content card + if is_selected: + background_color = selected_color + border_color = selected_border_color + icon = "āœ…" + box_shadow = f"0 3px 8px rgba({int(selected_border_color[1:3], 16)}, {int(selected_border_color[3:5], 16)}, {int(selected_border_color[5:7], 16)}, 0.3)" + else: + background_color = unselected_color + border_color = unselected_border_color + icon = "šŸ“‹" + box_shadow = "0 2px 4px rgba(0,0,0,0.1)" + + # Apply disabled styling if not enabled + current_text_color_final = text_color + if not client_enabled_condition: + background_color = "#666666" + border_color = "#666666" + current_text_color_final = "#999999" + + st.markdown(f""" +
+ {icon} + {key} +
+ """, unsafe_allow_html=True) + + return client_requirements, client_requirements_provided + + +# Example usage +def example_usage(): + """Example showing the fixed version""" + + business_challenges = { + "Revenue Decline": "**Revenue Decline** • Q4 revenue down 18% year-over-year\n• Customer acquisition costs increased 45%\n• Average deal size reduced by 30%\n\n", + + "Operational Inefficiency": "**Operational Inefficiency** • Manual processes consuming 60% more time\n• Employee productivity decreased 25%\n• Resource allocation suboptimal\n\n", + + "Market Competition": "**Market Competition** • New competitors capturing 35% market share\n• Price pressure from discount providers\n• Brand recognition declining in key segments\n\n", + + "Technology Gaps": "**Technology Gaps** • Legacy systems causing integration issues\n• Security vulnerabilities identified\n• Scalability limitations affecting growth\n\n", + + "Compliance Issues": "**Compliance Issues** • Regulatory requirements not fully met\n• Audit findings require immediate attention\n• Documentation processes outdated\n\n", + + "Workforce Challenges": "**Workforce Challenges** • High turnover rate at 32%\n• Skills gap in critical areas\n• Remote work productivity concerns\n\n" + } + + requirements, provided = render_three_column_selector_unified( + column_ratio=(2, 2, 2), + column_gap="large", + + left_title="Project Requirements", + textarea_height=200, + + unified_section_title="Business Challenges & Solutions", + unified_section_tooltip="Select relevant business challenges that will be incorporated into your project requirements.", + + default_data=business_challenges, + split_ratio=(3, 3), + + show_success_messages=True, + client_enabled_condition=True, + client_name_provided=True + ) + + return requirements, provided \ No newline at end of file diff --git a/GS_Sales_Proposal/test.py b/GS_Sales_Proposal/test.py new file mode 100644 index 0000000000000000000000000000000000000000..86e0af044879329a7b568a3b47157faaef9f6287 --- /dev/null +++ b/GS_Sales_Proposal/test.py @@ -0,0 +1,1161 @@ +import streamlit as st +from typing import Dict, Optional, Set, Tuple + +def render_two_column_pain_points_section( + # Column configuration + column_ratio: Tuple[float, float] = (1, 1), + column_gap: str = "medium", + + # Left column (text area) configuration + left_title: str = "left_title", + left_tooltip: str = "Define the core client requirements, technical specifications, project scope, deliverables, and expected outcomes. This forms the foundation of your proposal and helps ensure all client needs are addressed.", + left_required: bool = True, + textarea_height: int = 200, + textarea_placeholder: str = "Enter client name first to enable this field", + textarea_session_key: str = "client_requirements_content", + textarea_widget_key: str = "client_requirements_textarea", + + # Right column (pain points) configuration + right_title: str = "Client Pain Points", + right_tooltip: str = "This area displays extracted pain points from RFI documents or website analysis. You can also manually enter client's business challenges, current pain points, and organizational details that will help customize your proposal.", + selected_items_key: str = "selected_pain_points", + content_map_key: str = "pain_point_content_map", + data_source_key: Optional[str] = "rfi_pain_points_items", + + # Enable/disable conditions + client_enabled_condition: bool = True, + client_name_provided: bool = True, + + # Default data + default_data: Optional[Dict[str, str]] = None, + + # Pain points styling + button_column_width: float = 0.5, + content_column_width: float = 9, + show_success_messages: bool = False, + selected_color: str = "#2e7d32", + selected_border_color: str = "#4caf50", + unselected_color: str = "#404040", + unselected_border_color: str = "#404040", + text_color: str = "#ffffff" +) -> Tuple[str, bool]: + + """ + + Renders a two-column section with text area on left and pain points selection on right. + + Args: + column_ratio: Tuple defining the width ratio of left and right columns + column_gap: Gap between columns ("small", "medium", "large") + + left_title: Title for the left column (text area) + left_tooltip: Tooltip text for the left column + left_required: Whether to show asterisk for required field + textarea_height: Height of the text area in pixels + textarea_placeholder: Placeholder text when disabled + textarea_session_key: Session state key for storing text area content + textarea_widget_key: Widget key for the text area + + right_title: Title for the right column (pain points) + right_tooltip: Tooltip text for the right column + selected_items_key: Session state key tracking selected items + content_map_key: Session state key mapping items to content + data_source_key: Session state key containing pain points data + + client_enabled_condition: Whether functionality is enabled + client_name_provided: Whether client name is provided (for textarea) + + default_data: Default pain points data + + button_column_width: Width ratio for pain point buttons + content_column_width: Width ratio for pain point content display + show_success_messages: Whether to show add/remove messages + selected_color: Background color for selected items + selected_border_color: Border color for selected items + unselected_color: Background color for unselected items + unselected_border_color: Border color for unselected items + text_color: Text color for items + + Returns: + Tuple of (textarea_content, requirements_provided_bool) + """ + + # Default pain points data + if default_data is None: + default_data = { + "Revenue Challenges": "**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\n• Average deal size decreased by 22% due to increased price competition\n• Customer churn rate increased to 18%, up from 12% previous year\n• Revenue per customer dropped 8% as clients downgraded service tiers\n• New product launches generated only 60% of projected revenue\n• Seasonal revenue fluctuations creating 40% variance between peak and low periods\n• Pipeline conversion rates fell from 35% to 24% over past 12 months\n\n", + + "Cost and Margin Pressure": "**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\n• Labor costs rose 18% while productivity remained flat\n• Raw material prices up 25% with limited ability to pass costs to customers\n• Operational efficiency decreased by 14% due to outdated processes\n• Procurement costs increased 20% from supplier consolidation issues\n• Technology infrastructure costs grew 30% without proportional business benefits\n• Regulatory compliance expenses added $1.8M in unexpected annual costs\n• Facility and overhead costs up 16% while revenue remained stagnant\n\n", + + "Market Expansion and Customer Acquisition": "**Market Expansion and Customer Acquisition**\n\n • Win rate on new business opportunities dropped from 42% to 28%\n• Customer acquisition cost increased 35% while customer lifetime value declined\n• Expansion into new geographic markets yielding only 40% of projected results\n• Lack of local market knowledge resulting in 60% longer sales cycles\n• Digital marketing campaigns generating 50% fewer qualified leads\n• Competition from new market entrants capturing 25% of target customer segment\n• Limited brand recognition in new markets requiring 3x marketing investment\n• Difficulty penetrating enterprise accounts with average sales cycle extending to 18 months\n\n" + } + + # Initialize session state variables + if selected_items_key not in st.session_state: + st.session_state[selected_items_key] = set() + + if content_map_key not in st.session_state: + st.session_state[content_map_key] = {} + + if textarea_session_key not in st.session_state: + st.session_state[textarea_session_key] = "" + + # Create two columns + col_left, col_right = st.columns(column_ratio, gap=column_gap) + + # LEFT COLUMN - Text Area + with col_left: + # Title with tooltip and required indicator + required_asterisk = ' *' if left_required else '' + st.markdown(f''' +
+ {left_title}{required_asterisk} +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Text area + client_requirements = st.text_area( + label=left_title, + value=st.session_state[textarea_session_key] if client_name_provided else "", + height=textarea_height, + key=textarea_widget_key, + label_visibility="collapsed", + disabled=not client_name_provided, + placeholder=textarea_placeholder if not client_name_provided else "" + ) + + # Update session state when text area changes (only if enabled) + if client_name_provided: + st.session_state[textarea_session_key] = client_requirements + + client_requirements_provided = bool(client_name_provided and client_requirements.strip()) + + # RIGHT COLUMN - Pain Points Selection + with col_right: + # Title with tooltip + st.markdown(f''' +
+ {right_title} +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Get pain points data from session state or use default data + if (client_enabled_condition and + data_source_key and + st.session_state.get(data_source_key)): + pain_points_items = st.session_state[data_source_key] + else: + pain_points_items = default_data + + # Container for pain points items + with st.container(): + # Display pain points items with add/remove buttons + for i, (key, value) in enumerate(pain_points_items.items()): + # Check if this item is selected + is_selected = key in st.session_state[selected_items_key] + + # Create button and content columns + col_button, col_content = st.columns([button_column_width, content_column_width], gap="small") + + with col_button: + # Button styling + st.markdown(""" + + """, unsafe_allow_html=True) + + # Button appearance based on selection state + button_text = "āŒ" if is_selected else "āž•" + button_help = f"Remove '{key}' from {left_title.lower()}" if is_selected else f"Add '{key}' to {left_title.lower()}" + + if st.button(button_text, + key=f"toggle_{selected_items_key}_{i}", + help=button_help, + type="secondary", + disabled=not client_enabled_condition): + + if is_selected: + # REMOVE FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + original_content = st.session_state[content_map_key].get(key, value) + + # Remove content patterns + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + updated_content = updated_content.replace(pattern, "") + + # Clean up excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update session state + st.session_state[textarea_session_key] = updated_content + st.session_state[selected_items_key].discard(key) + if key in st.session_state[content_map_key]: + del st.session_state[content_map_key][key] + + if show_success_messages: + st.success(f"šŸ—‘ļø '{key}' removed!") + + else: + # ADD FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + new_content = current_content + f"\n\n{value}" if current_content else value + + # Update session state + st.session_state[textarea_session_key] = new_content + st.session_state[content_map_key][key] = value + st.session_state[selected_items_key].add(key) + + if show_success_messages: + st.success(f"āœ… '{key}' added!") + + st.rerun() + + with col_content: + # Styling based on selection state + if is_selected: + background_color = selected_color + border_color = selected_border_color + icon = "āœ…" + box_shadow = f"0 2px 8px rgba({int(selected_border_color[1:3], 16)}, {int(selected_border_color[3:5], 16)}, {int(selected_border_color[5:7], 16)}, 0.3)" + else: + background_color = unselected_color + border_color = unselected_border_color + icon = "šŸ“‹" + box_shadow = "0 2px 4px rgba(0,0,0,0.1)" + + # Apply disabled styling if not enabled + if not client_enabled_condition: + background_color = "#666666" + border_color = "#666666" + text_color = "#999999" + + st.markdown(f""" +
+ {icon} {key} +
+ """, unsafe_allow_html=True) + + return client_requirements, client_requirements_provided + + +# Example usage functions +def example_basic_usage(): + """Replace your original col5, col6 code with this single function call""" + + client_requirements, client_requirements_provided = render_two_column_pain_points_section( + client_enabled_condition=True, # Replace with your actual condition + client_name_provided=True, # Replace with your actual condition + show_success_messages=False # Since you had these commented out, + ) + + # Now you can use client_requirements and client_requirements_provided + # just like in your original code + return client_requirements, client_requirements_provided + + +def example_custom_usage(): + """Example with custom configuration""" + + # Custom pain points for different use case + technical_pain_points = { + "Infrastructure Issues": "**Infrastructure Issues**\n• Server downtime increased by 40%\n• Database performance degraded\n\n", + "Security Concerns": "**Security Concerns**\n• Multiple security vulnerabilities identified\n• Compliance issues detected\n\n" + } + + tech_requirements, tech_provided = render_two_column_pain_points_section( + # Column configuration + column_ratio=(1.2, 0.8), # Make left column slightly larger + + # Left column customization + left_title="Technical Requirements", + left_tooltip="Define technical specifications and system requirements", + textarea_session_key="technical_requirements_content", + textarea_widget_key="tech_requirements_textarea", + textarea_height=250, + + # Right column customization + right_title="Technical Pain Points", + selected_items_key="selected_tech_points", + content_map_key="tech_content_map", + data_source_key=None, # Use default_data instead + default_data=technical_pain_points, + + # Styling + selected_color="#1976d2", # Blue theme + selected_border_color="#42a5f5", + show_success_messages=True, + + # Conditions + client_enabled_condition=True, + client_name_provided=True + ) + + return tech_requirements, tech_provided + + +import streamlit as st +from typing import Dict, Optional, Set, Tuple + +def render_three_column_selector( + # Column configuration + column_ratio: Tuple[float, float, float] = (1, 1, 1), + column_gap: str = "medium", + + # Left column (text area) configuration + left_title: str = "Client Requirements", + left_tooltip: str = "Define the core client requirements, technical specifications, project scope, deliverables, and expected outcomes. This forms the foundation of your proposal and helps ensure all client needs are addressed.", + left_required: bool = True, + textarea_height: int = 200, + textarea_placeholder: str = "Enter client name first to enable this field", + textarea_session_key: str = "client_requirements_content", + textarea_widget_key: str = "client_requirements_textarea", + + # Middle column (pain points) configuration + middle_title: str = "Client Pain Points", + middle_tooltip: str = "This area displays extracted pain points from RFI documents or website analysis. You can also manually enter client's business challenges, current pain points, and organizational details that will help customize your proposal.", + middle_selected_items_key: str = "selected_pain_points", + middle_content_map_key: str = "pain_point_content_map", + middle_data_source_key: Optional[str] = "rfi_pain_points_items", + + # Right column (additional options) configuration + right_title: str = "Additional Options", + right_tooltip: str = "Additional configuration options and secondary pain points that can be included in your proposal.", + right_selected_items_key: str = "selected_additional_options", + right_content_map_key: str = "additional_content_map", + right_data_source_key: Optional[str] = "additional_options_items", + + # Enable/disable conditions + client_enabled_condition: bool = True, + client_name_provided: bool = True, + + # Default data + default_pain_points_data: Optional[Dict[str, str]] = None, + default_additional_data: Optional[Dict[str, str]] = None, + + # Pain points styling + button_column_width: float = 0.5, + content_column_width: float = 9, + show_success_messages: bool = False, + selected_color: str = "#2e7d32", # Green color + selected_border_color: str = "#4caf50", # Green border + unselected_color: str = "#404040", + unselected_border_color: str = "#404040", + text_color: str = "#ffffff" +) -> Tuple[str, bool]: + """ + Renders a three-column section with text area on left, pain points in middle, and additional options on right. + + Args: + column_ratio: Tuple defining the width ratio of left, middle, and right columns + column_gap: Gap between columns ("small", "medium", "large") + + left_title: Title for the left column (text area) + left_tooltip: Tooltip text for the left column + left_required: Whether to show asterisk for required field + textarea_height: Height of the text area in pixels + textarea_placeholder: Placeholder text when disabled + textarea_session_key: Session state key for storing text area content + textarea_widget_key: Widget key for the text area + + middle_title: Title for the middle column (pain points) + middle_tooltip: Tooltip text for the middle column + middle_selected_items_key: Session state key tracking selected items + middle_content_map_key: Session state key mapping items to content + middle_data_source_key: Session state key containing pain points data + + right_title: Title for the right column (additional options) + right_tooltip: Tooltip text for the right column + right_selected_items_key: Session state key tracking selected additional items + right_content_map_key: Session state key mapping additional items to content + right_data_source_key: Session state key containing additional options data + + client_enabled_condition: Whether functionality is enabled + client_name_provided: Whether client name is provided (for textarea) + + default_pain_points_data: Default pain points data for middle column + default_additional_data: Default additional options data for right column + + button_column_width: Width ratio for pain point buttons + content_column_width: Width ratio for pain point content display + show_success_messages: Whether to show add/remove messages + selected_color: Background color for selected items (green) + selected_border_color: Border color for selected items (green) + unselected_color: Background color for unselected items + unselected_border_color: Border color for unselected items + text_color: Text color for items + + Returns: + Tuple of (textarea_content, requirements_provided_bool) + """ + + # Default pain points data for middle column + if default_pain_points_data is None: + default_pain_points_data = { + "Revenue Challenges": "**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\n• Average deal size decreased by 22% due to increased price competition\n• Customer churn rate increased to 18%, up from 12% previous year\n• Revenue per customer dropped 8% as clients downgraded service tiers\n• New product launches generated only 60% of projected revenue\n• Seasonal revenue fluctuations creating 40% variance between peak and low periods\n• Pipeline conversion rates fell from 35% to 24% over past 12 months\n\n", + + "Cost and Margin Pressure": "**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\n• Labor costs rose 18% while productivity remained flat\n• Raw material prices up 25% with limited ability to pass costs to customers\n• Operational efficiency decreased by 14% due to outdated processes\n• Procurement costs increased 20% from supplier consolidation issues\n• Technology infrastructure costs grew 30% without proportional business benefits\n• Regulatory compliance expenses added $1.8M in unexpected annual costs\n• Facility and overhead costs up 16% while revenue remained stagnant\n\n", + + "Market Expansion": "**Market Expansion and Customer Acquisition**\n\n • Win rate on new business opportunities dropped from 42% to 28%\n• Customer acquisition cost increased 35% while customer lifetime value declined\n• Expansion into new geographic markets yielding only 40% of projected results\n• Lack of local market knowledge resulting in 60% longer sales cycles\n• Digital marketing campaigns generating 50% fewer qualified leads\n• Competition from new market entrants capturing 25% of target customer segment\n• Limited brand recognition in new markets requiring 3x marketing investment\n• Difficulty penetrating enterprise accounts with average sales cycle extending to 18 months\n\n" + } + + # Default additional options data for right column + if default_additional_data is None: + default_additional_data = { + "Technology Modernization": "**Technology Modernization**\n• Legacy systems causing 40% slower processing times\n• Integration challenges between disparate systems\n• Security vulnerabilities in outdated infrastructure\n• Limited scalability of current technology stack\n• Manual processes requiring 3x more resources\n• Data silos preventing comprehensive reporting\n\n", + + "Workforce Development": "**Workforce Development**\n• Skills gap in emerging technologies affecting 60% of teams\n• Employee retention challenges with 25% annual turnover\n• Training programs yielding limited ROI\n• Remote work productivity concerns\n• Leadership development needs across all levels\n• Change management resistance affecting adoption\n\n", + + "Compliance & Risk": "**Compliance & Risk Management**\n• Regulatory compliance gaps creating audit risks\n• Data privacy requirements increasing operational complexity\n• Risk assessment processes outdated and manual\n• Incident response procedures need updating\n• Third-party vendor risk management insufficient\n• Business continuity planning requires enhancement\n\n" + } + + # Initialize session state variables for middle column + if middle_selected_items_key not in st.session_state: + st.session_state[middle_selected_items_key] = set() + + if middle_content_map_key not in st.session_state: + st.session_state[middle_content_map_key] = {} + + # Initialize session state variables for right column + if right_selected_items_key not in st.session_state: + st.session_state[right_selected_items_key] = set() + + if right_content_map_key not in st.session_state: + st.session_state[right_content_map_key] = {} + + if textarea_session_key not in st.session_state: + st.session_state[textarea_session_key] = "" + + # Create three columns + col_left, col_middle, col_right = st.columns(column_ratio, gap=column_gap) + + # LEFT COLUMN - Text Area + with col_left: + # Title with tooltip and required indicator + required_asterisk = ' *' if left_required else '' + st.markdown(f''' +
+ {left_title}{required_asterisk} +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Text area + client_requirements = st.text_area( + label=left_title, + value=st.session_state[textarea_session_key] if client_name_provided else "", + height=textarea_height, + key=textarea_widget_key, + label_visibility="collapsed", + disabled=not client_name_provided, + placeholder=textarea_placeholder if not client_name_provided else "" + ) + + # Update session state when text area changes (only if enabled) + if client_name_provided: + st.session_state[textarea_session_key] = client_requirements + + client_requirements_provided = bool(client_name_provided and client_requirements.strip()) + + # Helper function to render a selection column + def render_selection_column(title, tooltip, selected_items_key, content_map_key, + data_source_key, default_data, column_prefix, current_text_color): + # Title with tooltip + st.markdown(f''' +
+ {title} +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Get data from session state or use default data + if (client_enabled_condition and + data_source_key and + st.session_state.get(data_source_key)): + items_data = st.session_state[data_source_key] + else: + items_data = default_data + + # Container for items + with st.container(): + # Display items with add/remove buttons + for i, (key, value) in enumerate(items_data.items()): + # Check if this item is selected + is_selected = key in st.session_state[selected_items_key] + + # Create button and content columns + col_button, col_content = st.columns([button_column_width, content_column_width], gap="small") + + with col_button: + # Button styling + st.markdown(""" + + """, unsafe_allow_html=True) + + # Button appearance based on selection state + button_text = "āŒ" if is_selected else "āž•" + button_help = f"Remove '{key}'" if is_selected else f"Add '{key}'" + + if st.button(button_text, + key=f"toggle_{column_prefix}_{selected_items_key}_{i}", + help=button_help, + type="secondary", + disabled=not client_enabled_condition): + + if is_selected: + # REMOVE FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + original_content = st.session_state[content_map_key].get(key, value) + + # Remove content patterns + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + updated_content = updated_content.replace(pattern, "") + + # Clean up excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update session state + st.session_state[textarea_session_key] = updated_content + st.session_state[selected_items_key].discard(key) + if key in st.session_state[content_map_key]: + del st.session_state[content_map_key][key] + + if show_success_messages: + st.success(f"šŸ—‘ļø '{key}' removed!") + + else: + # ADD FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + new_content = current_content + f"\n\n{value}" if current_content else value + + # Update session state + st.session_state[textarea_session_key] = new_content + st.session_state[content_map_key][key] = value + st.session_state[selected_items_key].add(key) + + if show_success_messages: + st.success(f"āœ… '{key}' added!") + + st.rerun() + + with col_content: + # Styling based on selection state + if is_selected: + background_color = selected_color + border_color = selected_border_color + icon = "āœ…" + box_shadow = f"0 2px 8px rgba({int(selected_border_color[1:3], 16)}, {int(selected_border_color[3:5], 16)}, {int(selected_border_color[5:7], 16)}, 0.3)" + else: + background_color = unselected_color + border_color = unselected_border_color + icon = "šŸ“‹" + box_shadow = "0 2px 4px rgba(0,0,0,0.1)" + + # Apply disabled styling if not enabled + current_text_color_final = current_text_color + if not client_enabled_condition: + background_color = "#666666" + border_color = "#666666" + current_text_color_final = "#999999" + + st.markdown(f""" +
+ {icon} {key} +
+ """, unsafe_allow_html=True) + + # MIDDLE COLUMN - Pain Points Selection + with col_middle: + render_selection_column( + middle_title, middle_tooltip, middle_selected_items_key, + middle_content_map_key, middle_data_source_key, + default_pain_points_data, "middle", text_color + ) + + # RIGHT COLUMN - Additional Options Selection + with col_right: + render_selection_column( + right_title, right_tooltip, right_selected_items_key, + right_content_map_key, right_data_source_key, + default_additional_data, "right", text_color + ) + + return client_requirements, client_requirements_provided + + +# Example usage functions +def example_basic_three_column_usage(): + """Basic usage of the three-column selector""" + + client_requirements, client_requirements_provided = render_three_column_selector( + client_enabled_condition=True, + client_name_provided=True, + show_success_messages=False + ) + + return client_requirements, client_requirements_provided + + +def example_custom_three_column_usage(): + """Example with custom configuration for three columns""" + + # Custom pain points for middle column + technical_pain_points = { + "Infrastructure Issues": "**Infrastructure Issues**\n• Server downtime increased by 40%\n• Database performance degraded\n\n", + "Security Concerns": "**Security Concerns**\n• Multiple security vulnerabilities identified\n• Compliance issues detected\n\n" + } + + # Custom additional options for right column + implementation_options = { + "Phased Rollout": "**Phased Implementation**\n• Gradual deployment across departments\n• Risk mitigation through staged approach\n\n", + "Training Program": "**Comprehensive Training**\n• User training and change management\n• Documentation and support materials\n\n" + } + + requirements, provided = render_three_column_selector( + # Column configuration + column_ratio=(1.5, 1, 1), # Make left column larger + + # Left column customization + left_title="Technical Requirements", + left_tooltip="Define technical specifications and system requirements", + textarea_session_key="technical_requirements_content", + textarea_widget_key="tech_requirements_textarea", + textarea_height=250, + + # Middle column customization + middle_title="Technical Pain Points", + middle_selected_items_key="selected_tech_points", + middle_content_map_key="tech_content_map", + middle_data_source_key=None, + default_pain_points_data=technical_pain_points, + + # Right column customization + right_title="Implementation Options", + right_selected_items_key="selected_implementation_options", + right_content_map_key="implementation_content_map", + right_data_source_key=None, + default_additional_data=implementation_options, + + # Styling - Green theme as requested + selected_color="#2e7d32", # Green + selected_border_color="#4caf50", # Light green border + show_success_messages=True, + + # Conditions + client_enabled_condition=True, + client_name_provided=True + ) + + return requirements, provided +def render_three_column_selector( + # Column configuration - Made wider to fill screen + column_ratio: Tuple[float, float, float] = (2, 2, 2), # Equal wider columns + column_gap: str = "large", # Increased gap for better spacing + + # Left column (text area) configuration + left_title: str = "Client Requirements", + left_tooltip: str = "Define the core client requirements, technical specifications, project scope, deliverables, and expected outcomes. This forms the foundation of your proposal and helps ensure all client needs are addressed.", + left_required: bool = True, + textarea_height: int = 300, # Increased height for better visibility + textarea_placeholder: str = "Enter client name first to enable this field", + textarea_session_key: str = "client_requirements_content", + textarea_widget_key: str = "client_requirements_textarea", + + # Middle column (pain points) configuration + middle_title: str = "Client Pain Points", + middle_tooltip: str = "This area displays extracted pain points from RFI documents or website analysis. You can also manually enter client's business challenges, current pain points, and organizational details that will help customize your proposal.", + middle_selected_items_key: str = "selected_pain_points", + middle_content_map_key: str = "pain_point_content_map", + middle_data_source_key: Optional[str] = "rfi_pain_points_items", + + # Right column (additional options) configuration + right_title: str = "Additional Options", + right_tooltip: str = "Additional configuration options and secondary pain points that can be included in your proposal.", + right_selected_items_key: str = "selected_additional_options", + right_content_map_key: str = "additional_content_map", + right_data_source_key: Optional[str] = "additional_options_items", + + # Enable/disable conditions + client_enabled_condition: bool = True, + client_name_provided: bool = True, + + # Default data + default_pain_points_data: Optional[Dict[str, str]] = None, + default_additional_data: Optional[Dict[str, str]] = None, + + # Pain points styling - Adjusted for full width + button_column_width: float = 1.2, # Wider buttons + content_column_width: float = 8, # Wider content area + show_success_messages: bool = False, + selected_color: str = "#2e7d32", # Green color + selected_border_color: str = "#4caf50", # Green border + unselected_color: str = "#404040", + unselected_border_color: str = "#404040", + text_color: str = "#ffffff" +) -> Tuple[str, bool]: + """ + Renders a full-width three-column section with text area on left, pain points in middle, and additional options on right. + + Args: + column_ratio: Tuple defining the width ratio of left, middle, and right columns (default: wider columns) + column_gap: Gap between columns ("small", "medium", "large") - default "large" for better spacing + + left_title: Title for the left column (text area) + left_tooltip: Tooltip text for the left column + left_required: Whether to show asterisk for required field + textarea_height: Height of the text area in pixels (increased default) + textarea_placeholder: Placeholder text when disabled + textarea_session_key: Session state key for storing text area content + textarea_widget_key: Widget key for the text area + + middle_title: Title for the middle column (pain points) + middle_tooltip: Tooltip text for the middle column + middle_selected_items_key: Session state key tracking selected items + middle_content_map_key: Session state key mapping items to content + middle_data_source_key: Session state key containing pain points data + + right_title: Title for the right column (additional options) + right_tooltip: Tooltip text for the right column + right_selected_items_key: Session state key tracking selected additional items + right_content_map_key: Session state key mapping additional items to content + right_data_source_key: Session state key containing additional options data + + client_enabled_condition: Whether functionality is enabled + client_name_provided: Whether client name is provided (for textarea) + + default_pain_points_data: Default pain points data for middle column + default_additional_data: Default additional options data for right column + + button_column_width: Width ratio for pain point buttons (wider default) + content_column_width: Width ratio for pain point content display (wider default) + show_success_messages: Whether to show add/remove messages + selected_color: Background color for selected items (green) + selected_border_color: Border color for selected items (green) + unselected_color: Background color for unselected items + unselected_border_color: Border color for unselected items + text_color: Text color for items + + Returns: + Tuple of (textarea_content, requirements_provided_bool) + """ + + # Add CSS for full-width styling + st.markdown(""" + + """, unsafe_allow_html=True) + + # Default pain points data for middle column + if default_pain_points_data is None: + default_pain_points_data = { + "Revenue Challenges": "**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\n• Average deal size decreased by 22% due to increased price competition\n• Customer churn rate increased to 18%, up from 12% previous year\n• Revenue per customer dropped 8% as clients downgraded service tiers\n• New product launches generated only 60% of projected revenue\n• Seasonal revenue fluctuations creating 40% variance between peak and low periods\n• Pipeline conversion rates fell from 35% to 24% over past 12 months\n\n", + + "Cost and Margin Pressure": "**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\n• Labor costs rose 18% while productivity remained flat\n• Raw material prices up 25% with limited ability to pass costs to customers\n• Operational efficiency decreased by 14% due to outdated processes\n• Procurement costs increased 20% from supplier consolidation issues\n• Technology infrastructure costs grew 30% without proportional business benefits\n• Regulatory compliance expenses added $1.8M in unexpected annual costs\n• Facility and overhead costs up 16% while revenue remained stagnant\n\n", + + "Market Expansion": "**Market Expansion and Customer Acquisition**\n\n • Win rate on new business opportunities dropped from 42% to 28%\n• Customer acquisition cost increased 35% while customer lifetime value declined\n• Expansion into new geographic markets yielding only 40% of projected results\n• Lack of local market knowledge resulting in 60% longer sales cycles\n• Digital marketing campaigns generating 50% fewer qualified leads\n• Competition from new market entrants capturing 25% of target customer segment\n• Limited brand recognition in new markets requiring 3x marketing investment\n• Difficulty penetrating enterprise accounts with average sales cycle extending to 18 months\n\n" + } + + # Default additional options data for right column + if default_additional_data is None: + default_additional_data = { + "Technology Modernization": "**Technology Modernization**\n• Legacy systems causing 40% slower processing times\n• Integration challenges between disparate systems\n• Security vulnerabilities in outdated infrastructure\n• Limited scalability of current technology stack\n• Manual processes requiring 3x more resources\n• Data silos preventing comprehensive reporting\n\n", + + "Workforce Development": "**Workforce Development**\n• Skills gap in emerging technologies affecting 60% of teams\n• Employee retention challenges with 25% annual turnover\n• Training programs yielding limited ROI\n• Remote work productivity concerns\n• Leadership development needs across all levels\n• Change management resistance affecting adoption\n\n", + + "Compliance & Risk": "**Compliance & Risk Management**\n• Regulatory compliance gaps creating audit risks\n• Data privacy requirements increasing operational complexity\n• Risk assessment processes outdated and manual\n• Incident response procedures need updating\n• Third-party vendor risk management insufficient\n• Business continuity planning requires enhancement\n\n" + } + + # Initialize session state variables for middle column + if middle_selected_items_key not in st.session_state: + st.session_state[middle_selected_items_key] = set() + + if middle_content_map_key not in st.session_state: + st.session_state[middle_content_map_key] = {} + + # Initialize session state variables for right column + if right_selected_items_key not in st.session_state: + st.session_state[right_selected_items_key] = set() + + if right_content_map_key not in st.session_state: + st.session_state[right_content_map_key] = {} + + if textarea_session_key not in st.session_state: + st.session_state[textarea_session_key] = "" + + # Create three columns with full width + col_left, col_middle, col_right = st.columns(column_ratio, gap=column_gap) + + # LEFT COLUMN - Text Area (Full Width) + with col_left: + # Title with tooltip and required indicator + required_asterisk = ' *' if left_required else '' + st.markdown(f''' +
+ {left_title}{required_asterisk} +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Text area with full width + client_requirements = st.text_area( + label=left_title, + value=st.session_state[textarea_session_key] if client_name_provided else "", + height=textarea_height, + key=textarea_widget_key, + label_visibility="collapsed", + disabled=not client_name_provided, + placeholder=textarea_placeholder if not client_name_provided else "" + ) + + # Update session state when text area changes (only if enabled) + if client_name_provided: + st.session_state[textarea_session_key] = client_requirements + + client_requirements_provided = bool(client_name_provided and client_requirements.strip()) + + # Helper function to render a selection column with full width + def render_selection_column(title, tooltip, selected_items_key, content_map_key, + data_source_key, default_data, column_prefix, current_text_color): + # Title with tooltip (full width) + st.markdown(f''' +
+ {title} +
ā“˜
+
+ ''', unsafe_allow_html=True) + + # Get data from session state or use default data + if (client_enabled_condition and + data_source_key and + st.session_state.get(data_source_key)): + items_data = st.session_state[data_source_key] + else: + items_data = default_data + + # Container for items (full width) + with st.container(): + # Display items with add/remove buttons (full width) + for i, (key, value) in enumerate(items_data.items()): + # Check if this item is selected + is_selected = key in st.session_state[selected_items_key] + + # Create button and content columns with wider proportions + col_button, col_content = st.columns([button_column_width, content_column_width], gap="medium") + + with col_button: + # Button appearance based on selection state + button_text = "āŒ REMOVE" if is_selected else "āž• ADD" + button_help = f"Remove '{key}'" if is_selected else f"Add '{key}'" + button_type = "primary" if is_selected else "secondary" + + if st.button(button_text, + key=f"toggle_{column_prefix}_{selected_items_key}_{i}", + help=button_help, + type=button_type, + disabled=not client_enabled_condition, + use_container_width=True): # Full width button + + if is_selected: + # REMOVE FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + original_content = st.session_state[content_map_key].get(key, value) + + # Remove content patterns + patterns_to_remove = [ + f"\n\n{original_content}", + f"{original_content}\n\n", + original_content + ] + + updated_content = current_content + for pattern in patterns_to_remove: + updated_content = updated_content.replace(pattern, "") + + # Clean up excessive newlines + updated_content = '\n\n'.join([section.strip() for section in updated_content.split('\n\n') if section.strip()]) + + # Update session state + st.session_state[textarea_session_key] = updated_content + st.session_state[selected_items_key].discard(key) + if key in st.session_state[content_map_key]: + del st.session_state[content_map_key][key] + + if show_success_messages: + st.success(f"šŸ—‘ļø '{key}' removed!") + + else: + # ADD FUNCTIONALITY + current_content = st.session_state.get(textarea_session_key, '') + new_content = current_content + f"\n\n{value}" if current_content else value + + # Update session state + st.session_state[textarea_session_key] = new_content + st.session_state[content_map_key][key] = value + st.session_state[selected_items_key].add(key) + + if show_success_messages: + st.success(f"āœ… '{key}' added!") + + st.rerun() + + with col_content: + # Styling based on selection state + if is_selected: + background_color = selected_color + border_color = selected_border_color + icon = "āœ…" + box_shadow = f"0 4px 12px rgba({int(selected_border_color[1:3], 16)}, {int(selected_border_color[3:5], 16)}, {int(selected_border_color[5:7], 16)}, 0.4)" + else: + background_color = unselected_color + border_color = unselected_border_color + icon = "šŸ“‹" + box_shadow = "0 2px 6px rgba(0,0,0,0.15)" + + # Apply disabled styling if not enabled + current_text_color_final = current_text_color + if not client_enabled_condition: + background_color = "#666666" + border_color = "#666666" + current_text_color_final = "#999999" + + st.markdown(f""" +
+ {icon} + {key} +
+ """, unsafe_allow_html=True) + + # MIDDLE COLUMN - Pain Points Selection (Full Width) + with col_middle: + render_selection_column( + middle_title, middle_tooltip, middle_selected_items_key, + middle_content_map_key, middle_data_source_key, + default_pain_points_data, "middle", text_color + ) + + # RIGHT COLUMN - Additional Options Selection (Full Width) + with col_right: + render_selection_column( + right_title, right_tooltip, right_selected_items_key, + right_content_map_key, right_data_source_key, + default_additional_data, "right", text_color + ) + + return client_requirements, client_requirements_provided + + +# Example usage functions with full-width configuration +def example_full_width_three_column_usage(): + """Full-width usage of the three-column selector""" + + client_requirements, client_requirements_provided = render_three_column_selector( + # Full width configuration + column_ratio=(2, 2, 2), # Equal wide columns + column_gap="large", # Larger gaps for better spacing + textarea_height=350, # Taller textarea + button_column_width=1.5, # Wider buttons + content_column_width=7, # Wider content + + client_enabled_condition=True, + client_name_provided=True, + show_success_messages=True + ) + + return client_requirements, client_requirements_provided + + +def example_custom_full_width_usage(): + """Example with custom configuration for full-width three columns""" + + # Custom pain points for middle column + technical_pain_points = { + "Infrastructure Issues": "**Infrastructure Issues**\n• Server downtime increased by 40%\n• Database performance degraded\n\n", + "Security Concerns": "**Security Concerns**\n• Multiple security vulnerabilities identified\n• Compliance issues detected\n\n" + } + + # Custom additional options for right column + implementation_options = { + "Phased Rollout": "**Phased Implementation**\n• Gradual deployment across departments\n• Risk mitigation through staged approach\n\n", + "Training Program": "**Comprehensive Training**\n• User training and change management\n• Documentation and support materials\n\n" + } + + requirements, provided = render_three_column_selector( + # Full-width column configuration + column_ratio=(2.5, 2, 2), # Make left column even larger + column_gap="large", + + # Left column customization + left_title="Technical Requirements", + left_tooltip="Define technical specifications and system requirements", + textarea_session_key="technical_requirements_content", + textarea_widget_key="tech_requirements_textarea", + textarea_height=400, # Even taller + + # Middle column customization + middle_title="Technical Pain Points", + middle_selected_items_key="selected_tech_points", + middle_content_map_key="tech_content_map", + middle_data_source_key=None, + default_pain_points_data=technical_pain_points, + + # Right column customization + right_title="Implementation Options", + right_selected_items_key="selected_implementation_options", + right_content_map_key="implementation_content_map", + right_data_source_key=None, + default_additional_data=implementation_options, + + # Full-width styling + button_column_width=1, # Even wider buttons + content_column_width=10, # Wider content area + selected_color="#2e7d32", # Green + selected_border_color="#4caf50", # Light green border + show_success_messages=True, + + # Conditions + client_enabled_condition=True, + client_name_provided=True + ) + + return requirements, provided +render_three_column_selector() \ No newline at end of file diff --git a/GS_Sales_Proposal/trial.ipynb b/GS_Sales_Proposal/trial.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..8813394a803aee867975cadfab2b0578b2a95b3c --- /dev/null +++ b/GS_Sales_Proposal/trial.ipynb @@ -0,0 +1,2307 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"---\\n### āœ… Refined Client Requirements\\n\\n**Project Title:** Growth Acceleration Program for [Client Name]\\n\\n**Project Goal:** To achieve a minimum 10% year-over-year increase in sales revenue within the next 12 months. This will be measured by comparing Q4 2024 revenue against Q4 2023 revenue.\\n\\n**Project Scope:** This project will encompass a comprehensive review of [Client Name]'s current Go-to-Market (GTM) strategy, including but not limited to: market analysis, competitive landscape assessment, sales process optimization, lead generation strategies, sales enablement, and marketing alignment. Growth Sutra will leverage its expertise in GTM strategy development and implementation to deliver tailored solutions.\\n\\n**Deliverables:**\\n\\n* **Phase 1 (Months 1-3):** GTM Strategy Audit & Roadmap Development – Comprehensive assessment of existing GTM processes, identification of key areas for improvement, and development of a detailed roadmap with prioritized initiatives. Deliverables include a detailed audit report, a prioritized roadmap, and a presentation of key findings and recommendations.\\n* **Phase 2 (Months 4-9):** Implementation & Optimization – Execution of the prioritized initiatives outlined in the roadmap, including sales training, marketing campaign development and execution, and sales process optimization. Regular progress reports and stakeholder meetings will be held.\\n* **Phase 3 (Months 10-12):** Performance Measurement & Refinement – Analysis of results against the defined goals, identification of areas for further optimization, and final report summarizing achievements and recommendations for sustained growth.\\n\\n**Timeline:** 12 months\\n\\n**Measurable Outcomes:** A minimum 10% increase in year-over-year sales revenue (Q4 2024 vs. Q4 2023), demonstrable improvements in key sales metrics (e.g., conversion rates, average deal size, sales cycle length), and enhanced sales team performance.\\n\\n**Success Criteria:** Successful completion of all phases as outlined above, achieving the targeted 10% sales increase, and demonstrable improvements in key sales metrics.\\n\\n\\n---\\n\\n### šŸ’” Innovative Suggestions\\n\\n- **Predictive Analytics & Sales Forecasting:** Integrate predictive analytics to forecast future sales performance based on historical data, market trends, and planned initiatives. This will allow for proactive adjustments to the GTM strategy and more accurate resource allocation, maximizing ROI. Rationale: Data-driven decision-making leads to improved accuracy and efficiency.\\n\\n- **Account-Based Marketing (ABM) Implementation:** Implement an ABM strategy targeting high-value accounts. This highly personalized approach will focus resources on key prospects, increasing conversion rates and average deal size. Rationale: ABM offers higher ROI than broad-based marketing efforts, particularly for B2B clients.\\n\\n\\n---\\n\\n### šŸ“Œ Best Practice Recommendations\\n\\n- **Missing Aspects:** The initial client requirement lacked crucial details such as scope, timeline, deliverables, and measurable outcomes. These elements are essential for defining a clear project plan and ensuring successful execution.\\n\\n- **Formatting & Phrasing:** The refined requirements utilize a more professional and structured format, incorporating clear headings, bullet points, and quantifiable goals. This enhances clarity and facilitates effective communication with the client. We've also moved beyond simply stating a 10% increase and defined the measurement period (year-over-year, comparing Q4 to Q4) for clarity and accountability. This is a best practice for avoiding ambiguity in project scope.\"" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from Recommendation.recommendation_utils import get_ai_client_requirements\n", + "\n", + "get_ai_client_requirements(\"Growth sutra is a LLP company knwon for GTM strategies\",\"lient want to increase his sales by 10%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shreyank/anaconda3/envs/adk/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{'title': 'Sustainable Revenue Growth & Market Share Expansion', 'icon': 'šŸ“ˆ'}, {'title': 'Digital Transformation & Operational Excellence', 'icon': 'āš™ļø'}, {'title': 'Talent Acquisition & Retention in a Competitive Market', 'icon': 'šŸ§‘\\u200dšŸ’¼'}]\n" + ] + } + ], + "source": [ + "from Recommendation.recommendation_utils import get_ai_business_priorities\n", + "\n", + "result = get_ai_business_priorities(\"CEO\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(result)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import streamlit as st\n", + "import pandas as pd\n", + "import os\n", + "import logging\n", + "from typing import List\n", + "from .client_utils import *\n", + "import threading\n", + "import time\n", + "from Search.Linkedin.linkedin_serp import *\n", + "from Recommendation.recommendation_utils import *\n", + "from .client_css import *\n", + "from .client_dataclass import ClientData, ClientDataManager\n", + "\n", + "# Configure logging\n", + "def setup_logging():\n", + " \"\"\"Setup logging configuration for client module\"\"\"\n", + " try:\n", + " # Create logs directory if it doesn't exist\n", + " logs_dir = \"logs\"\n", + " if not os.path.exists(logs_dir):\n", + " os.makedirs(logs_dir)\n", + " \n", + " # Configure logger\n", + " logger = logging.getLogger('client_module')\n", + " logger.setLevel(logging.DEBUG)\n", + " \n", + " # Remove existing handlers to avoid duplicates\n", + " for handler in logger.handlers[:]:\n", + " logger.removeHandler(handler)\n", + " \n", + " # Create file handler\n", + " file_handler = logging.FileHandler(os.path.join(logs_dir, 'client_logs.log'))\n", + " file_handler.setLevel(logging.DEBUG)\n", + " \n", + " # Create console handler\n", + " console_handler = logging.StreamHandler()\n", + " console_handler.setLevel(logging.INFO)\n", + " \n", + " # Create formatter\n", + " formatter = logging.Formatter(\n", + " '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'\n", + " )\n", + " file_handler.setFormatter(formatter)\n", + " console_handler.setFormatter(formatter)\n", + " \n", + " # Add handlers to logger\n", + " logger.addHandler(file_handler)\n", + " logger.addHandler(console_handler)\n", + " \n", + " return logger\n", + " except Exception as e:\n", + " print(f\"Error setting up logging: {str(e)}\")\n", + " return logging.getLogger('client_module')\n", + "\n", + "# Initialize logger\n", + "logger = setup_logging()\n", + "\n", + "def save_uploaded_file_and_get_path(uploaded_file):\n", + " \"\"\"Save uploaded file to a temporary directory and return the file path\"\"\"\n", + " logger.info(f\"Starting file upload process for file: {uploaded_file.name if uploaded_file else 'None'}\")\n", + " \n", + " try:\n", + " if uploaded_file is not None:\n", + " logger.debug(f\"File details - Name: {uploaded_file.name}, Size: {uploaded_file.size} bytes\")\n", + " \n", + " # Create uploads directory if it doesn't exist\n", + " upload_dir = os.getenv(\"FILE_SAVE_PATH\")\n", + " logger.debug(f\"Upload directory path: {upload_dir}\")\n", + " \n", + " if not os.path.exists(upload_dir):\n", + " try:\n", + " os.makedirs(upload_dir)\n", + " logger.info(f\"Created upload directory: {upload_dir}\")\n", + " except OSError as e:\n", + " logger.error(f\"Failed to create upload directory {upload_dir}: {str(e)}\")\n", + " raise\n", + " \n", + " # Create file path\n", + " file_path = os.path.join(upload_dir, uploaded_file.name)\n", + " logger.debug(f\"Full file path: {file_path}\")\n", + " \n", + " # Save the file\n", + " try:\n", + " with open(file_path, \"wb\") as f:\n", + " f.write(uploaded_file.getbuffer())\n", + " logger.info(f\"Successfully saved file to: {file_path}\")\n", + " return file_path\n", + " except IOError as e:\n", + " logger.error(f\"Failed to save file {file_path}: {str(e)}\")\n", + " raise\n", + " \n", + " else:\n", + " logger.warning(\"No file provided for upload\")\n", + " return None\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Unexpected error in save_uploaded_file_and_get_path: {str(e)}\")\n", + " raise\n", + "\n", + "\n", + "def validate_client_mandatory_fields():\n", + " \"\"\"Validate client mandatory fields using dataclass\"\"\"\n", + " logger.info(\"Starting client mandatory fields validation\")\n", + " \n", + " try:\n", + " client_data = ClientDataManager.get_client_data()\n", + " logger.debug(\"Retrieved client data for validation\")\n", + " \n", + " # Temporarily return True - validation disabled\n", + " logger.info(\"Validation bypassed - returning True\")\n", + " return True\n", + " \n", + " # Uncomment below for actual validation\n", + " # result = client_data.validate_mandatory_fields()\n", + " # logger.info(f\"Validation result: {result}\")\n", + " # return result\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in validate_client_mandatory_fields: {str(e)}\")\n", + " return False\n", + "\n", + "\n", + "def client_tab(st):\n", + " logger.info(\"Starting client_tab function\")\n", + " \n", + " try:\n", + " # Get client data from dataclass manager\n", + " client_data = ClientDataManager.get_client_data()\n", + " logger.debug(\"Retrieved client data from dataclass manager\")\n", + " \n", + " # Apply CSS only once\n", + " try:\n", + " if not client_data.css_applied:\n", + " logger.debug(\"Applying CSS for the first time\")\n", + " st.markdown(client_css, unsafe_allow_html=True)\n", + " ClientDataManager.update_client_data(css_applied=True)\n", + " logger.info(\"CSS applied and updated in client data\")\n", + " \n", + " # Re-apply CSS after every rerun to ensure persistence\n", + " st.markdown(client_css, unsafe_allow_html=True)\n", + " logger.debug(\"CSS re-applied for persistence\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error applying CSS: {str(e)}\")\n", + " st.error(\"Error loading page styles\")\n", + " \n", + " # Top section with client name and URLs\n", + " logger.debug(\"Creating top section with client name and URLs\")\n", + " col1, col2 = st.columns([1, 1])\n", + " \n", + " with col1:\n", + " try:\n", + " logger.debug(\"Processing client enterprise name section\")\n", + " \n", + " st.markdown(\"\"\"\n", + "
\n", + " Client Enterprise Name *\n", + "
ā“˜
\n", + "
\n", + " \"\"\", unsafe_allow_html=True)\n", + " \n", + " # Create a sub-column layout for name input and find URLs button\n", + " name_col, button_col = st.columns([3, 1])\n", + " \n", + " with name_col:\n", + " client_enterprise_name = st.text_input(\n", + " label=\"Client Enterprise Name\", \n", + " value=client_data.enterprise_name,\n", + " placeholder=\"Enter client enterprise name...\", \n", + " key=\"client_enterprise_name_input\",\n", + " label_visibility=\"collapsed\",\n", + " )\n", + " \n", + " # Update dataclass when input changes\n", + " if client_enterprise_name != client_data.enterprise_name:\n", + " try:\n", + " ClientDataManager.update_client_data(enterprise_name=client_enterprise_name)\n", + " logger.info(f\"Updated enterprise name: {client_enterprise_name}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating enterprise name: {str(e)}\")\n", + " \n", + " with button_col:\n", + " try:\n", + " # Find URLs button - only enabled when client name has more than 2 characters\n", + " find_urls_disabled = not (client_enterprise_name and len(client_enterprise_name.strip()) > 2)\n", + " logger.debug(f\"Find URLs button disabled: {find_urls_disabled}\")\n", + " \n", + " if st.button(\"šŸ” Find Website\",\n", + " disabled=find_urls_disabled,\n", + " help=\"Find website URLs for this company\",\n", + " key=\"find_urls_button\",\n", + " type=\"secondary\"):\n", + " \n", + " logger.info(f\"Find URLs button clicked for: {client_enterprise_name.strip()}\")\n", + " \n", + " # Add spinner while fetching URLs\n", + " with st.spinner(f\"Finding Websites for '{client_enterprise_name.strip()}'...\"):\n", + " try:\n", + " urls_list = get_urls_list(client_enterprise_name.strip())\n", + " logger.info(f\"Found {len(urls_list)} URLs for {client_enterprise_name.strip()}\")\n", + " \n", + " ClientDataManager.update_client_data(\n", + " website_urls_list=urls_list,\n", + " enterprise_name=client_enterprise_name\n", + " )\n", + " logger.debug(\"Updated client data with URLs list\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error finding URLs for {client_enterprise_name.strip()}: {str(e)}\")\n", + " ClientDataManager.update_client_data(website_urls_list=[])\n", + " st.error(f\"Error finding URLs: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in Find URLs button section: {str(e)}\")\n", + " \n", + " # Clear URLs if company name is cleared\n", + " try:\n", + " if not client_enterprise_name and client_data.enterprise_name:\n", + " logger.info(\"Company name cleared, clearing URLs list\")\n", + " ClientDataManager.update_client_data(\n", + " website_urls_list=[],\n", + " enterprise_name=\"\"\n", + " )\n", + " except Exception as e:\n", + " logger.error(f\"Error clearing URLs when company name cleared: {str(e)}\")\n", + " \n", + " # Show validation warning if triggered and field is empty\n", + " try:\n", + " if client_data.show_validation and check_field_validation(\"Client Enterprise Name\", client_enterprise_name, True):\n", + " show_field_warning(\"Client Enterprise Name\")\n", + " logger.debug(\"Showed validation warning for Client Enterprise Name\")\n", + " except Exception as e:\n", + " logger.error(f\"Error showing validation warning: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in client enterprise name column: {str(e)}\")\n", + " st.error(\"Error in client name section\")\n", + " \n", + " with col2:\n", + " try:\n", + " logger.debug(\"Processing client website URL section\")\n", + " \n", + " # Label row with inline emoji and tooltip\n", + " st.markdown('''\n", + "
\n", + " Client Website URL\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " # Create columns for dropdown and buttons\n", + " url_col, btn1_col, btn2_col, btn3_col = st.columns([7, 1, 1, 1])\n", + " \n", + " with url_col:\n", + " try:\n", + " # URL selection logic\n", + " client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip())\n", + " logger.debug(f\"Client name provided: {client_name_provided}\")\n", + " \n", + " if not client_data.website_urls_list:\n", + " url_options = [\"Select client website URL\"]\n", + " else:\n", + " url_options = [\"Select client website URL\"] + client_data.website_urls_list\n", + " \n", + " logger.debug(f\"URL options count: {len(url_options)}\")\n", + " \n", + " # Set default selection\n", + " default_index = 0\n", + " if client_data.website_url and client_data.website_url in url_options:\n", + " default_index = url_options.index(client_data.website_url)\n", + " \n", + " client_website_url = st.selectbox(\n", + " label=\"Client Website URL\",\n", + " options=url_options,\n", + " index=default_index,\n", + " key=\"client_website_url_selector\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided,\n", + " accept_new_options=True\n", + " )\n", + " \n", + " # Reset to empty string if default option is selected\n", + " if client_website_url == \"Select client website URL\":\n", + " client_website_url = \"\"\n", + " \n", + " # Update dataclass when URL changes\n", + " if client_website_url != client_data.website_url:\n", + " try:\n", + " ClientDataManager.update_client_data(website_url=client_website_url)\n", + " logger.info(f\"Updated website URL: {client_website_url}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating website URL: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in URL selection: {str(e)}\")\n", + " client_website_url = \"\"\n", + " \n", + " # Buttons for website actions\n", + " with btn1_col:\n", + " try:\n", + " if client_website_url:\n", + " st.link_button(\"🌐\", client_website_url, help=\"Visit website\", use_container_width=True)\n", + " else:\n", + " st.button(\"🌐\", help=\"Visit website\", disabled=True, use_container_width=True)\n", + " except Exception as e:\n", + " logger.error(f\"Error creating visit website button: {str(e)}\")\n", + " \n", + " with btn2_col:\n", + " try:\n", + " refresh_clicked = st.button(\"šŸ”„\", help=\"Refresh website URLs list\", key=\"refresh_urls_btn\", \n", + " use_container_width=True, disabled=not client_website_url)\n", + " except Exception as e:\n", + " logger.error(f\"Error creating refresh button: {str(e)}\")\n", + " refresh_clicked = False\n", + " \n", + " with btn3_col:\n", + " try:\n", + " scrape_clicked = st.button(\"šŸ“‘\", help=\"Get enterprise details\", key=\"scrape_website_btn\", \n", + " use_container_width=True, disabled=not client_website_url)\n", + " \n", + " if scrape_clicked and client_website_url:\n", + " logger.info(f\"Scrape button clicked for URL: {client_website_url}\")\n", + " ClientDataManager.update_client_data(\n", + " pending_scrape_url=client_website_url,\n", + " scraping_in_progress=True\n", + " )\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error creating scrape button: {str(e)}\")\n", + " scrape_clicked = False\n", + "\n", + " # Handle refresh action\n", + " if refresh_clicked and client_name_provided:\n", + " try:\n", + " logger.info(f\"Refreshing URLs for: {client_enterprise_name}\")\n", + " with st.spinner(\"Refreshing website URLs...\"):\n", + " urls_list = get_urls_list(client_enterprise_name)\n", + " ClientDataManager.update_client_data(website_urls_list=urls_list)\n", + " logger.info(f\"Successfully refreshed URLs, found {len(urls_list)} URLs\")\n", + " st.success(\"Website URLs refreshed!\")\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error refreshing URLs: {str(e)}\")\n", + " st.error(f\"Error refreshing URLs: {str(e)}\")\n", + "\n", + " # Handle pending scraping operation\n", + " if client_data.scraping_in_progress and client_data.pending_scrape_url:\n", + " try:\n", + " logger.info(f\"Starting website scraping for: {client_data.pending_scrape_url}\")\n", + " with st.spinner(f\"Scraping website details from {client_data.pending_scrape_url}...\"):\n", + " try:\n", + " website_details = get_url_details(client_data.pending_scrape_url)\n", + " logger.info(f\"Successfully scraped website details, length: {len(website_details) if website_details else 0}\")\n", + " \n", + " ClientDataManager.update_client_data(\n", + " enterprise_details_content=website_details,\n", + " last_analyzed_url=client_data.pending_scrape_url,\n", + " scraping_in_progress=False,\n", + " pending_scrape_url=None\n", + " )\n", + " st.success(\"Website details extracted successfully!\")\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error scraping website {client_data.pending_scrape_url}: {str(e)}\")\n", + " ClientDataManager.update_client_data(\n", + " scraping_in_progress=False,\n", + " pending_scrape_url=None\n", + " )\n", + " st.error(f\"Error scraping website: {str(e)}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error in scraping operation: {str(e)}\")\n", + "\n", + " # Show validation warning for URL field\n", + " try:\n", + " if client_data.show_validation and check_field_validation(\"Client Website URL\", client_website_url, False):\n", + " show_field_warning(\"Client Website URL\")\n", + " logger.debug(\"Showed validation warning for Client Website URL\")\n", + " except Exception as e:\n", + " logger.error(f\"Error showing URL validation warning: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in client website URL column: {str(e)}\")\n", + " st.error(\"Error in website URL section\")\n", + " \n", + " # File upload and enterprise details section\n", + " logger.debug(\"Creating file upload and enterprise details section\")\n", + " col3, col4 = st.columns([1, 1])\n", + " \n", + " with col3:\n", + " try:\n", + " logger.debug(\"Processing file upload section\")\n", + " \n", + " st.markdown('''\n", + "
\n", + " Upload RFI Document\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " # Add custom CSS for file uploader\n", + " st.markdown(\"\"\"\n", + " \n", + " \"\"\", unsafe_allow_html=True)\n", + " \n", + " # FILE UPLOAD\n", + " try:\n", + " rfi_document_upload = st.file_uploader(\n", + " label=\"Upload RFI Document\", \n", + " type=['pdf', 'docx', 'txt', 'csv', 'png', 'jpg', 'jpeg'], \n", + " key=\"rfi_document_uploader\",\n", + " label_visibility=\"collapsed\"\n", + " )\n", + " \n", + " if rfi_document_upload is not None:\n", + " logger.info(f\"File uploaded: {rfi_document_upload.name}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating file uploader: {str(e)}\")\n", + " rfi_document_upload = None\n", + " \n", + " # Show file info and analyze button\n", + " if rfi_document_upload is not None:\n", + " try:\n", + " file_size_kb = round(rfi_document_upload.size / 1024, 1)\n", + " file_size_display = f\"{file_size_kb}KB\" if file_size_kb < 1024 else f\"{round(file_size_kb/1024, 1)}MB\"\n", + " logger.debug(f\"File size: {file_size_display}\")\n", + " \n", + " # Single compact row\n", + " col_info, col_btn = st.columns([2.5, 1])\n", + " \n", + " with col_info:\n", + " if client_data.processing_rfi:\n", + " st.markdown(f\"\"\"\n", + "
\n", + " \n", + " šŸ”„ {rfi_document_upload.name[:20]}{'...' if len(rfi_document_upload.name) > 20 else ''} (Analyzing...)\n", + " \n", + "
\n", + " \"\"\", unsafe_allow_html=True)\n", + " else:\n", + " st.markdown(f\"šŸ“„ {rfi_document_upload.name[:25]}{'...' if len(rfi_document_upload.name) > 25 else ''} ({file_size_display})\", \n", + " unsafe_allow_html=True)\n", + " \n", + " with col_btn:\n", + " try:\n", + " button_color = \"#FF6B6B\" if client_data.processing_rfi else \"#4CAF50\"\n", + " st.markdown(f\"\"\"\n", + " \n", + " \"\"\", unsafe_allow_html=True)\n", + "\n", + " analyze_clicked = st.button(\n", + " \"Analyzing...\" if client_data.processing_rfi else \"Get pain points\",\n", + " key=\"analyze_rfi_document_btn\",\n", + " help=\"Process RFI document\" if not client_data.processing_rfi else \"Processing in progress...\",\n", + " type=\"secondary\",\n", + " disabled=client_data.processing_rfi,\n", + " use_container_width=True\n", + " )\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating analyze button: {str(e)}\")\n", + " analyze_clicked = False\n", + " \n", + " # Handle analyze button click\n", + " if analyze_clicked and not client_data.processing_rfi:\n", + " try:\n", + " if not client_enterprise_name:\n", + " logger.warning(\"Analyze clicked but no client enterprise name provided\")\n", + " st.error(\"āŒ Please enter the Client Enterprise Name first\")\n", + " else:\n", + " logger.info(\"Starting RFI analysis process\")\n", + " ClientDataManager.update_client_data(processing_rfi=True)\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error handling analyze button click: {str(e)}\")\n", + " \n", + " # Show processing indicator\n", + " if client_data.processing_rfi:\n", + " try:\n", + " with st.container():\n", + " col_spinner, col_text = st.columns([0.5, 4])\n", + " with col_spinner:\n", + " with st.spinner(''):\n", + " pass\n", + " with col_text:\n", + " st.markdown(\"**šŸ” Analyzing RFI document and extracting key insights...**\")\n", + " \n", + " # Perform the actual processing\n", + " try:\n", + " logger.info(\"Starting RFI document processing\")\n", + " file_path = save_uploaded_file_and_get_path(rfi_document_upload)\n", + " \n", + " if file_path and client_enterprise_name:\n", + " logger.info(f\"Processing RFI file: {file_path}\")\n", + " pain_points_data = get_pain_points(file_path, client_enterprise_name)\n", + " logger.info(f\"Successfully extracted pain points, count: {len(pain_points_data) if pain_points_data else 0}\")\n", + " \n", + " ClientDataManager.update_client_data(\n", + " uploaded_file_path=file_path,\n", + " rfi_pain_points_items=pain_points_data,\n", + " document_analyzed=True,\n", + " processing_rfi=False\n", + " )\n", + " st.success(\"āœ… RFI document analyzed successfully!\")\n", + " st.rerun()\n", + " else:\n", + " logger.error(\"Error saving the uploaded file or missing client name\")\n", + " st.error(\"āŒ Error saving the uploaded file\")\n", + " ClientDataManager.update_client_data(processing_rfi=False)\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error analyzing RFI document: {str(e)}\")\n", + " st.error(f\"āŒ Error analyzing RFI document: {str(e)}\")\n", + " ClientDataManager.update_client_data(\n", + " rfi_pain_points_items={},\n", + " document_analyzed=False,\n", + " processing_rfi=False\n", + " )\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in processing indicator section: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in file info section: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in file upload column: {str(e)}\")\n", + " st.error(\"Error in file upload section\")\n", + "\n", + " with col4:\n", + " try:\n", + " logger.debug(\"Processing enterprise details section\")\n", + " \n", + " st.markdown('''\n", + "
\n", + " Client Enterprise Details\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip())\n", + " \n", + " try:\n", + " enterprise_details = st.text_area(\n", + " label=\"Client Enterprise Details\", \n", + " value=client_data.enterprise_details_content if client_name_provided else \"\",\n", + " placeholder=\"Enter client name first to enable this field\" if not client_name_provided else \"Select/Enter the client website URL to fetch enterprise details\", \n", + " height=150, \n", + " key=\"enterprise_details_textarea\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided\n", + " )\n", + " \n", + " # Update dataclass when text area changes\n", + " if client_name_provided and enterprise_details != client_data.enterprise_details_content:\n", + " try:\n", + " ClientDataManager.update_client_data(enterprise_details_content=enterprise_details)\n", + " logger.debug(\"Updated enterprise details content\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating enterprise details: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating enterprise details textarea: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in enterprise details column: {str(e)}\")\n", + " st.error(\"Error in enterprise details section\")\n", + "\n", + " # Client Requirements and Pain Points Row\n", + " logger.debug(\"Creating client requirements and pain points section\")\n", + " col5, col6 = st.columns([1, 1])\n", + "\n", + " with col5:\n", + " try:\n", + " logger.debug(\"Processing client requirements section\")\n", + " \n", + " st.markdown('''\n", + "
\n", + " Client Requirements *\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " try:\n", + " client_requirements = st.text_area(\n", + " label=\"Client Requirements\", \n", + " value=client_data.client_requirements_content if client_name_provided else \"\", \n", + " height=200, \n", + " key=\"client_requirements_textarea\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided,\n", + " placeholder=\"Enter client name first to enable this field\" if not client_name_provided else \"Add your client requirements here youmay take suggestions from AI in the right as well\"\n", + " )\n", + " \n", + " # Update the client data when the text area changes (only if enabled)\n", + " if client_name_provided:\n", + " try:\n", + " ClientDataManager.update_client_data(client_requirements_content=client_requirements)\n", + " logger.debug(\"Updated client requirements content\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating client requirements: {str(e)}\")\n", + " \n", + " client_requirements_provided = bool(client_name_provided and client_requirements.strip())\n", + " logger.debug(f\"Client requirements provided: {client_requirements_provided}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating client requirements textarea: {str(e)}\")\n", + " client_requirements = \"\"\n", + " client_requirements_provided = False\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in client requirements column: {str(e)}\")\n", + " st.error(\"Error in client requirements section\")\n", + " \n", + " with col6:\n", + " try:\n", + " logger.debug(\"Processing client pain points section\")\n", + " \n", + " # Title with tooltip only (no buttons)\n", + " st.markdown('''\n", + "
\n", + " Client Pain Points\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " # Get RFI pain points items from client data or use dummy data\n", + " try:\n", + " if client_name_provided and client_data.rfi_pain_points_items:\n", + " rfi_pain_points_items = client_data.rfi_pain_points_items\n", + " logger.debug(f\"Using actual pain points data, count: {len(rfi_pain_points_items)}\")\n", + " else:\n", + " # Dummy data when no client name or no file uploaded\n", + " rfi_pain_points_items = {\n", + " \"Revenue Challenges\": \"**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\\n• Average deal size decreased by 22% due to increased price competition\\n• Customer churn rate increased to 18%, up from 12% previous year\\n• Revenue per customer dropped 8% as clients downgraded service tiers\\n• New product launches generated only 60% of projected revenue\\n• Seasonal revenue fluctuations creating 40% variance between peak and low periods\\n• Pipeline conversion rates fell from 35% to 24% over past 12 months\\n\\n\",\n", + " \n", + " \"Cost and Margin Pressure\": \"**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\\n• Labor costs rose 18% while productivity remained flat\\n• Raw material prices up 25% with limited ability to pass costs to customers\\n• Operational efficiency decreased by 14% due to outdated processes\\n• Procurement costs increased 20% from supplier consolidation issues\\n• Technology infrastructure costs grew 30" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import streamlit as st\n", + "import pandas as pd\n", + "import os\n", + "import logging\n", + "from typing import List\n", + "from .client_utils import *\n", + "import threading\n", + "import time\n", + "from Search.Linkedin.linkedin_serp import *\n", + "from Recommendation.recommendation_utils import *\n", + "from .client_css import *\n", + "from .client_dataclass import ClientData, ClientDataManager\n", + "\n", + "# Configure logging\n", + "def setup_logging():\n", + " \"\"\"Setup logging configuration for client module\"\"\"\n", + " try:\n", + " # Create logs directory if it doesn't exist\n", + " logs_dir = \"logs\"\n", + " if not os.path.exists(logs_dir):\n", + " os.makedirs(logs_dir)\n", + " \n", + " # Configure logger\n", + " logger = logging.getLogger('client_module')\n", + " logger.setLevel(logging.DEBUG)\n", + " \n", + " # Remove existing handlers to avoid duplicates\n", + " for handler in logger.handlers[:]:\n", + " logger.removeHandler(handler)\n", + " \n", + " # Create file handler\n", + " file_handler = logging.FileHandler(os.path.join(logs_dir, 'client_logs.log'))\n", + " file_handler.setLevel(logging.DEBUG)\n", + " \n", + " # Create console handler\n", + " console_handler = logging.StreamHandler()\n", + " console_handler.setLevel(logging.INFO)\n", + " \n", + " # Create formatter\n", + " formatter = logging.Formatter(\n", + " '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'\n", + " )\n", + " file_handler.setFormatter(formatter)\n", + " console_handler.setFormatter(formatter)\n", + " \n", + " # Add handlers to logger\n", + " logger.addHandler(file_handler)\n", + " logger.addHandler(console_handler)\n", + " \n", + " return logger\n", + " except Exception as e:\n", + " print(f\"Error setting up logging: {str(e)}\")\n", + " return logging.getLogger('client_module')\n", + "\n", + "# Initialize logger\n", + "logger = setup_logging()\n", + "\n", + "def save_uploaded_file_and_get_path(uploaded_file):\n", + " \"\"\"Save uploaded file to a temporary directory and return the file path\"\"\"\n", + " logger.info(f\"Starting file upload process for file: {uploaded_file.name if uploaded_file else 'None'}\")\n", + " \n", + " try:\n", + " if uploaded_file is not None:\n", + " logger.debug(f\"File details - Name: {uploaded_file.name}, Size: {uploaded_file.size} bytes\")\n", + " \n", + " # Create uploads directory if it doesn't exist\n", + " upload_dir = os.getenv(\"FILE_SAVE_PATH\")\n", + " logger.debug(f\"Upload directory path: {upload_dir}\")\n", + " \n", + " if not os.path.exists(upload_dir):\n", + " try:\n", + " os.makedirs(upload_dir)\n", + " logger.info(f\"Created upload directory: {upload_dir}\")\n", + " except OSError as e:\n", + " logger.error(f\"Failed to create upload directory {upload_dir}: {str(e)}\")\n", + " raise\n", + " \n", + " # Create file path\n", + " file_path = os.path.join(upload_dir, uploaded_file.name)\n", + " logger.debug(f\"Full file path: {file_path}\")\n", + " \n", + " # Save the file\n", + " try:\n", + " with open(file_path, \"wb\") as f:\n", + " f.write(uploaded_file.getbuffer())\n", + " logger.info(f\"Successfully saved file to: {file_path}\")\n", + " return file_path\n", + " except IOError as e:\n", + " logger.error(f\"Failed to save file {file_path}: {str(e)}\")\n", + " raise\n", + " \n", + " else:\n", + " logger.warning(\"No file provided for upload\")\n", + " return None\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Unexpected error in save_uploaded_file_and_get_path: {str(e)}\")\n", + " raise\n", + "\n", + "\n", + "def validate_client_mandatory_fields():\n", + " \"\"\"Validate client mandatory fields using dataclass\"\"\"\n", + " logger.info(\"Starting client mandatory fields validation\")\n", + " \n", + " try:\n", + " client_data = ClientDataManager.get_client_data()\n", + " logger.debug(\"Retrieved client data for validation\")\n", + " \n", + " # Temporarily return True - validation disabled\n", + " logger.info(\"Validation bypassed - returning True\")\n", + " return True\n", + " \n", + " # Uncomment below for actual validation\n", + " # result = client_data.validate_mandatory_fields()\n", + " # logger.info(f\"Validation result: {result}\")\n", + " # return result\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in validate_client_mandatory_fields: {str(e)}\")\n", + " return False\n", + "\n", + "\n", + "def client_tab(st):\n", + " logger.info(\"Starting client_tab function\")\n", + " \n", + " try:\n", + " # Get client data from dataclass manager\n", + " client_data = ClientDataManager.get_client_data()\n", + " logger.debug(\"Retrieved client data from dataclass manager\")\n", + " \n", + " # Apply CSS only once\n", + " try:\n", + " if not client_data.css_applied:\n", + " logger.debug(\"Applying CSS for the first time\")\n", + " st.markdown(client_css, unsafe_allow_html=True)\n", + " ClientDataManager.update_client_data(css_applied=True)\n", + " logger.info(\"CSS applied and updated in client data\")\n", + " \n", + " # Re-apply CSS after every rerun to ensure persistence\n", + " st.markdown(client_css, unsafe_allow_html=True)\n", + " logger.debug(\"CSS re-applied for persistence\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error applying CSS: {str(e)}\")\n", + " st.error(\"Error loading page styles\")\n", + " \n", + " # Top section with client name and URLs\n", + " logger.debug(\"Creating top section with client name and URLs\")\n", + " col1, col2 = st.columns([1, 1])\n", + " \n", + " with col1:\n", + " try:\n", + " logger.debug(\"Processing client enterprise name section\")\n", + " \n", + " st.markdown(\"\"\"\n", + "
\n", + " Client Enterprise Name *\n", + "
ā“˜
\n", + "
\n", + " \"\"\", unsafe_allow_html=True)\n", + " \n", + " # Create a sub-column layout for name input and find URLs button\n", + " name_col, button_col = st.columns([3, 1])\n", + " \n", + " with name_col:\n", + " client_enterprise_name = st.text_input(\n", + " label=\"Client Enterprise Name\", \n", + " value=client_data.enterprise_name,\n", + " placeholder=\"Enter client enterprise name...\", \n", + " key=\"client_enterprise_name_input\",\n", + " label_visibility=\"collapsed\",\n", + " )\n", + " \n", + " # Update dataclass when input changes\n", + " if client_enterprise_name != client_data.enterprise_name:\n", + " try:\n", + " ClientDataManager.update_client_data(enterprise_name=client_enterprise_name)\n", + " logger.info(f\"Updated enterprise name: {client_enterprise_name}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating enterprise name: {str(e)}\")\n", + " \n", + " with button_col:\n", + " try:\n", + " # Find URLs button - only enabled when client name has more than 2 characters\n", + " find_urls_disabled = not (client_enterprise_name and len(client_enterprise_name.strip()) > 2)\n", + " logger.debug(f\"Find URLs button disabled: {find_urls_disabled}\")\n", + " \n", + " if st.button(\"šŸ” Find Website\",\n", + " disabled=find_urls_disabled,\n", + " help=\"Find website URLs for this company\",\n", + " key=\"find_urls_button\",\n", + " type=\"secondary\"):\n", + " \n", + " logger.info(f\"Find URLs button clicked for: {client_enterprise_name.strip()}\")\n", + " \n", + " # Add spinner while fetching URLs\n", + " with st.spinner(f\"Finding Websites for '{client_enterprise_name.strip()}'...\"):\n", + " try:\n", + " urls_list = get_urls_list(client_enterprise_name.strip())\n", + " logger.info(f\"Found {len(urls_list)} URLs for {client_enterprise_name.strip()}\")\n", + " \n", + " ClientDataManager.update_client_data(\n", + " website_urls_list=urls_list,\n", + " enterprise_name=client_enterprise_name\n", + " )\n", + " logger.debug(\"Updated client data with URLs list\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error finding URLs for {client_enterprise_name.strip()}: {str(e)}\")\n", + " ClientDataManager.update_client_data(website_urls_list=[])\n", + " st.error(f\"Error finding URLs: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in Find URLs button section: {str(e)}\")\n", + " \n", + " # Clear URLs if company name is cleared\n", + " try:\n", + " if not client_enterprise_name and client_data.enterprise_name:\n", + " logger.info(\"Company name cleared, clearing URLs list\")\n", + " ClientDataManager.update_client_data(\n", + " website_urls_list=[],\n", + " enterprise_name=\"\"\n", + " )\n", + " except Exception as e:\n", + " logger.error(f\"Error clearing URLs when company name cleared: {str(e)}\")\n", + " \n", + " # Show validation warning if triggered and field is empty\n", + " try:\n", + " if client_data.show_validation and check_field_validation(\"Client Enterprise Name\", client_enterprise_name, True):\n", + " show_field_warning(\"Client Enterprise Name\")\n", + " logger.debug(\"Showed validation warning for Client Enterprise Name\")\n", + " except Exception as e:\n", + " logger.error(f\"Error showing validation warning: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in client enterprise name column: {str(e)}\")\n", + " st.error(\"Error in client name section\")\n", + " \n", + " with col2:\n", + " try:\n", + " logger.debug(\"Processing client website URL section\")\n", + " \n", + " # Label row with inline emoji and tooltip\n", + " st.markdown('''\n", + "
\n", + " Client Website URL\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " # Create columns for dropdown and buttons\n", + " url_col, btn1_col, btn2_col, btn3_col = st.columns([7, 1, 1, 1])\n", + " \n", + " with url_col:\n", + " try:\n", + " # URL selection logic\n", + " client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip())\n", + " logger.debug(f\"Client name provided: {client_name_provided}\")\n", + " \n", + " if not client_data.website_urls_list:\n", + " url_options = [\"Select client website URL\"]\n", + " else:\n", + " url_options = [\"Select client website URL\"] + client_data.website_urls_list\n", + " \n", + " logger.debug(f\"URL options count: {len(url_options)}\")\n", + " \n", + " # Set default selection\n", + " default_index = 0\n", + " if client_data.website_url and client_data.website_url in url_options:\n", + " default_index = url_options.index(client_data.website_url)\n", + " \n", + " client_website_url = st.selectbox(\n", + " label=\"Client Website URL\",\n", + " options=url_options,\n", + " index=default_index,\n", + " key=\"client_website_url_selector\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided,\n", + " accept_new_options=True\n", + " )\n", + " \n", + " # Reset to empty string if default option is selected\n", + " if client_website_url == \"Select client website URL\":\n", + " client_website_url = \"\"\n", + " \n", + " # Update dataclass when URL changes\n", + " if client_website_url != client_data.website_url:\n", + " try:\n", + " ClientDataManager.update_client_data(website_url=client_website_url)\n", + " logger.info(f\"Updated website URL: {client_website_url}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating website URL: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in URL selection: {str(e)}\")\n", + " client_website_url = \"\"\n", + " \n", + " # Buttons for website actions\n", + " with btn1_col:\n", + " try:\n", + " if client_website_url:\n", + " st.link_button(\"🌐\", client_website_url, help=\"Visit website\", use_container_width=True)\n", + " else:\n", + " st.button(\"🌐\", help=\"Visit website\", disabled=True, use_container_width=True)\n", + " except Exception as e:\n", + " logger.error(f\"Error creating visit website button: {str(e)}\")\n", + " \n", + " with btn2_col:\n", + " try:\n", + " refresh_clicked = st.button(\"šŸ”„\", help=\"Refresh website URLs list\", key=\"refresh_urls_btn\", \n", + " use_container_width=True, disabled=not client_website_url)\n", + " except Exception as e:\n", + " logger.error(f\"Error creating refresh button: {str(e)}\")\n", + " refresh_clicked = False\n", + " \n", + " with btn3_col:\n", + " try:\n", + " scrape_clicked = st.button(\"šŸ“‘\", help=\"Get enterprise details\", key=\"scrape_website_btn\", \n", + " use_container_width=True, disabled=not client_website_url)\n", + " \n", + " if scrape_clicked and client_website_url:\n", + " logger.info(f\"Scrape button clicked for URL: {client_website_url}\")\n", + " ClientDataManager.update_client_data(\n", + " pending_scrape_url=client_website_url,\n", + " scraping_in_progress=True\n", + " )\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error creating scrape button: {str(e)}\")\n", + " scrape_clicked = False\n", + "\n", + " # Handle refresh action\n", + " if refresh_clicked and client_name_provided:\n", + " try:\n", + " logger.info(f\"Refreshing URLs for: {client_enterprise_name}\")\n", + " with st.spinner(\"Refreshing website URLs...\"):\n", + " urls_list = get_urls_list(client_enterprise_name)\n", + " ClientDataManager.update_client_data(website_urls_list=urls_list)\n", + " logger.info(f\"Successfully refreshed URLs, found {len(urls_list)} URLs\")\n", + " st.success(\"Website URLs refreshed!\")\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error refreshing URLs: {str(e)}\")\n", + " st.error(f\"Error refreshing URLs: {str(e)}\")\n", + "\n", + " # Handle pending scraping operation\n", + " if client_data.scraping_in_progress and client_data.pending_scrape_url:\n", + " try:\n", + " logger.info(f\"Starting website scraping for: {client_data.pending_scrape_url}\")\n", + " with st.spinner(f\"Scraping website details from {client_data.pending_scrape_url}...\"):\n", + " try:\n", + " website_details = get_url_details(client_data.pending_scrape_url)\n", + " logger.info(f\"Successfully scraped website details, length: {len(website_details) if website_details else 0}\")\n", + " \n", + " ClientDataManager.update_client_data(\n", + " enterprise_details_content=website_details,\n", + " last_analyzed_url=client_data.pending_scrape_url,\n", + " scraping_in_progress=False,\n", + " pending_scrape_url=None\n", + " )\n", + " st.success(\"Website details extracted successfully!\")\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error scraping website {client_data.pending_scrape_url}: {str(e)}\")\n", + " ClientDataManager.update_client_data(\n", + " scraping_in_progress=False,\n", + " pending_scrape_url=None\n", + " )\n", + " st.error(f\"Error scraping website: {str(e)}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error in scraping operation: {str(e)}\")\n", + "\n", + " # Show validation warning for URL field\n", + " try:\n", + " if client_data.show_validation and check_field_validation(\"Client Website URL\", client_website_url, False):\n", + " show_field_warning(\"Client Website URL\")\n", + " logger.debug(\"Showed validation warning for Client Website URL\")\n", + " except Exception as e:\n", + " logger.error(f\"Error showing URL validation warning: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in client website URL column: {str(e)}\")\n", + " st.error(\"Error in website URL section\")\n", + " \n", + " # File upload and enterprise details section\n", + " logger.debug(\"Creating file upload and enterprise details section\")\n", + " col3, col4 = st.columns([1, 1])\n", + " \n", + " with col3:\n", + " try:\n", + " logger.debug(\"Processing file upload section\")\n", + " \n", + " st.markdown('''\n", + "
\n", + " Upload RFI Document\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " # Add custom CSS for file uploader\n", + " st.markdown(\"\"\"\n", + " \n", + " \"\"\", unsafe_allow_html=True)\n", + " \n", + " # FILE UPLOAD\n", + " try:\n", + " rfi_document_upload = st.file_uploader(\n", + " label=\"Upload RFI Document\", \n", + " type=['pdf', 'docx', 'txt', 'csv', 'png', 'jpg', 'jpeg'], \n", + " key=\"rfi_document_uploader\",\n", + " label_visibility=\"collapsed\"\n", + " )\n", + " \n", + " if rfi_document_upload is not None:\n", + " logger.info(f\"File uploaded: {rfi_document_upload.name}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating file uploader: {str(e)}\")\n", + " rfi_document_upload = None\n", + " \n", + " # Show file info and analyze button\n", + " if rfi_document_upload is not None:\n", + " try:\n", + " file_size_kb = round(rfi_document_upload.size / 1024, 1)\n", + " file_size_display = f\"{file_size_kb}KB\" if file_size_kb < 1024 else f\"{round(file_size_kb/1024, 1)}MB\"\n", + " logger.debug(f\"File size: {file_size_display}\")\n", + " \n", + " # Single compact row\n", + " col_info, col_btn = st.columns([2.5, 1])\n", + " \n", + " with col_info:\n", + " if client_data.processing_rfi:\n", + " st.markdown(f\"\"\"\n", + "
\n", + " \n", + " šŸ”„ {rfi_document_upload.name[:20]}{'...' if len(rfi_document_upload.name) > 20 else ''} (Analyzing...)\n", + " \n", + "
\n", + " \"\"\", unsafe_allow_html=True)\n", + " else:\n", + " st.markdown(f\"šŸ“„ {rfi_document_upload.name[:25]}{'...' if len(rfi_document_upload.name) > 25 else ''} ({file_size_display})\", \n", + " unsafe_allow_html=True)\n", + " \n", + " with col_btn:\n", + " try:\n", + " button_color = \"#FF6B6B\" if client_data.processing_rfi else \"#4CAF50\"\n", + " st.markdown(f\"\"\"\n", + " \n", + " \"\"\", unsafe_allow_html=True)\n", + "\n", + " analyze_clicked = st.button(\n", + " \"Analyzing...\" if client_data.processing_rfi else \"Get pain points\",\n", + " key=\"analyze_rfi_document_btn\",\n", + " help=\"Process RFI document\" if not client_data.processing_rfi else \"Processing in progress...\",\n", + " type=\"secondary\",\n", + " disabled=client_data.processing_rfi,\n", + " use_container_width=True\n", + " )\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating analyze button: {str(e)}\")\n", + " analyze_clicked = False\n", + " \n", + " # Handle analyze button click\n", + " if analyze_clicked and not client_data.processing_rfi:\n", + " try:\n", + " if not client_enterprise_name:\n", + " logger.warning(\"Analyze clicked but no client enterprise name provided\")\n", + " st.error(\"āŒ Please enter the Client Enterprise Name first\")\n", + " else:\n", + " logger.info(\"Starting RFI analysis process\")\n", + " ClientDataManager.update_client_data(processing_rfi=True)\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error handling analyze button click: {str(e)}\")\n", + " \n", + " # Show processing indicator\n", + " if client_data.processing_rfi:\n", + " try:\n", + " with st.container():\n", + " col_spinner, col_text = st.columns([0.5, 4])\n", + " with col_spinner:\n", + " with st.spinner(''):\n", + " pass\n", + " with col_text:\n", + " st.markdown(\"**šŸ” Analyzing RFI document and extracting key insights...**\")\n", + " \n", + " # Perform the actual processing\n", + " try:\n", + " logger.info(\"Starting RFI document processing\")\n", + " file_path = save_uploaded_file_and_get_path(rfi_document_upload)\n", + " \n", + " if file_path and client_enterprise_name:\n", + " logger.info(f\"Processing RFI file: {file_path}\")\n", + " pain_points_data = get_pain_points(file_path, client_enterprise_name)\n", + " logger.info(f\"Successfully extracted pain points, count: {len(pain_points_data) if pain_points_data else 0}\")\n", + " \n", + " ClientDataManager.update_client_data(\n", + " uploaded_file_path=file_path,\n", + " rfi_pain_points_items=pain_points_data,\n", + " document_analyzed=True,\n", + " processing_rfi=False\n", + " )\n", + " st.success(\"āœ… RFI document analyzed successfully!\")\n", + " st.rerun()\n", + " else:\n", + " logger.error(\"Error saving the uploaded file or missing client name\")\n", + " st.error(\"āŒ Error saving the uploaded file\")\n", + " ClientDataManager.update_client_data(processing_rfi=False)\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error analyzing RFI document: {str(e)}\")\n", + " st.error(f\"āŒ Error analyzing RFI document: {str(e)}\")\n", + " ClientDataManager.update_client_data(\n", + " rfi_pain_points_items={},\n", + " document_analyzed=False,\n", + " processing_rfi=False\n", + " )\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in processing indicator section: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in file info section: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in file upload column: {str(e)}\")\n", + " st.error(\"Error in file upload section\")\n", + "\n", + " with col4:\n", + " try:\n", + " logger.debug(\"Processing enterprise details section\")\n", + " \n", + " st.markdown('''\n", + "
\n", + " Client Enterprise Details\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " client_name_provided = bool(client_enterprise_name and client_enterprise_name.strip())\n", + " \n", + " try:\n", + " enterprise_details = st.text_area(\n", + " label=\"Client Enterprise Details\", \n", + " value=client_data.enterprise_details_content if client_name_provided else \"\",\n", + " placeholder=\"Enter client name first to enable this field\" if not client_name_provided else \"Select/Enter the client website URL to fetch enterprise details\", \n", + " height=150, \n", + " key=\"enterprise_details_textarea\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided\n", + " )\n", + " \n", + " # Update dataclass when text area changes\n", + " if client_name_provided and enterprise_details != client_data.enterprise_details_content:\n", + " try:\n", + " ClientDataManager.update_client_data(enterprise_details_content=enterprise_details)\n", + " logger.debug(\"Updated enterprise details content\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating enterprise details: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating enterprise details textarea: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in enterprise details column: {str(e)}\")\n", + " st.error(\"Error in enterprise details section\")\n", + "\n", + " # Client Requirements and Pain Points Row\n", + " logger.debug(\"Creating client requirements and pain points section\")\n", + " col5, col6 = st.columns([1, 1])\n", + "\n", + " with col5:\n", + " try:\n", + " logger.debug(\"Processing client requirements section\")\n", + " \n", + " st.markdown('''\n", + "
\n", + " Client Requirements *\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " try:\n", + " client_requirements = st.text_area(\n", + " label=\"Client Requirements\", \n", + " value=client_data.client_requirements_content if client_name_provided else \"\", \n", + " height=200, \n", + " key=\"client_requirements_textarea\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided,\n", + " placeholder=\"Enter client name first to enable this field\" if not client_name_provided else \"Add your client requirements here youmay take suggestions from AI in the right as well\"\n", + " )\n", + " \n", + " # Update the client data when the text area changes (only if enabled)\n", + " if client_name_provided:\n", + " try:\n", + " ClientDataManager.update_client_data(client_requirements_content=client_requirements)\n", + " logger.debug(\"Updated client requirements content\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating client requirements: {str(e)}\")\n", + " \n", + " client_requirements_provided = bool(client_name_provided and client_requirements.strip())\n", + " logger.debug(f\"Client requirements provided: {client_requirements_provided}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error creating client requirements textarea: {str(e)}\")\n", + " client_requirements = \"\"\n", + " client_requirements_provided = False\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in client requirements column: {str(e)}\")\n", + " st.error(\"Error in client requirements section\")\n", + " \n", + " \n", + " # Continue from COL6 with enhanced logging and error handling\n", + " with col6:\n", + " try:\n", + " logger.info(\"Starting COL6 - Client Pain Points section rendering\")\n", + " \n", + " # Title with tooltip only (no buttons)\n", + " st.markdown('''\n", + "
\n", + " Client Pain Points\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " \n", + " try:\n", + " # Get RFI pain points items from client data or use dummy data\n", + " if client_name_provided and client_data.rfi_pain_points_items:\n", + " rfi_pain_points_items = client_data.rfi_pain_points_items\n", + " logger.info(f\"Using client RFI pain points data with {len(rfi_pain_points_items)} items\")\n", + " else:\n", + " # Dummy data when no client name or no file uploaded\n", + " rfi_pain_points_items = {\n", + " \"Revenue Challenges\": \"**Revenue Challenges** • Sales declined by 15% year-over-year despite market growth\\n• Missed quarterly revenue targets by $2.3M for three consecutive quarters\\n• Average deal size decreased by 22% due to increased price competition\\n• Customer churn rate increased to 18%, up from 12% previous year\\n• Revenue per customer dropped 8% as clients downgraded service tiers\\n• New product launches generated only 60% of projected revenue\\n• Seasonal revenue fluctuations creating 40% variance between peak and low periods\\n• Pipeline conversion rates fell from 35% to 24% over past 12 months\\n\\n\",\n", + " \n", + " \"Cost and Margin Pressure\": \"**Cost and Margin Pressure** • Cost of Goods Sold increased by 12% due to supply chain disruptions\\n• Labor costs rose 18% while productivity remained flat\\n• Raw material prices up 25% with limited ability to pass costs to customers\\n• Operational efficiency decreased by 14% due to outdated processes\\n• Procurement costs increased 20% from supplier consolidation issues\\n• Technology infrastructure costs grew 30% without proportional business benefits\\n• Regulatory compliance expenses added $1.8M in unexpected annual costs\\n• Facility and overhead costs up 16% while revenue remained stagnant\\n\\n\",\n", + " \n", + " \"Market Expansion and Customer Acquisition\": \"**Market Expansion and Customer Acquisition**\\n\\n • Win rate on new business opportunities dropped from 42% to 28%\\n• Customer acquisition cost increased 35% while customer lifetime value declined\\n• Expansion into new geographic markets yielding only 40% of projected results\\n• Lack of local market knowledge resulting in 60% longer sales cycles\\n• Digital marketing campaigns generating 50% fewer qualified leads\\n• Competition from new market entrants capturing 25% of target customer segment\\n• Limited brand recognition in new markets requiring 3x marketing investment\\n• Difficulty penetrating enterprise accounts with average sales cycle extending to 18 months\\n\\n\"\n", + " }\n", + " logger.info(\"Using dummy pain points data as fallback\")\n", + "\n", + " except Exception as e:\n", + " logger.error(f\"Error retrieving pain points data: {str(e)}\")\n", + " rfi_pain_points_items = {}\n", + " st.error(\"Error loading pain points data\")\n", + "\n", + " # Use a single container for all pain points items\n", + " try:\n", + " with st.container():\n", + " logger.debug(f\"Rendering {len(rfi_pain_points_items)} pain point items\")\n", + " \n", + " # Display pain points items with add/remove buttons\n", + " for i, (key, value) in enumerate(rfi_pain_points_items.items()):\n", + " try:\n", + " logger.debug(f\"Processing pain point item {i}: {key}\")\n", + " \n", + " # Check if this item is selected\n", + " try:\n", + " is_selected = key in client_data.selected_pain_points\n", + " logger.debug(f\"Pain point '{key}' selection status: {is_selected}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error checking selection status for '{key}': {str(e)}\")\n", + " is_selected = False\n", + " \n", + " # Create a box container with +/- button and content on same horizontal level\n", + " col_add, col_content = st.columns([0.5, 9], gap=\"medium\")\n", + " \n", + " with col_add:\n", + " try:\n", + " # Style the button to align vertically with the content box\n", + " st.markdown(\"\"\"\n", + " \n", + " \"\"\", unsafe_allow_html=True)\n", + " \n", + " # Change button appearance based on selection state\n", + " button_text = \"āŒ\" if is_selected else \"āž•\"\n", + " button_help = f\"Remove '{key}' from client requirements\" if is_selected else f\"Add '{key}' to client requirements section\"\n", + " button_type = \"secondary\" \n", + " \n", + " if st.button(button_text, \n", + " key=f\"toggle_rfi_pain_point_item_{i}\", \n", + " help=button_help,\n", + " type=button_type,\n", + " disabled=not client_name_provided):\n", + " \n", + " logger.info(f\"Pain point button clicked for '{key}', current selection: {is_selected}\")\n", + " \n", + " try:\n", + " if is_selected:\n", + " # REMOVE FUNCTIONALITY\n", + " logger.info(f\"Removing pain point '{key}' from requirements\")\n", + " \n", + " try:\n", + " # Get current content from the client data\n", + " current_content = client_data.client_requirements_content\n", + " logger.debug(f\"Current content length: {len(current_content) if current_content else 0}\")\n", + " \n", + " # Get the original content that was added for this key\n", + " original_content = client_data.pain_point_content_map.get(key, value)\n", + " logger.debug(f\"Original content to remove length: {len(original_content)}\")\n", + " \n", + " # Remove this specific pain point section from content\n", + " patterns_to_remove = [\n", + " f\"\\n\\n{original_content}\",\n", + " f\"{original_content}\\n\\n\",\n", + " original_content\n", + " ]\n", + " \n", + " updated_content = current_content\n", + " for pattern in patterns_to_remove:\n", + " if pattern in updated_content:\n", + " updated_content = updated_content.replace(pattern, \"\")\n", + " logger.debug(f\"Removed pattern from content\")\n", + " break\n", + " \n", + " # Clean up any excessive newlines\n", + " updated_content = '\\n\\n'.join([section.strip() for section in updated_content.split('\\n\\n') if section.strip()])\n", + " \n", + " # Update client data\n", + " client_data.selected_pain_points.discard(key)\n", + " if key in client_data.pain_point_content_map:\n", + " del client_data.pain_point_content_map[key]\n", + " \n", + " ClientDataManager.update_client_data(\n", + " client_requirements_content=updated_content,\n", + " selected_pain_points=client_data.selected_pain_points,\n", + " pain_point_content_map=client_data.pain_point_content_map\n", + " )\n", + " \n", + " logger.info(f\"Successfully removed pain point '{key}'\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in remove functionality for '{key}': {str(e)}\")\n", + " st.error(f\"Error removing pain point: {str(e)}\")\n", + " \n", + " else:\n", + " # ADD FUNCTIONALITY\n", + " logger.info(f\"Adding pain point '{key}' to requirements\")\n", + " \n", + " try:\n", + " # Get current content from client data\n", + " current_content = client_data.client_requirements_content\n", + " logger.debug(f\"Current content length before add: {len(current_content) if current_content else 0}\")\n", + " \n", + " # Append the value to the content\n", + " new_content = current_content + f\"\\n\\n{value}\" if current_content else value\n", + " logger.debug(f\"New content length after add: {len(new_content)}\")\n", + " \n", + " # Update client data\n", + " client_data.selected_pain_points.add(key)\n", + " client_data.pain_point_content_map[key] = value\n", + " \n", + " ClientDataManager.update_client_data(\n", + " client_requirements_content=new_content,\n", + " selected_pain_points=client_data.selected_pain_points,\n", + " pain_point_content_map=client_data.pain_point_content_map\n", + " )\n", + " \n", + " logger.info(f\"Successfully added pain point '{key}'\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error in add functionality for '{key}': {str(e)}\")\n", + " st.error(f\"Error adding pain point: {str(e)}\")\n", + " \n", + " st.rerun()\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error handling button click for '{key}': {str(e)}\")\n", + " st.error(f\"Error processing pain point selection: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error rendering button for pain point '{key}': {str(e)}\")\n", + " st.error(f\"Error rendering button: {str(e)}\")\n", + "\n", + " with col_content:\n", + " try:\n", + " # Style the content box based on selection state\n", + " if is_selected:\n", + " background_color = \"#2e7d32\"\n", + " border_color = \"#5a9f9f\"\n", + " text_color = \"#ffffff\"\n", + " icon = \"āœ…\"\n", + " box_shadow = \"0 2px 8px rgba(76, 175, 80, 0.3)\"\n", + " else:\n", + " background_color = \"#f5f5f5\"\n", + " border_color = \"#5a9f9f\"\n", + " text_color = \"#000000\"\n", + " icon = \"šŸ“‹\"\n", + " box_shadow = \"0 2px 4px rgba(0,0,0,0.1)\"\n", + " \n", + " st.markdown(f\"\"\"\n", + "
\n", + " {icon} {key}\n", + "
\n", + " \"\"\", unsafe_allow_html=True)\n", + " \n", + " logger.debug(f\"Successfully rendered content box for '{key}'\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error rendering content box for '{key}': {str(e)}\")\n", + " st.error(f\"Error rendering content: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error processing pain point item {i} ('{key}'): {str(e)}\")\n", + " st.error(f\"Error processing pain point item: {str(e)}\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Error rendering pain points container: {str(e)}\")\n", + " st.error(\"Error loading pain points section\")\n", + " \n", + " except Exception as e:\n", + " logger.error(f\"Critical error in COL6 rendering: {str(e)}\")\n", + " st.error(\"Critical error in pain points section\")\n", + "\n", + " # SPOC Row\n", + " try:\n", + " col_spoc1, col_spoc2 = st.columns([1, 1])\n", + " logger.info(\"Created SPOC columns layout\")\n", + " except Exception as e:\n", + " logger.error(f\"Error creating SPOC columns: {str(e)}\")\n", + " st.error(\"Error creating layout\")\n", + "\n", + " with col_spoc1:\n", + " try:\n", + " st.markdown('''\n", + "
\n", + " SPOC Name\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " logger.info(\"Rendered SPOC name tooltip\")\n", + " except Exception as e:\n", + " logger.error(f\"Error rendering SPOC name tooltip: {str(e)}\")\n", + " \n", + " try:\n", + " spoc_name = st.text_input(\n", + " label=\"SPOC Name\", \n", + " value=client_data.spoc_name,\n", + " placeholder=\"Enter SPOC full name...\", \n", + " key=\"spoc_name_input\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided\n", + " )\n", + " logger.info(f\"SPOC name input rendered with value: {spoc_name}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error creating SPOC name input: {str(e)}\")\n", + " spoc_name = \"\"\n", + " \n", + " try:\n", + " # Update client data when SPOC name changes\n", + " if spoc_name != client_data.spoc_name:\n", + " ClientDataManager.update_client_data(spoc_name=spoc_name)\n", + " logger.info(f\"Updated SPOC name to: {spoc_name}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating SPOC name: {str(e)}\")\n", + " \n", + " try:\n", + " # Automatically search for LinkedIn profiles when SPOC name changes\n", + " if spoc_name and spoc_name.strip() and spoc_name != client_data.last_searched_spoc and client_name_provided:\n", + " with st.spinner(f\"Searching LinkedIn profiles for {spoc_name}...\"):\n", + " # Assuming search_linkedin_serpapi is available\n", + " linkedin_profiles = search_linkedin_serpapi(spoc_name.strip())\n", + " ClientDataManager.update_client_data(\n", + " linkedin_profiles=linkedin_profiles,\n", + " last_searched_spoc=spoc_name\n", + " )\n", + " logger.info(f\"LinkedIn search completed for: {spoc_name}\")\n", + " st.rerun()\n", + " except Exception as e:\n", + " logger.error(f\"Error searching LinkedIn profiles: {str(e)}\")\n", + "\n", + " with col_spoc2:\n", + " try:\n", + " # Check if SPOC name is provided (for disabling LinkedIn field)\n", + " spoc_name_provided = bool(spoc_name and spoc_name.strip()) and client_name_provided\n", + " logger.info(f\"SPOC name provided status: {spoc_name_provided}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error checking SPOC name provided status: {str(e)}\")\n", + " spoc_name_provided = False\n", + " \n", + " try:\n", + " st.markdown('''\n", + "
\n", + " Select SPOC LinkedIn Profile\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " logger.info(\"Rendered LinkedIn profile tooltip\")\n", + " except Exception as e:\n", + " logger.error(f\"Error rendering LinkedIn profile tooltip: {str(e)}\")\n", + " \n", + " try:\n", + " # Prepare LinkedIn profile options\n", + " if spoc_name_provided and client_data.linkedin_profiles:\n", + " # Create options with profile titles for better selection\n", + " linkedin_options = [\"Select a LinkedIn profile...\"]\n", + " linkedin_url_mapping = {} # To map display text to actual URL\n", + " \n", + " for url, profile_data in client_data.linkedin_profiles.items():\n", + " display_text = f\"{profile_data['role']} ({profile_data['name']})\"\n", + " linkedin_options.append(display_text)\n", + " linkedin_url_mapping[display_text] = url\n", + " \n", + " selected_linkedin_display = st.selectbox(\n", + " label=\"SPOC LinkedIn Profile\",\n", + " options=linkedin_options,\n", + " key=\"spoc_linkedin_profile_selector\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not spoc_name_provided,\n", + " accept_new_options=True,\n", + " )\n", + " \n", + " # Extract the actual URL from the selected option\n", + " if selected_linkedin_display != \"Select a LinkedIn profile...\":\n", + " spoc_linkedin_profile = linkedin_url_mapping[selected_linkedin_display]\n", + " ClientDataManager.update_client_data(spoc_linkedin_profile=spoc_linkedin_profile)\n", + " logger.info(f\"LinkedIn profile selected: {spoc_linkedin_profile}\")\n", + " else:\n", + " spoc_linkedin_profile = None\n", + " logger.info(\"No LinkedIn profile selected\")\n", + " \n", + " elif spoc_name_provided and not client_data.linkedin_profiles:\n", + " # Show message when no profiles found\n", + " st.selectbox(\n", + " label=\"SPOC LinkedIn Profile\",\n", + " options=[\"No LinkedIn profiles found. Try a different name.\"],\n", + " key=\"spoc_linkedin_profile_selector\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=True,\n", + " accept_new_options=True\n", + " )\n", + " spoc_linkedin_profile = None\n", + " logger.info(\"No LinkedIn profiles found\")\n", + " else:\n", + " # Default disabled state\n", + " spoc_linkedin_profile = st.selectbox(\n", + " label=\"SPOC LinkedIn Profile\",\n", + " options=[\"Enter SPOC name to get LinkedIn profiles\"],\n", + " key=\"spoc_linkedin_profile_selector\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not spoc_name_provided,\n", + " accept_new_options=True\n", + " )\n", + " logger.info(\"LinkedIn profile selector in disabled state\")\n", + " except Exception as e:\n", + " logger.error(f\"Error creating LinkedIn profile selector: {str(e)}\")\n", + " spoc_linkedin_profile = None\n", + "\n", + " try:\n", + " # Display selected profile information and handle dynamic updates\n", + " if spoc_name_provided and client_data.linkedin_profiles:\n", + " # Check if LinkedIn profile selection has changed\n", + " profile_changed = False\n", + " if 'spoc_linkedin_profile' in locals() and spoc_linkedin_profile:\n", + " if client_data.current_selected_profile_url != spoc_linkedin_profile:\n", + " ClientDataManager.update_client_data(current_selected_profile_url=spoc_linkedin_profile)\n", + " profile_changed = True\n", + " logger.info(f\"LinkedIn profile changed to: {spoc_linkedin_profile}\")\n", + " \n", + " selected_profile_data = client_data.linkedin_profiles.get(spoc_linkedin_profile)\n", + " if selected_profile_data:\n", + " st.info(f\"\"\"**Selected Profile:** {selected_profile_data['role']} - {selected_profile_data['name']} \n", + " **LinkedIn URL:** {spoc_linkedin_profile}\"\"\")\n", + " \n", + " # Update roles and priorities when profile changes\n", + " if profile_changed:\n", + " # Remove previously auto-populated LinkedIn role if it exists\n", + " linkedin_roles_to_remove = []\n", + " for i, role in enumerate(client_data.selected_target_roles):\n", + " # Check if this role was from a previous LinkedIn profile\n", + " for url, profile in client_data.linkedin_profiles.items():\n", + " if url != spoc_linkedin_profile and profile['role'] == role:\n", + " linkedin_roles_to_remove.append(i)\n", + " break\n", + " \n", + " # Remove old LinkedIn roles\n", + " for idx in reversed(linkedin_roles_to_remove):\n", + " client_data.selected_target_roles.pop(idx)\n", + " \n", + " # Add new LinkedIn role\n", + " linkedin_role = selected_profile_data['role']\n", + " if linkedin_role and linkedin_role not in client_data.selected_target_roles:\n", + " client_data.selected_target_roles.append(linkedin_role)\n", + " \n", + " # Remove old LinkedIn priorities and add new ones\n", + " linkedin_priorities_to_remove = []\n", + " for priority in client_data.selected_business_priorities:\n", + " # Check if this priority was from a previous LinkedIn profile\n", + " for url, profile in client_data.linkedin_profiles.items():\n", + " if url != spoc_linkedin_profile and priority in profile.get('top_3_priorities', []):\n", + " linkedin_priorities_to_remove.append(priority)\n", + " break\n", + " \n", + " # Remove old LinkedIn priorities\n", + " for priority in linkedin_priorities_to_remove:\n", + " if priority in client_data.selected_business_priorities:\n", + " client_data.selected_business_priorities.remove(priority)\n", + " \n", + " # Add new LinkedIn priorities\n", + " inferred_priorities = selected_profile_data.get('top_3_priorities', [])\n", + " for priority in inferred_priorities:\n", + " if priority not in client_data.selected_business_priorities:\n", + " client_data.selected_business_priorities.append(priority)\n", + " \n", + " # Update client data\n", + " ClientDataManager.update_client_data(\n", + " selected_target_roles=client_data.selected_target_roles,\n", + " selected_business_priorities=client_data.selected_business_priorities\n", + " )\n", + " logger.info(\"Updated roles and priorities based on LinkedIn profile change\")\n", + " \n", + " # Force rerun to update the display\n", + " st.rerun()\n", + " elif client_data.current_selected_profile_url is not None:\n", + " # Profile was deselected\n", + " ClientDataManager.update_client_data(current_selected_profile_url=None)\n", + " profile_changed = True\n", + " logger.info(\"LinkedIn profile deselected\")\n", + " except Exception as e:\n", + " logger.error(f\"Error handling LinkedIn profile changes: {str(e)}\")\n", + "\n", + " try:\n", + " # Roles and Priorities Row\n", + " col7, col8 = st.columns([1, 1])\n", + " logger.info(\"Created roles and priorities columns\")\n", + " except Exception as e:\n", + " logger.error(f\"Error creating roles and priorities columns: {str(e)}\")\n", + "\n", + " with col7:\n", + " try:\n", + " st.markdown('''\n", + "
\n", + " SPOC Role \n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " logger.info(\"Rendered SPOC role tooltip\")\n", + " except Exception as e:\n", + " logger.error(f\"Error rendering SPOC role tooltip: {str(e)}\")\n", + "\n", + " try:\n", + " # Prepare role options for dropdown based on LinkedIn profile selection\n", + " role_options = [\"Select a role...\"]\n", + " \n", + " # Get default roles from function (assuming this function exists)\n", + " target_roles_list = get_roles_list() or []\n", + " logger.info(f\"Retrieved {len(target_roles_list)} default roles\")\n", + " \n", + " # Check if a LinkedIn profile is selected\n", + " selected_linkedin_role = None\n", + " if (spoc_name_provided and \n", + " client_data.linkedin_profiles and \n", + " 'spoc_linkedin_profile' in locals() and \n", + " spoc_linkedin_profile):\n", + " \n", + " # Get the selected LinkedIn profile data\n", + " selected_profile_data = client_data.linkedin_profiles.get(spoc_linkedin_profile)\n", + " if selected_profile_data:\n", + " selected_linkedin_role = selected_profile_data.get('role')\n", + " if selected_linkedin_role:\n", + " # Show LinkedIn profile role + default roles from get_roles_list()\n", + " role_options = [\"Select a role...\", selected_linkedin_role]\n", + " # Add default roles, avoiding duplicates\n", + " for role in target_roles_list:\n", + " if role not in role_options:\n", + " role_options.append(role)\n", + " logger.info(f\"Added LinkedIn role: {selected_linkedin_role}\")\n", + " \n", + " # If no LinkedIn profile selected, show all available roles\n", + " if not selected_linkedin_role:\n", + " # Add standard roles from get_roles_list()\n", + " role_options.extend(target_roles_list)\n", + " \n", + " # Add LinkedIn roles if available (but no specific profile selected)\n", + " if spoc_name_provided and client_data.linkedin_profiles:\n", + " for url, profile_data in client_data.linkedin_profiles.items():\n", + " linkedin_role = profile_data.get('role')\n", + " if linkedin_role and linkedin_role not in role_options:\n", + " role_options.append(linkedin_role)\n", + " except Exception as e:\n", + " logger.error(f\"Error preparing role options: {str(e)}\")\n", + " role_options = [\"Select a role...\"]\n", + "\n", + " try:\n", + " # Determine the default/current value for the selectbox\n", + " current_selection = \"Select a role...\"\n", + " if selected_linkedin_role and selected_linkedin_role in role_options:\n", + " # Auto-select the LinkedIn role\n", + " current_selection = selected_linkedin_role\n", + " elif \"target_role_selector_dropdown\" in st.session_state:\n", + " # Keep the current selection if it exists in options\n", + " current_value = st.session_state[\"target_role_selector_dropdown\"]\n", + " if current_value in role_options:\n", + " current_selection = current_value\n", + "\n", + " # ROLES DROPDOWN - Only one role can be selected\n", + " selected_target_role = st.selectbox(\n", + " label=\"Target Role Selector\", \n", + " options=role_options,\n", + " index=role_options.index(current_selection) if current_selection in role_options else 0,\n", + " key=\"target_role_selector_dropdown\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not (client_name_provided and spoc_name_provided),\n", + " accept_new_options=True\n", + " )\n", + " logger.info(f\"Role selector rendered with selection: {selected_target_role}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error creating role selector: {str(e)}\")\n", + " selected_target_role = None\n", + "\n", + " try:\n", + " # Update client_data with the single selected role\n", + " if selected_target_role and selected_target_role != \"Select a role...\":\n", + " # Store as a single role, not a list\n", + " client_data.selected_target_role = selected_target_role\n", + " ClientDataManager.update_client_data(selected_target_role=selected_target_role)\n", + " logger.info(f\"Updated selected target role: {selected_target_role}\")\n", + " else:\n", + " client_data.selected_target_role = None\n", + " ClientDataManager.update_client_data(selected_target_role=None)\n", + " logger.info(\"Cleared selected target role\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating selected target role: {str(e)}\")\n", + "\n", + " with col8:\n", + " try:\n", + " # Label with tooltip\n", + " st.markdown('''\n", + "
\n", + " SPOC Business priorities\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " logger.info(\"Rendered business priorities tooltip\")\n", + " except Exception as e:\n", + " logger.error(f\"Error rendering business priorities tooltip: {str(e)}\")\n", + "\n", + " try:\n", + " # Default priorities (used if role is not selected or error occurs)\n", + " default_priorities = [\n", + " {'title': 'Revenue Growth and Market Share Expansion', 'icon': 'šŸ“ˆ'}, \n", + " {'title': 'Profitability and Cost Optimization', 'icon': 'šŸ’°'}, \n", + " {'title': 'Digital Transformation and Innovation', 'icon': 'šŸ¤–'}\n", + " ]\n", + "\n", + " # Load role-based priorities if role is selected\n", + " business_priorities_list = default_priorities # Start with default\n", + " if (\n", + " spoc_name_provided and \n", + " hasattr(client_data, 'selected_target_role') and \n", + " client_data.selected_target_role and \n", + " client_data.selected_target_role != \"Select a role...\"\n", + " ):\n", + " try:\n", + " role_priorities = get_ai_business_priorities(client_data.selected_target_role)\n", + " if role_priorities:\n", + " business_priorities_list = role_priorities\n", + " logger.info(f\"Loaded {len(business_priorities_list)} role-based priorities\")\n", + " except Exception as priority_error:\n", + " logger.error(f\"Error loading role-based priorities: {str(priority_error)}\")\n", + " pass # Silently fall back to default\n", + " \n", + " logger.info(f\"Using {len(business_priorities_list)} business priorities\")\n", + " except Exception as e:\n", + " logger.error(f\"Error preparing business priorities: {str(e)}\")\n", + " business_priorities_list = default_priorities\n", + "\n", + " try:\n", + " # Initialize selected_business_priorities if missing\n", + " if not hasattr(client_data, 'selected_business_priorities'):\n", + " client_data.selected_business_priorities = []\n", + " logger.info(\"Initialized selected_business_priorities\")\n", + " except Exception as e:\n", + " logger.error(f\"Error initializing selected_business_priorities: {str(e)}\")\n", + "\n", + " try:\n", + " # Show checkboxes for priorities\n", + " for i, priority in enumerate(business_priorities_list):\n", + " priority_title = priority.get('title') if isinstance(priority, dict) else str(priority)\n", + " priority_icon = priority.get('icon', 'šŸ“‹') if isinstance(priority, dict) else 'šŸ“‹'\n", + " display_text = f\"{priority_icon} **{priority_title}**\"\n", + " \n", + " # Determine checkbox state\n", + " default_checked = priority_title in client_data.selected_business_priorities\n", + " is_enabled = (\n", + " spoc_name_provided and \n", + " hasattr(client_data, 'selected_target_role') and \n", + " client_data.selected_target_role and \n", + " client_data.selected_target_role != \"Select a role...\"\n", + " )\n", + " \n", + " is_checked = st.checkbox(\n", + " display_text,\n", + " key=f\"business_priority_checkbox_{i}\",\n", + " value=default_checked,\n", + " disabled=not spoc_name_provided\n", + " )\n", + "\n", + " # Update selected priorities\n", + " if is_checked and priority_title not in client_data.selected_business_priorities:\n", + " client_data.selected_business_priorities.append(priority_title)\n", + " ClientDataManager.update_client_data(selected_business_priorities=client_data.selected_business_priorities)\n", + " logger.info(f\"Added business priority: {priority_title}\")\n", + " elif not is_checked and priority_title in client_data.selected_business_priorities:\n", + " client_data.selected_business_priorities.remove(priority_title)\n", + " ClientDataManager.update_client_data(selected_business_priorities=client_data.selected_business_priorities)\n", + " logger.info(f\"Removed business priority: {priority_title}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error handling business priorities checkboxes: {str(e)}\")\n", + "\n", + " try:\n", + " col9, col10 = st.columns([1, 1])\n", + " logger.info(\"Created additional requirements columns\")\n", + " except Exception as e:\n", + " logger.error(f\"Error creating additional requirements columns: {str(e)}\")\n", + "\n", + " with col9:\n", + " try:\n", + " st.markdown('''\n", + "
\n", + " Additional Client Requirements\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " logger.info(\"Rendered additional client requirements tooltip\")\n", + " except Exception as e:\n", + " logger.error(f\"Error rendering additional client requirements tooltip: {str(e)}\")\n", + " \n", + " try:\n", + " # TEXT AREA - DISABLED if no client name\n", + " client_additional_requirements = st.text_area(\n", + " label=\"Additional Client Requirements\", \n", + " value=client_data.client_additional_requirements_content if client_name_provided else \"\",\n", + " placeholder=\"Enter client name first to enable this field\" if not client_name_provided else \"Enter specific client requirements, expectations, project scope, compliance needs, budget constraints...\",\n", + " height=200,\n", + " key=\"client_additional_requirements_textarea\",\n", + " label_visibility=\"collapsed\",\n", + " disabled=not client_name_provided\n", + " )\n", + " logger.info(f\"Additional requirements text area rendered with {len(client_additional_requirements)} characters\")\n", + " except Exception as e:\n", + " logger.error(f\"Error creating additional requirements text area: {str(e)}\")\n", + " client_additional_requirements = \"\"\n", + " \n", + " try:\n", + " # Update the dataclass when the text area changes (only if enabled)\n", + " if client_name_provided and client_additional_requirements != client_data.client_additional_requirements_content:\n", + " ClientDataManager.update_client_data(client_additional_requirements_content=client_additional_requirements)\n", + " client_data = ClientDataManager.get_client_data() # Refresh reference\n", + " logger.info(\"Updated additional client requirements content\")\n", + " except Exception as e:\n", + " logger.error(f\"Error updating additional client requirements: {str(e)}\")\n", + " \n", + " try:\n", + " client_additional_requirements_provided = bool(client_name_provided and client_additional_requirements.strip())\n", + " logger.info(f\"Additional requirements provided status: {client_additional_requirements_provided}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error checking additional requirements provided status: {str(e)}\")\n", + " client_additional_requirements_provided = False\n", + "\n", + " with col10:\n", + " try:\n", + " # Title with tooltip only (no buttons)\n", + " st.markdown('''\n", + "
\n", + " Additional Specifications to be considered\n", + "
ā“˜
\n", + "
\n", + " ''', unsafe_allow_html=True)\n", + " logger.info(\"Rendered additional specifications tooltip\")\n", + " except Exception as e:\n", + " logger.error(f\"Error rendering additional specifications tooltip: {str(e)}\")\n", + " \n", + " try:\n", + " # Get additional specs items from client data or use dummy data\n", + " if client_name_provided and client_data.additional_specs_items:\n", + " additional_specs_items = client_data.additional_specs_items\n", + " logger.info(f\"Using {len(additional_specs_items)} client-specific additional specs\")\n", + " else:\n", + " # Dummy data when no client name or no specific data\n", + " additional_specs_items = {\n", + " \"Technical Infrastructure Requirements\": \"**Technical Infrastructure Requirements**\\n• Cloud hosting with 99.9% uptime SLA and auto-scaling capabilities\\n• Multi-region deployment for disaster recovery and performance optimization\\n• Integration with existing ERP, CRM, and financial management systems\\n• API-first architecture with RESTful services and webhook support\\n• Database performance optimization with sub-second query response times\\n• Security compliance with SOC2, ISO 27001, and industry-specific regulations\\n• Load balancing and CDN implementation for global content delivery\\n• Automated backup and recovery systems with point-in-time restoration\\n\\n\",\n", + " \n", + " \"Compliance and Security Standards\": \"**Compliance and Security Standards**\\n• GDPR, CCPA, and regional data privacy regulation compliance\\n• End-to-end encryption for data in transit and at rest\\n• Multi-factor authentication and role-based access controls\\n• Regular security audits and penetration testing protocols\\n• Data retention and deletion policies per regulatory requirements\\n• Audit trail logging for all system interactions and data changes\\n• Incident response plan with 4-hour notification requirements\\n• Employee background checks and security clearance verification\\n\\n\",\n", + " \n", + " \"Performance and Scalability Metrics\": \"**Performance and Scalability Metrics**\\n• System response time under 2 seconds for 95% of user interactions\\n• Concurrent user capacity of 10,000+ with linear scaling capability\\n• Database query optimization with indexing and caching strategies\\n• Mobile application performance with offline synchronization\\n• Bandwidth optimization for low-connectivity environments\\n• Real-time analytics and reporting with sub-minute data refresh\\n• Automated performance monitoring with threshold-based alerting\\n• Capacity planning with predictive scaling based on usage patterns\\n\\n\"\n", + " }\n", + " logger.info(\"Using default additional specs items\")\n", + " except Exception as e:\n", + " logger.error(f\"Error preparing additional specs items: {str(e)}\")\n", + " additional_specs_items = {}\n", + "\n", + " try:\n", + " # Use a single container for all additional specs items\n", + " with st.container():\n", + " # Display additional specs items with add/remove buttons\n", + " for i, (key, value) in enumerate(additional_specs_items.items()):\n", + " try:\n", + " # Check if this item is selected\n", + " is_selected = key in client_data.selected_additional_specs\n", + " \n", + " # Create a box container with +/- button and content on same horizontal level\n", + " col_add, col_content = st.columns([0.5, 9], gap=\"medium\")\n", + " \n", + " with col_add:\n", + " # Style the button to align vertically with the content box\n", + " st.markdown(\"\"\"\n", + " \n", + " \"\"\", unsafe_allow_html=True)\n", + " \n", + " # Change button appearance based on selection state\n", + " button_text = \"āŒ\" if is_selected else \"āž•\"\n", + " button_help = f\"Remove '{key}' from additional requirements\" if is_selected else f\"Add '{key}' to additional requirements section\"\n", + " button_type = \"secondary\" \n", + " \n", + " if st.button(button_text, \n", + " key=f\"toggle_additional_spec_item_{i}\", \n", + " help=button_help,\n", + " type=button_type,\n", + " disabled=not client_name_provided):\n", + " \n", + " if is_selected:\n", + " try:\n", + " # REMOVE FUNCTIONALITY\n", + " # Get current content from the dataclass\n", + " current_content = client_data.client_additional_requirements_content\n", + " \n", + " # Get the original content that was added for this key\n", + " original_content = client_data.additional_specs_content_map.get(key, value)\n", + " \n", + " # Remove this specific additional spec section from content\n", + " # Try multiple removal patterns to be more robust\n", + " patterns_to_remove = [\n", + " f\"\\n\\n{original_content}\",\n", + " f\"{original_content}\\n\\n\",\n", + " original_content\n", + " ]\n", + " \n", + " updated_content = current_content\n", + " for pattern in patterns_to_remove:\n", + " updated_content = updated_content.replace(pattern, \"\")\n", + " \n", + " # Clean up any excessive newlines\n", + " updated_content = '\\n\\n'.join([section.strip() for section in updated_content.split('\\n\\n') if section.strip()])\n", + " \n", + " # Update the dataclass\n", + " client_data.client_additional_requirements_content = updated_content\n", + " client_data.selected_additional_specs.discard(key)\n", + " if key in client_data.additional_specs_content_map:\n", + " del client_data.additional_specs_content_map[key]\n", + " \n", + " # Save changes to session state\n", + " ClientDataManager.save_client_data(client_data)\n", + " logger.info(f\"Removed additional spec: {key}\")\n", + " except Exception as remove_error:\n", + " logger.error(f\"Error removing additional spec '{key}': {str(remove_error)}\")\n", + " \n", + " else:\n", + " try:\n", + " # ADD FUNCTIONALITY\n", + " # Get current content from the dataclass\n", + " current_content = client_data.client_additional_requirements_content\n", + " \n", + " # Append the value to the content\n", + " new_content = current_content + f\"\\n\\n{value}\" if current_content else value\n", + " \n", + " # Update the dataclass\n", + " client_data.client_additional_requirements_content = new_content\n", + " client_data.additional_specs_content_map[key] = value\n", + " client_data.selected_additional_specs.add(key)\n", + " \n", + " # Save changes to session state\n", + " ClientDataManager.save_client_data(client_data)\n", + " logger.info(f\"Added additional spec: {key}\")\n", + " except Exception as add_error:\n", + " logger.error(f\"Error adding additional spec '{key}': {str(add_error)}\")\n", + " \n", + " st.rerun()\n", + "\n", + " with col_content:\n", + " try:\n", + " # Style the content box based on selection state\n", + " if is_selected:\n", + " background_color = \"#2e7d32\"\n", + " border_color = \"#5a9f9f\"\n", + " text_color = \"#ffffff\"\n", + " icon = \"āœ…\"\n", + " box_shadow = \"0 2px 8px rgba(76, 175, 80, 0.3)\"\n", + " else:\n", + " background_color = \"#f5f5f5\"\n", + " border_color = \"#5a9f9f\"\n", + " text_color = \"#000000\"\n", + " icon = \"šŸ“‹\"\n", + " box_shadow = \"0 2px 4px rgba(0,0,0,0.1)\"\n", + " \n", + " st.markdown(f\"\"\"\n", + "
\n", + " {icon} {key}\n", + "
\n", + " \"\"\", unsafe_allow_html=True)\n", + " except Exception as content_error:\n", + " logger.error(f\"Error rendering content box for '{key}': {str(content_error)}\")\n", + " except Exception as item_error:\n", + " logger.error(f\"Error processing additional spec item '{key}': {str(item_error)}\")\n", + " except Exception as e:\n", + " logger.error(f\"Error displaying additional specs items: {str(e)}\")\n", + " \n", + " try:\n", + " # Handle validation trigger from main app\n", + " if client_data.show_validation:\n", + " # Your validation logic here\n", + " logger.info(\"Validation triggered\")\n", + " pass\n", + " except Exception as e:\n", + " logger.error(f\"Error handling validation: {str(e)}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "adk", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/GS_Sales_Proposal/utils.py b/GS_Sales_Proposal/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..e51301c70299f48fc294d333a48ec599391edc7e --- /dev/null +++ b/GS_Sales_Proposal/utils.py @@ -0,0 +1,41 @@ +import os +import streamlit as st + + +def check_field_validation(field_name: str, field_value: str, is_mandatory: bool = False) -> bool: + """Check if field validation should show warning""" + if is_mandatory and not field_value.strip(): + return True + return False + +def show_field_warning(field_name: str): + """Show warning message for mandatory fields""" + st.markdown(f'
āš ļø {field_name} is mandatory and cannot be empty!
', unsafe_allow_html=True) + + +def save_uploaded_file(uploaded_file, save_dir="uploaded_rf_is"): + os.makedirs(save_dir, exist_ok=True) + save_path = os.path.join(save_dir, uploaded_file.name) + + with open(save_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + + return save_path + +def save_uploaded_file_and_get_path(uploaded_file): + """Save uploaded file to a temporary directory and return the file path""" + if uploaded_file is not None: + # Create uploads directory if it doesn't exist + upload_dir = "uploads" + if not os.path.exists(upload_dir): + os.makedirs(upload_dir) + + # Create file path + file_path = os.path.join(upload_dir, uploaded_file.name) + + # Save the file + with open(file_path, "wb") as f: + f.write(uploaded_file.getbuffer()) + + return file_path + return None \ No newline at end of file