Saral_Ai / saral-ai.py
divyesh01's picture
File Uploading
41ea5e0 verified
import streamlit as st
import re
import os
from dotenv import load_dotenv
from openai import AzureOpenAI
import json
from nlp_parsed import parse_recruiter_query,prompt_enhancer
from SERP import query_making, serp_api_call
from apify import apify_call
from validate import validate_function, score_candidates
from postgres_db import fetch_from_saral_data, check_completeness, data_input, cur, conn, store_prompt
st.set_page_config(page_title="LinkedIn Recruiter Assistant", page_icon="🎯")
if "parsed_data" not in st.session_state:
st.session_state.parsed_data = {}
if "matched_results" not in st.session_state:
st.session_state.matched_results = []
if "unmatched_results" not in st.session_state:
st.session_state.unmatched_results = []
if "progress_placeholder" not in st.session_state:
st.session_state.progress_placeholder = None
if "progress" not in st.session_state:
st.session_state.progress = None
if "current_page" not in st.session_state:
st.session_state.current_page = 0
if "run_search" not in st.session_state:
st.session_state.run_search = False
if "user_input" not in st.session_state:
st.session_state.user_input = ""
st.header("Saral AI")
user_input = st.text_area(
"Enter your query here:",
placeholder="Enter your query here",
key="user_input_box",
value=st.session_state.user_input # always pull from session_state
)
st.session_state.user_input = user_input
# Show query parsing immediately (live preview)
if user_input.strip():
parsed_data = parse_recruiter_query(user_input)
st.session_state.parsed_data = parsed_data
if "error" in parsed_data:
st.error(parsed_data["error"])
elif parsed_data.get("is_indian") == False:
print("Our platform is not allowing search for out of india")
else:
with st.expander("Query", expanded=True):
col1, col2 = st.columns([1, 1])
with col1:
st.markdown(f'**Job Title:** {parsed_data.get("job_title", "None")}')
st.markdown(f'**Skills:** {parsed_data.get("skills", "None")}')
st.markdown(
f'**Experience:** {parsed_data.get("experience","None")} years of Experience'
)
st.markdown(f'is_indian :{parsed_data.get("is_indian","None")}')
with col2:
st.markdown(f'**Location:** {parsed_data.get("location", "None")}')
st.markdown(
f'**Work Preference:** {parsed_data.get("work_preference", "None")}'
)
st.markdown(f'**Job Type:** {parsed_data.get("job_type", "None")}')
# Enhance prompt button
if st.button("Enhance Prompt", use_container_width=True):
enhanced = prompt_enhancer(st.session_state.user_input)
# Store only in your own session_state variable
st.session_state.user_input = enhanced
# force rerun so text_area shows updated text
# st.experimental_rerun()
# Only fetch SERP + Apify when button clicked
if st.button(
"Enter",
use_container_width=True,
disabled=(parsed_data.get("is_indian") is False) # disable only if explicitly False
):
st.session_state.current_page = 0 # reset pagination
st.session_state.run_search = True
if st.session_state.run_search:
if not user_input.strip():
st.warning("Please enter a valid query.")
st.stop()
store_prompt(conn,user_input,parsed_data)
# Progress bar
st.session_state.progress_placeholder = st.empty()
st.session_state.progress = st.session_state.progress_placeholder.progress(0)
status = st.empty()
if user_input.strip() and "error" not in parsed_data:
query, location = query_making(parsed_data) # getting query like https:://linkedin.com --- AND location list
print(query)
### pagination concept
if st.session_state.current_page >= 0 :
results_per_page = 10
start = st.session_state.current_page * results_per_page
serp_data = serp_api_call(
query,
start= start,
results_per_page=10
)
saral_data, remain_urls = fetch_from_saral_data(serp_data, conn)
print(remain_urls)
st.session_state.progress.progress(30)
serp_json = {}
apify_json = {}
if len(remain_urls) >= 1:
for idx, i in enumerate(remain_urls,start=1):
serp_json[idx] = i
apify_json = apify_call(serp_json)
st.session_state.progress.progress(70)
if apify_json:
total_candidates = saral_data + apify_json
else:
total_candidates = saral_data
data_input(total_candidates)
# Validate funciton (location)
matched, unmatched = validate_function(location, total_candidates)
st.session_state.progress.progress(70)
matched = score_candidates(parsed_data , matched)
st.session_state.matched_results = matched
st.session_state.unmatched_results = unmatched
st.session_state.progress.progress(100)
st.session_state.progress_placeholder.empty()
st.session_state.progress = None
st.session_state.progress_placeholder = None
else:
st.warning("Please enter a valid query.")
if st.session_state.matched_results:
# length of Matched and unmatched profiles
col1, col2 = st.columns([1, 1])
with col1:
st.success(f"Matched Profiles: {len(st.session_state.matched_results)}")
with col2:
st.warning(f"Unmatched Profiles: {len(st.session_state.unmatched_results)}")
col1, col2, col3 = st.columns([1,2,1])
with col1:
if st.button("< Previous") and st.session_state.current_page > 0:
st.session_state.current_page -= 1
st.session_state.run_search = True
with col2:
st.write(f'Current Page {st.session_state.current_page + 1}')
with col3:
if st.button("Next >"):
st.session_state.current_page += 1
st.session_state.run_search = True
st.subheader("Candidates Profiles")
for idx, profiles in enumerate(st.session_state.matched_results, start=1):
with st.expander(f"{idx}. {profiles.get('fullName', 'Unknown')}"):
st.json(profiles)
with st.expander(
f"{idx}. {profiles.get('fullName', 'Unknown')} • Score: {profiles.get('score','None')} ", expanded=True
):
col1, col2 = st.columns([1, 2])
with col1:
image = profiles.get("profilePic")
temp_image = "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRDVO09x_DXK3p4Mt1j08Ab0R875TdhsDcG2A&s"
if profiles.get("profilePic"):
st.image(profiles.get("profilePic"), width=150)
else:
st.image(temp_image, width=150)
st.markdown(f"**Location:** {profiles.get('addressWithCountry','-')}")
st.markdown(f"**Email:** {profiles.get('email','None')}")
experiences = profiles.get("experiences", [])
open_to_work = True # default
for exp in experiences:
caption = exp.get("caption", "")
if "Present" in caption: # if still working
open_to_work = False
break
st.markdown(f"**Open to Work:** {'False' if not open_to_work else 'True'}")
st.markdown(
f"**LinkedIn:** [LinkedIn]({profiles.get('linkedinUrl','')})"
)
with col2:
st.markdown(f"### {profiles.get('fullName')}")
if profiles.get("headline"):
st.markdown(f"*{profiles.get('headline')}*")
skills_raw = profiles.get("skills", [])
skill_titles = [
s.get("title")
for s in skills_raw
if isinstance(s, dict) and "title" in s
]
if skill_titles:
st.markdown("**Skills:** " + " • ".join(skill_titles[:10]))
if profiles.get("about"):
about = profiles.get("about")
st.markdown(
"**About:** " + (about[:250] + "..." if len(about) > 250 else about)
)
if profiles.get("experiences"):
st.markdown("**Experience**")
for exp in profiles["experiences"]:
title = exp.get("title", "")
subtitle = exp.get("subtitle") or exp.get("metadata", "")
caption = exp.get("caption", "")
# Print main line
st.write(f"• {title} at {subtitle}{caption}")
# Print description if available
if exp.get("description"):
for desc in exp["description"]:
if isinstance(desc, dict) and "text" in desc:
st.markdown(f" - {desc['text']}")
if profiles.get("is_complete"):
st.markdown(f'{profiles.get("is_complete")}')
if st.session_state.unmatched_results:
st.subheader("Unmatched Profiles")
for idx, profiles in enumerate(st.session_state.unmatched_results, start=1):
st.markdown(
f"{idx}, {profiles.get('fullName', 'Unknown')} - {profiles.get('addressWithCountry', 'Unknown')} [LINKEDIN]({profiles.get('linkedinUrl', 'Unknown')})"
)