File size: 9,056 Bytes
6e54bce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import streamlit as st
import PyPDF2
import io
import docx2txt
from typing import Optional
import re
from pinecone_handler import PineconeHandler
from time_handling import read_timestamp

def extract_text_from_pdf(pdf_file) -> str:
    """Extract text content from PDF file"""
    pdf_reader = PyPDF2.PdfReader(pdf_file)
    text = ""
    for page in pdf_reader.pages:
        text += page.extract_text() + "\n"
    return text

def extract_text_from_docx(docx_file) -> str:
    """Extract text content from DOCX file"""
    text = docx2txt.process(docx_file)
    return text

def extract_resume_text(uploaded_file) -> Optional[str]:
    """Extract text from uploaded resume file"""
    if uploaded_file is None:
        return None
        
    # Get the file extension
    file_extension = uploaded_file.name.split('.')[-1].lower()
    
    try:
        # Process based on file type
        if file_extension == 'pdf':
            return extract_text_from_pdf(uploaded_file)
        elif file_extension in ['docx', 'doc']:
            return extract_text_from_docx(uploaded_file)
        elif file_extension == 'txt':
            return str(uploaded_file.read(), "utf-8")
        else:
            st.error(f"Unsupported file format: {file_extension}")
            return None
    except Exception as e:
        st.error(f"Error processing file: {str(e)}")
        return None

def clean_resume_text(text: str) -> str:
    """Clean and process resume text"""
    if not text:
        return ""
        
    # Remove special characters and extra whitespace
    text = re.sub(r'\s+', ' ', text)
    text = text.strip()
    
    return text

def is_description_truncated(description: str) -> bool:
    """Check if the description appears to be truncated"""
    # Check for obvious truncation indicators
    truncation_indicators = [
        lambda x: len(x) >= 995,  # Close to the 1000 char limit
        lambda x: x.rstrip().endswith(('...', '…')),
        lambda x: re.search(r'\w+$', x) and not re.search(r'[.!?]$', x),  # Ends mid-word or without punctuation
    ]
    
    return any(indicator(description) for indicator in truncation_indicators)

def format_job_description(description: str, truncated: bool = False) -> str:
    """Format job description text with proper sections and line breaks"""
    if not description:
        return ""
    
    # Common section headers in job descriptions
    sections = [
        "About us", "About you", "About the role", "About the position",
        "Requirements", "Qualifications", "Skills", "Responsibilities",
        "What you'll do", "What we offer", "Benefits", "Your profile",
        "Required skills", "What you need", "Who you are"
    ]
    
    # Add line breaks before section headers
    formatted_text = description
    for section in sections:
        # Look for section headers with case-insensitive matching
        pattern = re.compile(f'({section}:?)', re.IGNORECASE)
        formatted_text = pattern.sub(r'\n\n\1', formatted_text)
    
    # Handle bullet points (both • and - symbols)
    formatted_text = re.sub(r'[•-]\s*', '\n• ', formatted_text)
    
    # Add line breaks for sentences that look like list items
    formatted_text = re.sub(r'(?<=\w)\.(?=\s*[A-Z])', '.\n', formatted_text)
    
    # Clean up any excessive line breaks
    formatted_text = re.sub(r'\n{3,}', '\n\n', formatted_text)
    
    if truncated:
        formatted_text = formatted_text.rstrip() + "..."
    
    return formatted_text.strip()



def main():
    st.title("Resume-Based Job Search")
    st.write("Upload your resume to find matching job opportunities")
    
    # Initialize PineconeHandler
    try:
        handler = PineconeHandler()
    except Exception as e:
        st.error(f"Error connecting to Pinecone: {str(e)}")
        return
    
    # File uploader
    uploaded_file = st.file_uploader("Upload your resume", type=['pdf', 'docx', 'doc', 'txt'])
    
    # Search parameters
    num_results = st.slider("Number of results", min_value=1, max_value=20, value=5)
    
    if uploaded_file:
        with st.spinner("Processing resume..."):
            # Extract and clean resume text
            resume_text = extract_resume_text(uploaded_file)
            if resume_text:
                clean_text = clean_resume_text(resume_text)
                
                # Preview extracted text
                with st.expander("Preview extracted text"):
                    st.text(clean_text[:500] + "..." if len(clean_text) > 500 else clean_text)
                
                # Search button
                if st.button("Search Jobs"):
                    with st.spinner("Searching for matching jobs..."):
                        try:
                            # Search for similar job ads
                            results = handler.search_similar_ads(clean_text, top_k=num_results)
                            
                            if results:
                                st.subheader("Matching Jobs")
                                for i, match in enumerate(results, 1):
                                    metadata = match.metadata
                                    score = match.score
                                    
                                    # Create job card
                                    with st.container():
                                        # Header section with key information
                                        col1, col2 = st.columns([2, 1])
                                        with col1:
                                            st.markdown(f"### {metadata['headline']}")
                                        with col2:
                                            st.markdown(f"**Match Score:** {score:.2f}")
                                        
                                        # Job details section
                                        st.markdown(f"**Company:** {metadata.get('company', 'Not specified')}")
                                        st.markdown(f"**Location:** {metadata['city']}")
                                        st.markdown(f"**Occupation:** {metadata['occupation']}")
                                        st.markdown(f"**Published:** {metadata['published']}")
                                        if metadata.get('logo_url'):
                                            st.image(metadata['logo_url'], width=100)
                                        
                                        # Check if description is truncated
                                        description = metadata['description']
                                        is_truncated = is_description_truncated(description)
                                        
                                        # Display initial description preview
                                        formatted_description = format_job_description(
                                            description[:500] if is_truncated else description,
                                            truncated=is_truncated
                                        )
                                        st.markdown(formatted_description)
                                        
                                        # If truncated, show expandable full description
                                        if is_truncated:
                                            with st.expander("Read Full Description"):
                                                # Try to fetch full description from webpage_url
                                                st.markdown("""
                                                    **Note:** The full description has been truncated in our database. 
                                                    Please visit the original job posting for complete details.
                                                """)
                                                if metadata.get('webpage_url'):
                                                    st.markdown(f"[View Original Job Posting]({metadata['webpage_url']})")
                                        
                                        # Application section
                                        st.markdown("### How to Apply")
                                        if metadata.get('webpage_url'):
                                            st.markdown(f"[Apply Online]({metadata['webpage_url']})")
                                        if metadata.get('email'):
                                            st.markdown(f"📧 Contact: {metadata['email']}")
                                        
                                        st.markdown("---")
                            else:
                                st.info("No matching jobs found. Try adjusting your search criteria.")
                                
                        except Exception as e:
                            st.error(f"Error searching jobs: {str(e)}")

if __name__ == "__main__":
    main()