AreejMehboob commited on
Commit
173d0a1
·
verified ·
1 Parent(s): f534c0f

Rename src/streamlit_app.py to src/estreamlit_app.py

Browse files
src/{streamlit_app.py → estreamlit_app.py} RENAMED
@@ -599,457 +599,8 @@ def generate_linkedin(full_name):
599
  else:
600
  return f"https://linkedin.com/in/{name_parts[0]}-profile"
601
 
602
- def main():
603
- st.set_page_config(layout="wide")
604
-
605
- # Sidebar for navigation
606
- with st.sidebar:
607
- # About section with blue background
608
- st.markdown("""
609
- <div style="background-color:#3299a8; padding:10px; border-radius:5px;">
610
- <h3 style="color:white;">About</h3>
611
- <p style="color:white;">
612
- This Resume Matching System helps you find the best candidates for your job openings.
613
- Simply input your job description and requirements, and our AI-powered system will
614
- analyze and rank resumes based on skill match, experience, and overall fit.
615
- </p>
616
- </div>
617
- """, unsafe_allow_html=True)
618
-
619
- # How it Works section with light blue background
620
- st.markdown("""
621
- <div style="background-color:#e6f3f5; padding:10px; border-radius:5px; margin-top:10px;">
622
- <h3 style="color:#3299a8;">How it Works</h3>
623
- <ul style="margin-left: 15px; padding-left: 10px;">
624
- <li>Enter your job description or select a sample</li>
625
- <li>Our AI-agent analyzes key skills and requirements</li>
626
- <li>View ranked candidates with match percentages</li>
627
- <li>Examine detailed skill comparisons for each resume</li>
628
- </ul>
629
- </div>
630
- """, unsafe_allow_html=True)
631
-
632
- # Sample Job Descriptions section
633
- st.markdown("<h3 style='font-size:1.2em;'>Sample Job Descriptions</h3>", unsafe_allow_html=True)
634
-
635
- # Display sample job descriptions in sidebar with smaller font
636
- st.write("Click any job to prefill the form:")
637
-
638
- # Create a container with custom CSS for smaller buttons
639
- st.markdown("""
640
- <style>
641
- div.stButton > button {
642
- font-size: 0.8em;
643
- padding: 2px 8px;
644
- margin: 3px 0px;
645
- width: 100%;
646
- text-align: left;
647
- }
648
- .resume-card {
649
- background-color: #f8f9fa;
650
- border-radius: 10px;
651
- padding: 15px;
652
- margin-bottom: 20px;
653
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
654
- }
655
- .card-header {
656
- border-bottom: 1px solid #e0e0e0;
657
- padding-bottom: 10px;
658
- margin-bottom: 15px;
659
- }
660
- .contact-section {
661
- background-color: #e6f3f5;
662
- padding: 10px;
663
- border-radius: 5px;
664
- margin-top: 10px;
665
- }
666
- .skill-badge {
667
- background-color: #3299a8;
668
- color: white;
669
- padding: 4px 8px;
670
- border-radius: 15px;
671
- margin: 2px;
672
- display: inline-block;
673
- font-size: 0.85em;
674
- }
675
- .missing-skill-badge {
676
- background-color: #6c757d;
677
- color: white;
678
- padding: 4px 8px;
679
- border-radius: 15px;
680
- margin: 2px;
681
- display: inline-block;
682
- font-size: 0.85em;
683
- }
684
- .experience-card {
685
- background-color: #f8f9fa;
686
- border-left: 3px solid #3299a8;
687
- padding: 10px;
688
- margin-bottom: 10px;
689
- border-radius: 0 5px 5px 0;
690
- }
691
- </style>
692
- """, unsafe_allow_html=True)
693
-
694
- # Add horizontal line before job listings
695
- st.markdown("<hr>", unsafe_allow_html=True)
696
-
697
- # Job buttons
698
- for i, job in enumerate(job_posts):
699
- if st.button(job["title"], key=f"job_{i}"):
700
- # This will be used to set the job description text area
701
- st.session_state.job_description = job["jd"]
702
-
703
- # Main content area
704
- st.title("Resume Matching System")
705
-
706
- # Initialize session state for job description if it doesn't exist
707
- if 'job_description' not in st.session_state:
708
- st.session_state.job_description = "Enter job description here..."
709
-
710
- # Input fields - using session state for job description
711
- job_description = st.text_area("Job Description", value=st.session_state.job_description, height=250)
712
- additional_requirements = st.text_area("Additional Requirements", "", height=100)
713
-
714
- # Create two columns for the numeric inputs
715
- limit = st.slider("Number of Results", min_value=1, max_value=10, value=5, step=1)
716
-
717
- # Search button and animation container
718
- search_button = st.button("Search Resumes", type="primary")
719
-
720
- # Create a container for the code animation
721
- code_container = st.container()
722
-
723
- # Add custom CSS for the code animation box
724
- st.markdown("""
725
- <style>
726
- .highlight {
727
- background-color: black !important;
728
- }
729
- .highlight pre {
730
- color: yellow !important;
731
- }
732
- .highlight .gp, .highlight .gu, .highlight .gt {
733
- color: yellow !important;
734
- }
735
- pre code {
736
- white-space: pre-wrap !important;
737
- }
738
- </style>
739
- """, unsafe_allow_html=True)
740
- st.markdown("""
741
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
742
- """, unsafe_allow_html=True)
743
- if search_button:
744
-
745
- # Prepare the API request
746
-
747
- url = f"{api_endpoint}/resumes/search"
748
- headers = {
749
- "accept": "application/json",
750
- "Content-Type": "application/json",
751
- "Authorization": hf_token
752
- }
753
- payload = {
754
- "job_description": job_description,
755
- "additional_requirements": additional_requirements,
756
- "limit": limit,
757
- }
758
 
