"""
HRHUB - Bilateral HR Matching System
Main Streamlit Application
A professional HR matching system that connects candidates with companies
using NLP embeddings and cosine similarity matching.
"""
import streamlit as st
import sys
from pathlib import Path
# Add parent directory to path for imports
sys.path.append(str(Path(__file__).parent))
from config import *
from data.mock_data import (
get_candidate_data,
get_company_matches,
get_network_graph_data
)
from utils.display import (
display_candidate_profile,
display_company_card,
display_match_table,
display_stats_overview
)
from utils.visualization import create_network_graph
import streamlit.components.v1 as components
def configure_page():
"""Configure Streamlit page settings and custom CSS."""
st.set_page_config(
page_title="HRHUB - HR Matching",
page_icon="π’",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for better styling
st.markdown("""
""", unsafe_allow_html=True)
def render_header():
"""Render application header."""
st.markdown(f'
{APP_TITLE}
', unsafe_allow_html=True)
st.markdown(f'{APP_SUBTITLE}
', unsafe_allow_html=True)
# Demo mode indicator
if DEMO_MODE:
st.info(
"π **Demo Mode Active** - Displaying hardcoded sample data. "
"This will be replaced with real matching when embeddings are loaded.",
icon="βΉοΈ"
)
def render_sidebar():
"""Render sidebar with controls and information."""
with st.sidebar:
st.image("https://via.placeholder.com/250x80/0066CC/FFFFFF?text=HRHUB", use_container_width=True)
st.markdown("---")
st.markdown("### βοΈ Settings")
# Number of matches
top_k = st.slider(
"Number of Matches",
min_value=5,
max_value=20,
value=DEFAULT_TOP_K,
step=5,
help="Select how many top companies to display"
)
# Minimum score threshold
min_score = st.slider(
"Minimum Match Score",
min_value=0.0,
max_value=1.0,
value=MIN_SIMILARITY_SCORE,
step=0.05,
help="Filter companies below this similarity score"
)
st.markdown("---")
# View mode selection
st.markdown("### π View Mode")
view_mode = st.radio(
"Select view:",
["π Overview", "π Detailed Cards", "π Table View"],
help="Choose how to display company matches"
)
st.markdown("---")
# Information section
with st.expander("βΉοΈ About HRHUB", expanded=False):
st.markdown("""
**HRHUB** is a bilateral HR matching system that uses:
- π€ **NLP Embeddings**: Sentence transformers (384 dimensions)
- π **Cosine Similarity**: Scale-invariant matching
- π **Job Postings Bridge**: Aligns candidate and company language
**Key Innovation:**
Companies enriched with job posting data speak the same
"skills language" as candidates!
""")
with st.expander("π How to Use", expanded=False):
st.markdown("""
1. **View Candidate Profile**: See the candidate's skills and background
2. **Explore Matches**: Review top company matches with scores
3. **Network Graph**: Visualize connections interactively
4. **Company Details**: Click to see full company information
""")
st.markdown("---")
# Version info
st.caption(f"Version: {VERSION}")
st.caption("Β© 2024 HRHUB Team")
return top_k, min_score, view_mode
def render_network_section(candidate_id: int, top_k: int):
"""Render interactive network visualization section."""
st.markdown('', unsafe_allow_html=True)
with st.spinner("Generating interactive network graph..."):
# Get graph data
graph_data = get_network_graph_data(candidate_id, top_k)
# Create HTML graph
html_content = create_network_graph(
nodes=graph_data['nodes'],
edges=graph_data['edges'],
height="600px"
)
# Display in Streamlit
components.html(html_content, height=620, scrolling=False)
# Graph instructions
with st.expander("π Graph Controls", expanded=False):
st.markdown("""
**How to interact with the graph:**
- π±οΈ **Drag nodes**: Click and drag to reposition
- π **Zoom**: Scroll to zoom in/out
- π **Pan**: Click background and drag to pan
- π― **Hover**: Hover over nodes and edges for details
**Legend:**
- π’ **Green circles**: Candidates
- π΄ **Red squares**: Companies
- **Line thickness**: Match strength (thicker = better match)
""")
def render_matches_section(matches, view_mode: str):
"""Render company matches section with different view modes."""
st.markdown('', unsafe_allow_html=True)
if view_mode == "π Overview":
# Table view
display_match_table(matches)
elif view_mode == "π Detailed Cards":
# Card view - detailed
for rank, (comp_id, score, comp_data) in enumerate(matches, 1):
display_company_card(comp_data, score, rank)
elif view_mode == "π Table View":
# Compact table
display_match_table(matches)
def main():
"""Main application entry point."""
# Configure page
configure_page()
# Render header
render_header()
# Render sidebar and get settings
top_k, min_score, view_mode = render_sidebar()
# Main content area
st.markdown("---")
# Load candidate data
candidate_id = DEMO_CANDIDATE_ID
candidate = get_candidate_data(candidate_id)
# Load company matches
matches = get_company_matches(candidate_id, top_k)
# Filter by minimum score
matches = [(cid, score, cdata) for cid, score, cdata in matches if score >= min_score]
if not matches:
st.warning(f"No matches found above {min_score:.0%} threshold. Try lowering the minimum score.")
return
# Display statistics overview
display_stats_overview(candidate, matches)
# Create two columns for layout
col1, col2 = st.columns([1, 2])
with col1:
# Candidate profile section
st.markdown('', unsafe_allow_html=True)
display_candidate_profile(candidate)
with col2:
# Matches section
render_matches_section(matches, view_mode)
st.markdown("---")
# Network visualization (full width)
render_network_section(candidate_id, len(matches))
st.markdown("---")
# Footer with instructions
st.success(
"β
**MVP Demo Ready!** This interface shows the core functionality. "
"Next step: Replace mock data with real embeddings for dynamic matching.",
icon="π"
)
# Technical info expander
with st.expander("π§ Technical Details", expanded=False):
st.markdown(f"""
**Current Configuration:**
- Embedding Dimension: {EMBEDDING_DIMENSION}
- Similarity Metric: Cosine Similarity
- Top K Matches: {top_k}
- Minimum Score: {min_score:.0%}
- Demo Mode: {'β
Enabled' if DEMO_MODE else 'β Disabled'}
**Data Sources:**
- Candidates: 9,544 profiles
- Companies: 180,000 entities
- Job Postings: 700 (bridge data)
**Algorithm:**
1. Text representation of candidates/companies
2. Sentence transformer embeddings (384D)
3. Cosine similarity calculation
4. Top-K ranking
""")
if __name__ == "__main__":
main()