759
- try:
760
- # Display the animation while waiting
761
- with code_container:
762
- # Create the black box with yellow code animation
763
- code_display = simulated_code_animation(code_container)
764
- import time
765
- strt_time=time.time()
766
- # Make the API request
767
- response = requests.post(url, headers=headers, json=payload)
768
- print(f"total time taken by fast api to generate answer: {time.time()-strt_time}")
769
- # Clear the code animation
770
- code_container.empty()
771
-
772
- if response.status_code == 200:
773
- # Process successful response
774
- data = response.json()
775
- st.success(f"Found {data['count']} matching resumes")
776
-
777
- # Display each resume with the new card-based layout
778
- for i, resume in enumerate(data['results']):
779
- resume_data = resume['resume_data']
780
- explainability = resume['explainibility']
781
- # Generate synthetic contact information
782
- full_name = resume_data.get('full_name', 'No Name')
783
- # Add synthetic data to resume_data if not present
784
- if 'email' not in resume_data and 'email_id' not in resume_data:
785
- resume_data['email'] = generate_email(full_name)
786
- if 'phone' not in resume_data:
787
- resume_data['phone'] = generate_phone_number()
788
- if 'linkedin_profile' not in resume_data and 'linkedin' not in resume_data:
789
- resume_data['linkedin'] = generate_linkedin(full_name)
790
-
791
- # Main card with essential information
792
- with st.container():
793
- col1, col2 = st.columns([7, 3])
794
-
795
- with col1:
796
- # Name with larger font and accent color
797
- st.markdown(f"""
798
- <h2 style="color:#2C3E50; font-size:28px; margin-bottom:5px;">
799
- {full_name}
800
- </h2>
801
- """, unsafe_allow_html=True)
802
-
803
- # Years of experience with an icon
804
- years_exp = resume_data.get('total_experience_years', 'Not specified')
805
- st.markdown(f"""
806
- <div style="margin-bottom:15px;">
807
- <span style="color:#3299a8; font-size:16px;">
808
- <i class="fas fa-briefcase"></i> <strong>Years of Experience:</strong>
809
- </span>
810
- <span style="font-size:16px;">{years_exp}</span>
811
- </div>
812
- """, unsafe_allow_html=True)
813
-
814
- # Summary in a nice box with a border
815
- summary = ""
816
- if 'overall_summary' in resume:
817
- summary = resume.get('overall_summary')
818
- elif isinstance(resume_data.get('summary'), str):
819
- summary = resume_data.get('summary')
820
-
821
- st.markdown(explainability)
822
-
823
- if summary:
824
- st.markdown(f"""
825
- <div style="border-left:4px solid #3299a8; padding-left:15px;
826
- background-color:#f8f9fa; padding:10px; border-radius:0 5px 5px 0;">
827
- <div style="color:#3299a8; font-weight:bold; margin-bottom:5px;">SUMMARY</div>
828
- <div style="font-size:15px; line-height:1.5;">{summary}</div>
829
- </div>
830
- """, unsafe_allow_html=True)
831
- st.markdown("<br>", unsafe_allow_html=True)
832
- with col2:
833
- # Contact card with background
834
- st.markdown("""
835
- <div style="background-color:#f0f7fa; border-radius:8px; padding:15px;
836
- box-shadow:0 2px 5px rgba(0,0,0,0.05); margin-bottom:15px;">
837
- <h3 style="color:#3299a8; font-size:18px; border-bottom:2px solid #3299a8;
838
- padding-bottom:8px; margin-top:0;">
839
- <i class="fas fa-address-card"></i> CONTACT INFORMATION
840
- </h3>
841
- """, unsafe_allow_html=True)
842
-
843
- # Email with icon
844
- email = resume_data.get('email', resume_data.get('email_id', 'Not provided'))
845
- st.markdown(f"""
846
- <div style="margin-bottom:10px;">
847
- <i class="fas fa-envelope" style="color:#3299a8; width:20px;"></i>
848
- <strong style="color:#555;">Email:</strong><br>
849
- <a href="mailto:{email}" style="text-decoration:none; color:#3299a8;
850
- margin-left:25px; display:block; margin-top:3px;">{email}</a>
851
- </div>
852
- """, unsafe_allow_html=True)
853
-
854
- # Phone with icon
855
- phone = resume_data.get('phone', 'Not provided')
856
- st.markdown(f"""
857
- <div style="margin-bottom:10px;">
858
- <i class="fas fa-phone" style="color:#3299a8; width:20px;"></i>
859
- <strong style="color:#555;">Phone:</strong><br>
860
- <span style="margin-left:25px; display:block; margin-top:3px;">{phone}</span>
861
- </div>
862
- """, unsafe_allow_html=True)
863
-
864
- # Location with icon
865
- location = resume_data.get('location', 'Not specified')
866
- st.markdown(f"""
867
- <div style="margin-bottom:10px;">
868
- <i class="fas fa-map-marker-alt" style="color:#3299a8; width:20px;"></i>
869
- <strong style="color:#555;">Location:</strong><br>
870
- <span style="margin-left:25px; display:block; margin-top:3px;">{location}</span>
871
- </div>
872
- """, unsafe_allow_html=True)
873
-
874
- # LinkedIn with icon
875
- linkedin_url = resume_data.get('linkedin_profile', resume_data.get('linkedin', 'Not provided'))
876
- st.markdown(f"""
877
- <div style="margin-bottom:5px;">
878
- <i class="fab fa-linkedin" style="color:#3299a8; width:20px;"></i>
879
- <strong style="color:#555;">LinkedIn:</strong><br>
880
- <a href="{linkedin_url}" target="_blank" style="text-decoration:none;
881
- color:#3299a8; margin-left:25px; display:block; margin-top:3px;
882
- white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:100%;">
883
- {linkedin_url.replace('https://', '')}
884
- </a>
885
- </div>
886
- </div>
887
- """, unsafe_allow_html=True)
888
-
889
- # Create expandable sections for details - MOVED INSIDE THE LOOP
890
- with st.expander(f"Resume Content for {full_name}", expanded=False):
891
- tabs = st.tabs(["All Skills", "Experience", "Projects", "Education"])
892
-
893
- # All Skills tab
894
- with tabs[0]:
895
- st.subheader("Skills")
896
- if 'skills' in resume_data and resume_data['skills']:
897
- skills_html = ""
898
- for skill in resume_data['skills']:
899
- # Check if this skill is in matched_skills
900
- is_matched = skill in resume.get('matched_skills', [])
901
- badge_class = "skill-badge" if is_matched else "missing-skill-badge"
902
- skills_html += f'<span class="{badge_class}">{skill}</span> '
903
- st.markdown(skills_html, unsafe_allow_html=True)
904
- else:
905
- st.write("No skills listed.")
906
-
907
- # Experiences tab
908
- with tabs[1]:
909
- st.subheader("Professional Experience")
910
- if 'experience' in resume_data and resume_data['experience']:
911
- for exp in resume_data['experience']:
912
- with st.container():
913
- st.markdown(f"""
914
- <div class="experience-card">
915
- <strong>{exp.get('title', 'Position')}</strong> at <strong>{exp.get('company', 'Company')}</strong>
916
- <br><em>{exp.get('duration', '')}</em>
917
- <p>{exp.get('description', '')}</p>
918
- </div>
919
- """, unsafe_allow_html=True)
920
- elif 'professional_experiences' in resume_data and resume_data['professional_experiences']:
921
- for exp in resume_data['professional_experiences']:
922
- with st.container():
923
- st.markdown(f"""
924
- <div class="experience-card">
925
- <strong>{exp.get('title', 'Position')}</strong> at <strong>{exp.get('company', 'Company')}</strong>
926
- <br><em>{exp.get('duration', '')}</em>
927
- <p>{exp.get('description', '')}</p>
928
- </div>
929
- """, unsafe_allow_html=True)
930
- else:
931
- st.write("No experience information available.")
932
-
933
- # Projects tab
934
- with tabs[2]:
935
- st.subheader("Projects")
936
- if 'projects' in resume_data and resume_data['projects']:
937
- for project in resume_data['projects']:
938
- if isinstance(project, dict):
939
- project_name = project.get('name', project.get('title', 'Project'))
940
- project_desc = project.get('description', '')
941
- st.markdown(f"**{project_name}**")
942
- st.markdown(f"{project_desc}")
943
- st.markdown("---")
944
- else:
945
- st.markdown(f"- {project}")
946
- else:
947
- st.write("No projects listed.")
948
-
949
- # Education tab
950
- with tabs[3]:
951
- st.subheader("Education")
952
- if 'education' in resume_data and resume_data['education']:
953
- for edu in resume_data['education']:
954
- with st.container():
955
- st.markdown(f"""
956
- <div class="experience-card">
957
- <strong>{edu.get('degree', 'Degree')}</strong>
958
- <br><strong>{edu.get('institution', 'Institution')}</strong>
959
- <br><em>{edu.get('date', '')}</em>
960
- </div>
961
- """, unsafe_allow_html=True)
962
- else:
963
- st.write("No education information available.")
964
-
965
- # # Accomplishments tab
966
- # with tabs[4]:
967
- # st.subheader("Accomplishments")
968
- # if 'accomplishments' in resume_data and resume_data['accomplishments']:
969
- # for accomplishment in resume_data['accomplishments']:
970
- # if isinstance(accomplishment, str):
971
- # st.markdown(f"- {accomplishment}")
972
- # elif isinstance(accomplishment, dict):
973
- # # Handle if accomplishments are stored as dictionary objects
974
- # title = accomplishment.get('title', 'Accomplishment')
975
- # description = accomplishment.get('description', '')
976
- # st.markdown(f"**{title}**")
977
- # if description:
978
- # st.markdown(f"{description}")
979
- # st.markdown("---")
980
- # else:
981
- # st.write("No accomplishments listed.")
982
-
983
- # # Certifications tab
984
- # with tabs[5]:
985
- # st.subheader("Certifications")
986
- # if 'certifications' in resume_data and resume_data['certifications']:
987
- # for cert in resume_data['certifications']:
988
- # if isinstance(cert, str):
989
- # st.markdown(f"- {cert}")
990
- # elif isinstance(cert, dict):
991
- # # Handle if certifications are stored as dictionary objects
992
- # name = cert.get('name', cert.get('title', 'Certification'))
993
- # issuer = cert.get('issuer', cert.get('organization', ''))
994
- # date = cert.get('date', cert.get('issued_date', ''))
995
- # st.markdown(f"**{name}**")
996
- # if issuer:
997
- # st.markdown(f"Issuer: {issuer}")
998
- # if date:
999
- # st.markdown(f"Date: {date}")
1000
- # st.markdown("---")
1001
- # else:
1002
- # st.write("No certifications listed.")
1003
-
1004
- # Add the download button after each resume
1005
- st.markdown("""
1006
- <style>
1007
- .download-resume-btn {
1008
- display: inline-block;
1009
- width: 100%;
1010
- background-color: #3299a8;
1011
- color: white;
1012
- text-align: center;
1013
- padding: 12px 20px;
1014
- margin: 8px 0 25px 0;
1015
- border-radius: 5px;
1016
- border: none;
1017
- cursor: pointer;
1018
- font-size: 16px;
1019
- font-weight: 500;
1020
- transition: all 0.3s ease;
1021
- text-decoration: none;
1022
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
1023
- }
1024
-
1025
- .download-resume-btn:hover {
1026
- background-color: #277a86;
1027
- box-shadow: 0 4px 8px rgba(0,0,0,0.2);
1028
- transform: translateY(-2px);
1029
- }
1030
-
1031
- .download-resume-btn i {
1032
- margin-right: 10px;
1033
- }
1034
- </style>
1035
- """, unsafe_allow_html=True)
1036
-
1037
- # Create a centered container for the download button
1038
- col1, col2, col3 = st.columns([1, 2, 1])
1039
- with col2:
1040
- # Render the download button with icon
1041
- st.markdown(f"""
1042
- <button class="download-resume-btn" onclick="alert('This is a demo feature. In a production environment, this would download the resume for {full_name}.')">
1043
- <i class="fas fa-file-download"></i> Download Resume
1044
- </button>
1045
- """, unsafe_allow_html=True)
1046
-
1047
- # Add a separator between resumes
1048
- st.markdown("---")
1049
- except Exception as e:
1050
- # Clear the code animation in case of error
1051
- code_container.empty()
1052
- st.error(f"An error occurred: {str(e)}")
1053
 
1054
  if __name__ == "__main__":
1055
  main()
 
599
  else:
600
  return f"https://linkedin.com/in/{name_parts[0]}-profile"
601
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
602
 
603
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
604
 
605
  if __name__ == "__main__":
606
  main()