Sa-m commited on
Commit
ca41c48
·
verified ·
1 Parent(s): c5d6b98

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -58
app.py CHANGED
@@ -8,15 +8,46 @@ import google.generativeai as genai
8
  from transformers import BertTokenizer, TFBertModel
9
  import numpy as np
10
  import speech_recognition as sr
11
- # from gtts import gTTS # Not used directly in main app logic here
12
- # import pygame # Not used directly in main app logic here
13
  import time
14
  from dotenv import load_dotenv
15
  import soundfile as sf # For saving audio numpy array
 
 
 
 
 
16
 
17
  # Load environment variables
18
  load_dotenv()
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # Configure Generative AI
21
  genai.configure(api_key=os.getenv("GOOGLE_API_KEY") or "YOUR_DEFAULT_API_KEY_HERE") # Use environment variable or set a default
22
  text_model = genai.GenerativeModel("gemini-pro")
@@ -32,7 +63,7 @@ except Exception as e:
32
  model = None
33
  tokenizer = None
34
 
35
- # --- Helper Functions (Logic from Streamlit) ---
36
 
37
  def getallinfo(data):
38
  if not data or not data.strip(): # Check for None or empty/whitespace
@@ -239,7 +270,7 @@ def generate_metrics(data, answer, question):
239
  }
240
  return metrics
241
 
242
- # --- Gradio UI Components and Logic ---
243
 
244
  def process_resume(file_obj):
245
  """Handles resume upload and processing."""
@@ -484,31 +515,60 @@ def submit_interview(interview_state):
484
 
485
  return "Interview submitted successfully!", interview_state
486
 
487
- # --- Login and Navigation Logic ---
488
 
489
- def login(username, password):
490
- # Simple mock login - replace with real authentication logic
491
  # For demo, accept any non-empty username/password
492
- if username and password:
493
- welcome_msg = f"Welcome, {username}!"
 
 
 
 
 
 
 
 
494
  # Show main app, hide login
495
  return (welcome_msg,
496
  gr.update(visible=False), # login_section
497
  gr.update(visible=True), # main_app
498
- "", "", # Clear username/password inputs
499
- username) # Update user_state
500
- else:
501
- return ("Please enter username and password.",
502
- gr.update(visible=True), # login_section stays visible
503
- gr.update(visible=False), # main_app stays hidden
504
- username, password, # Keep inputs
505
- "") # user_state empty
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
 
507
  def logout():
508
  return ("", # Clear login status
509
  gr.update(visible=True), # Show login section
510
  gr.update(visible=False), # Hide main app
511
- "", "", # Clear username/password inputs
 
512
  "") # Clear user_state
513
 
514
  def navigate_to_interview():
@@ -517,22 +577,46 @@ def navigate_to_interview():
517
  def navigate_to_chat():
518
  return (gr.update(visible=False), gr.update(visible=True)) # Hide interview, show chat
519
 
 
 
 
 
 
 
 
 
 
 
 
520
  # --- Gradio Interface ---
521
 
522
  with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
523
  gr.Markdown("# 🦈 PrepGenie")
524
  # State to hold interview data
525
  interview_state = gr.State({})
526
- # State for username
527
  user_state = gr.State("")
528
 
529
  # --- Login Section ---
530
  with gr.Column(visible=True) as login_section:
531
  gr.Markdown("## Login")
532
- username_input = gr.Textbox(label="Username")
533
- password_input = gr.Textbox(label="Password", type="password")
534
  login_btn = gr.Button("Login")
535
- login_status = gr.Textbox(label="Status", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
536
 
537
  # --- Main App Sections (Initially Hidden) ---
538
  with gr.Column(visible=False) as main_app:
@@ -546,18 +630,21 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
546
  with gr.Row():
547
  with gr.Column(scale=1):
548
  interview_btn = gr.Button("Mock Interview")
549
- chat_btn = gr.Button("Chat with Resume")
 
 
 
550
  with gr.Column(scale=4):
551
  # --- Interview Section ---
552
- with gr.Column(visible=False) as interview_selection: # Define interview_selection
553
  gr.Markdown("## Mock Interview")
554
  # File Upload Section
555
  with gr.Row():
556
  with gr.Column():
557
- file_upload = gr.File(label="Upload Resume (PDF)", file_types=[".pdf"])
558
- process_btn = gr.Button("Process Resume")
559
  with gr.Column():
560
- file_status = gr.Textbox(label="Status", interactive=False)
561
 
562
  # Role Selection (Initially hidden)
563
  role_selection = gr.Dropdown(
@@ -581,54 +668,61 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
581
  feedback_display = gr.Textbox(label="Feedback", interactive=False, visible=False)
582
  metrics_display = gr.JSON(label="Metrics", visible=False)
583
 
584
- # Hidden textbox to hold processed resume data temporarily
585
- processed_resume_data_hidden = gr.Textbox(visible=False) # Renamed for clarity
586
 
587
  # --- Chat Section ---
588
- with gr.Column(visible=False) as chat_selection: # Define chat_selection
589
- gr.Markdown("## Chat with Resume (Placeholder)")
590
- gr.Markdown("This section would contain the chat interface logic from `chat.py`.")
591
- # You would integrate the chat logic here, similar to how interview is done.
592
- # For now, it's a placeholder.
593
- chat_placeholder = gr.Textbox(label="Chat Placeholder", value="Chat functionality would be integrated here.", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
594
 
595
 
596
  # Navigation buttons
597
- # Define the components for navigation *before* using them in event listeners
598
  interview_view = interview_selection
599
  chat_view = chat_selection
600
 
601
  interview_btn.click(fn=navigate_to_interview, inputs=None, outputs=[interview_view, chat_view])
602
- chat_btn.click(fn=navigate_to_chat, inputs=None, outputs=[interview_view, chat_view])
 
603
  # Update welcome message when user_state changes (basic)
604
  user_state.change(fn=lambda user: f"### Welcome, {user}!" if user else "### Welcome, User!", inputs=[user_state], outputs=[welcome_display])
605
 
606
- # --- Event Listeners ---
607
-
608
- # Process Resume
609
- process_btn.click(
610
  fn=process_resume,
611
- inputs=[file_upload],
612
  outputs=[
613
- file_status, role_selection, start_interview_btn,
614
  question_display, answer_instructions, audio_input,
615
  submit_answer_btn, next_question_btn, submit_interview_btn,
616
  answer_display, feedback_display, metrics_display,
617
- processed_resume_data_hidden # Pass processed data for next step
618
  ]
619
  )
620
 
621
  # Start Interview
622
  start_interview_btn.click(
623
  fn=start_interview,
624
- inputs=[role_selection, processed_resume_data_hidden],
625
  outputs=[
626
- file_status, question_display,
627
- # Note: Directly accessing state dict keys in outputs like interview_state["questions"] is problematic.
628
- # Gradio expects component objects or gr.Updates. We pass the state object itself.
629
- # The function returns values to update UI components and the entire state dict.
630
- # interview_state["questions"], interview_state["answers"], # Invalid
631
- # interview_state["interactions"], interview_state["metrics_list"], # Invalid
632
  # Outputs for UI updates
633
  audio_input, submit_answer_btn, next_question_btn,
634
  submit_interview_btn, feedback_display, metrics_display,
@@ -642,7 +736,7 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
642
  fn=submit_answer,
643
  inputs=[audio_input, interview_state],
644
  outputs=[
645
- file_status, answer_display, interview_state,
646
  feedback_display, feedback_display, # Update value and visibility
647
  metrics_display, metrics_display, # Update value and visibility
648
  audio_input, submit_answer_btn, next_question_btn,
@@ -655,7 +749,7 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
655
  fn=next_question,
656
  inputs=[interview_state],
657
  outputs=[
658
- file_status, question_display, interview_state,
659
  audio_input, submit_answer_btn, next_question_btn,
660
  feedback_display, metrics_display, submit_interview_btn,
661
  question_display, answer_instructions,
@@ -667,21 +761,61 @@ with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
667
  submit_interview_btn.click(
668
  fn=submit_interview,
669
  inputs=[interview_state],
670
- outputs=[file_status, interview_state]
671
  # In a full app, you might navigate to an evaluation page here
672
  )
673
 
674
- # Login/Logout Event Listeners
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
675
  login_btn.click(
676
  fn=login,
677
- inputs=[username_input, password_input],
678
- outputs=[login_status, login_section, main_app, username_input, password_input, user_state]
 
 
 
 
 
 
679
  )
680
 
681
  logout_btn.click(
682
  fn=logout,
683
  inputs=None,
684
- outputs=[login_status, login_section, main_app, username_input, password_input, user_state]
 
 
 
 
 
 
 
 
 
 
 
 
 
685
  )
686
 
687
  # Run the app
 
8
  from transformers import BertTokenizer, TFBertModel
9
  import numpy as np
10
  import speech_recognition as sr
 
 
11
  import time
12
  from dotenv import load_dotenv
13
  import soundfile as sf # For saving audio numpy array
14
+ import json
15
+
16
+ # --- Firebase Admin SDK Setup ---
17
+ import firebase_admin
18
+ from firebase_admin import credentials, auth, firestore
19
 
20
  # Load environment variables
21
  load_dotenv()
22
 
23
+ # Initialize Firebase Admin SDK (Use environment variable for credentials path or default)
24
+ firebase_credentials_path = os.getenv("FIREBASE_CREDENTIALS_PATH", "prepgenie-64134-firebase-adminsdk-fbsvc-3370ac4ab9.json")
25
+ if not firebase_admin._apps:
26
+ try:
27
+ # Try loading from file path first
28
+ cred = credentials.Certificate(firebase_credentials_path)
29
+ firebase_admin.initialize_app(cred)
30
+ print("Firebase Admin initialized successfully using credentials file.")
31
+ except FileNotFoundError:
32
+ print(f"Firebase credentials file not found at {firebase_credentials_path}. Trying environment variable...")
33
+ try:
34
+ # Fallback: Try loading from environment variable (expects JSON string)
35
+ firebase_credentials_json = os.getenv("FIREBASE_CREDENTIALS_JSON")
36
+ if firebase_credentials_json:
37
+ cred_dict = json.loads(firebase_credentials_json)
38
+ cred = credentials.Certificate(cred_dict)
39
+ firebase_admin.initialize_app(cred)
40
+ print("Firebase Admin initialized successfully using environment variable.")
41
+ else:
42
+ raise ValueError("FIREBASE_CREDENTIALS_JSON environment variable not set.")
43
+ except (json.JSONDecodeError, ValueError) as e:
44
+ print(f"Error initializing Firebase Admin: {e}")
45
+ print("Firebase authentication will not be available.")
46
+ # You might want to handle this case differently, e.g., disable features or show an error
47
+ except Exception as e:
48
+ print(f"Unexpected error initializing Firebase Admin: {e}")
49
+ print("Firebase authentication will not be available.")
50
+
51
  # Configure Generative AI
52
  genai.configure(api_key=os.getenv("GOOGLE_API_KEY") or "YOUR_DEFAULT_API_KEY_HERE") # Use environment variable or set a default
53
  text_model = genai.GenerativeModel("gemini-pro")
 
63
  model = None
64
  tokenizer = None
65
 
66
+ # --- Helper Functions (Logic from Streamlit - Interview) ---
67
 
68
  def getallinfo(data):
69
  if not data or not data.strip(): # Check for None or empty/whitespace
 
270
  }
271
  return metrics
272
 
273
+ # --- Gradio UI Components and Logic (Interview) ---
274
 
275
  def process_resume(file_obj):
276
  """Handles resume upload and processing."""
 
515
 
516
  return "Interview submitted successfully!", interview_state
517
 
518
+ # --- Login and Navigation Logic (Firebase Integrated) ---
519
 
520
+ def login(email, password):
521
+ # Simple mock login using Firebase - replace with real authentication logic
522
  # For demo, accept any non-empty username/password
523
+ # In a real app, you would verify the password against Firebase Auth (server-side)
524
+ # This is a simplified placeholder that checks if user exists.
525
+ if not email or not password:
526
+ return ("Please enter email and password.", gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), email, password, "")
527
+
528
+ try:
529
+ # Attempt to get user by email (this checks existence, not password in client-side code)
530
+ # A real implementation would involve secure server-side verification.
531
+ user = auth.get_user_by_email(email)
532
+ welcome_msg = f"Welcome, {user.display_name or user.uid}!" # Use display name or UID
533
  # Show main app, hide login
534
  return (welcome_msg,
535
  gr.update(visible=False), # login_section
536
  gr.update(visible=True), # main_app
537
+ "", "", # Clear email/password inputs
538
+ user.uid) # Update user_state with UID
539
+ except auth.UserNotFoundError:
540
+ return ("User not found. Please check your email or sign up.", gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), email, password, "")
541
+ except Exception as e:
542
+ error_msg = f"Login failed: {str(e)}"
543
+ print(error_msg)
544
+ return (error_msg, gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), email, password, "")
545
+
546
+ def signup(email, password, username):
547
+ if not email or not password or not username:
548
+ return ("Please fill all fields.", gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), email, password, username, "")
549
+
550
+ try:
551
+ # Create user in Firebase
552
+ user = auth.create_user(email=email, password=password, uid=username, display_name=username)
553
+ success_msg = f"Account created successfully for {username}!"
554
+ # Automatically log the user in or prompt for login
555
+ # Here, we'll just show success and keep them on the signup form
556
+ return (success_msg, gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), "", "", "", user.uid) # Clear inputs, set user state
557
+ except auth.UidAlreadyExistsError:
558
+ return ("Username already exists. Please choose another.", gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), email, password, username, "")
559
+ except auth.EmailAlreadyExistsError:
560
+ return ("Email already exists. Please use another email.", gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), email, password, username, "")
561
+ except Exception as e:
562
+ error_msg = f"Signup failed: {str(e)}"
563
+ print(error_msg)
564
+ return (error_msg, gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), email, password, username, "")
565
 
566
  def logout():
567
  return ("", # Clear login status
568
  gr.update(visible=True), # Show login section
569
  gr.update(visible=False), # Hide main app
570
+ gr.update(visible=False), # Hide signup section (if it was visible)
571
+ "", "", "", # Clear email/password/username inputs
572
  "") # Clear user_state
573
 
574
  def navigate_to_interview():
 
577
  def navigate_to_chat():
578
  return (gr.update(visible=False), gr.update(visible=True)) # Hide interview, show chat
579
 
580
+ # --- Import Chat Module Functions ---
581
+ # Assuming chat.py is in the same directory or correctly in the Python path
582
+ try:
583
+ from login_module import chat as chat_module
584
+ CHAT_MODULE_AVAILABLE = True
585
+ print("Chat module imported successfully.")
586
+ except ImportError as e:
587
+ print(f"Warning: Could not import chat module: {e}")
588
+ CHAT_MODULE_AVAILABLE = False
589
+ chat_module = None
590
+
591
  # --- Gradio Interface ---
592
 
593
  with gr.Blocks(title="PrepGenie - Mock Interview") as demo:
594
  gr.Markdown("# 🦈 PrepGenie")
595
  # State to hold interview data
596
  interview_state = gr.State({})
597
+ # State for username/UID
598
  user_state = gr.State("")
599
 
600
  # --- Login Section ---
601
  with gr.Column(visible=True) as login_section:
602
  gr.Markdown("## Login")
603
+ login_email_input = gr.Textbox(label="Email Address")
604
+ login_password_input = gr.Textbox(label="Password", type="password")
605
  login_btn = gr.Button("Login")
606
+ login_status = gr.Textbox(label="Login Status", interactive=False)
607
+ # Switch to Signup
608
+ switch_to_signup_btn = gr.Button("Don't have an account? Sign Up")
609
+
610
+ # --- Signup Section ---
611
+ with gr.Column(visible=False) as signup_section:
612
+ gr.Markdown("## Sign Up")
613
+ signup_email_input = gr.Textbox(label="Email Address")
614
+ signup_password_input = gr.Textbox(label="Password", type="password")
615
+ signup_username_input = gr.Textbox(label="Unique Username")
616
+ signup_btn = gr.Button("Create my account")
617
+ signup_status = gr.Textbox(label="Signup Status", interactive=False)
618
+ # Switch to Login
619
+ switch_to_login_btn = gr.Button("Already have an account? Login")
620
 
621
  # --- Main App Sections (Initially Hidden) ---
622
  with gr.Column(visible=False) as main_app:
 
630
  with gr.Row():
631
  with gr.Column(scale=1):
632
  interview_btn = gr.Button("Mock Interview")
633
+ if CHAT_MODULE_AVAILABLE:
634
+ chat_btn = gr.Button("Chat with Resume")
635
+ else:
636
+ chat_btn = gr.Button("Chat with Resume (Unavailable)", interactive=False)
637
  with gr.Column(scale=4):
638
  # --- Interview Section ---
639
+ with gr.Column(visible=False) as interview_selection:
640
  gr.Markdown("## Mock Interview")
641
  # File Upload Section
642
  with gr.Row():
643
  with gr.Column():
644
+ file_upload_interview = gr.File(label="Upload Resume (PDF)", file_types=[".pdf"])
645
+ process_btn_interview = gr.Button("Process Resume")
646
  with gr.Column():
647
+ file_status_interview = gr.Textbox(label="Status", interactive=False)
648
 
649
  # Role Selection (Initially hidden)
650
  role_selection = gr.Dropdown(
 
668
  feedback_display = gr.Textbox(label="Feedback", interactive=False, visible=False)
669
  metrics_display = gr.JSON(label="Metrics", visible=False)
670
 
671
+ # Hidden textbox to hold processed resume data temporarily for interview
672
+ processed_resume_data_hidden_interview = gr.Textbox(visible=False)
673
 
674
  # --- Chat Section ---
675
+ if CHAT_MODULE_AVAILABLE:
676
+ with gr.Column(visible=False) as chat_selection:
677
+ gr.Markdown("## Chat with Resume")
678
+ # File Upload Section (Chat uses its own upload)
679
+ with gr.Row():
680
+ with gr.Column():
681
+ file_upload_chat = gr.File(label="Upload Resume (PDF)", file_types=[".pdf"])
682
+ process_chat_btn = gr.Button("Process Resume")
683
+ with gr.Column():
684
+ file_status_chat = gr.Textbox(label="Status", interactive=False)
685
+
686
+ # Chat Section (Initially hidden)
687
+ chatbot = gr.Chatbot(label="Chat History", visible=False)
688
+ query_input = gr.Textbox(label="Ask about your resume", placeholder="Type your question here...", visible=False)
689
+ send_btn = gr.Button("Send", visible=False)
690
+ else:
691
+ with gr.Column(visible=False) as chat_selection:
692
+ gr.Markdown("## Chat with Resume (Unavailable)")
693
+ gr.Textbox(value="Chat module is not available.", interactive=False)
694
 
695
 
696
  # Navigation buttons
 
697
  interview_view = interview_selection
698
  chat_view = chat_selection
699
 
700
  interview_btn.click(fn=navigate_to_interview, inputs=None, outputs=[interview_view, chat_view])
701
+ if CHAT_MODULE_AVAILABLE:
702
+ chat_btn.click(fn=navigate_to_chat, inputs=None, outputs=[interview_view, chat_view])
703
  # Update welcome message when user_state changes (basic)
704
  user_state.change(fn=lambda user: f"### Welcome, {user}!" if user else "### Welcome, User!", inputs=[user_state], outputs=[welcome_display])
705
 
706
+ # --- Event Listeners for Interview ---
707
+ # Process Resume (Interview)
708
+ process_btn_interview.click(
 
709
  fn=process_resume,
710
+ inputs=[file_upload_interview],
711
  outputs=[
712
+ file_status_interview, role_selection, start_interview_btn,
713
  question_display, answer_instructions, audio_input,
714
  submit_answer_btn, next_question_btn, submit_interview_btn,
715
  answer_display, feedback_display, metrics_display,
716
+ processed_resume_data_hidden_interview
717
  ]
718
  )
719
 
720
  # Start Interview
721
  start_interview_btn.click(
722
  fn=start_interview,
723
+ inputs=[role_selection, processed_resume_data_hidden_interview],
724
  outputs=[
725
+ file_status_interview, question_display,
 
 
 
 
 
726
  # Outputs for UI updates
727
  audio_input, submit_answer_btn, next_question_btn,
728
  submit_interview_btn, feedback_display, metrics_display,
 
736
  fn=submit_answer,
737
  inputs=[audio_input, interview_state],
738
  outputs=[
739
+ file_status_interview, answer_display, interview_state,
740
  feedback_display, feedback_display, # Update value and visibility
741
  metrics_display, metrics_display, # Update value and visibility
742
  audio_input, submit_answer_btn, next_question_btn,
 
749
  fn=next_question,
750
  inputs=[interview_state],
751
  outputs=[
752
+ file_status_interview, question_display, interview_state,
753
  audio_input, submit_answer_btn, next_question_btn,
754
  feedback_display, metrics_display, submit_interview_btn,
755
  question_display, answer_instructions,
 
761
  submit_interview_btn.click(
762
  fn=submit_interview,
763
  inputs=[interview_state],
764
+ outputs=[file_status_interview, interview_state]
765
  # In a full app, you might navigate to an evaluation page here
766
  )
767
 
768
+ # --- Event Listeners for Chat (if available) ---
769
+ if CHAT_MODULE_AVAILABLE:
770
+ # Process Resume for Chat
771
+ process_chat_btn.click(
772
+ fn=chat_module.process_resume_chat,
773
+ inputs=[file_upload_chat],
774
+ outputs=[file_status_chat, processed_resume_data_state, query_input, send_btn, chatbot]
775
+ )
776
+
777
+ # Chat Interaction
778
+ send_btn.click(
779
+ fn=chat_module.chat_with_resume,
780
+ inputs=[query_input, processed_resume_data_state, chatbot], # chatbot provides history
781
+ outputs=[query_input, chatbot] # Update input (clear) and chatbot (new history)
782
+ )
783
+ query_input.submit( # Allow submitting with Enter key
784
+ fn=chat_module.chat_with_resume,
785
+ inputs=[query_input, processed_resume_data_state, chatbot],
786
+ outputs=[query_input, chatbot]
787
+ )
788
+
789
+ # --- Login/Logout Event Listeners ---
790
  login_btn.click(
791
  fn=login,
792
+ inputs=[login_email_input, login_password_input],
793
+ outputs=[login_status, login_section, main_app, signup_section, login_email_input, login_password_input, user_state]
794
+ )
795
+
796
+ signup_btn.click(
797
+ fn=signup,
798
+ inputs=[signup_email_input, signup_password_input, signup_username_input],
799
+ outputs=[signup_status, signup_section, login_section, main_app, signup_email_input, signup_password_input, signup_username_input, user_state]
800
  )
801
 
802
  logout_btn.click(
803
  fn=logout,
804
  inputs=None,
805
+ outputs=[login_status, login_section, main_app, signup_section, login_email_input, login_password_input, signup_username_input, user_state]
806
+ )
807
+
808
+ # Switch between Login and Signup
809
+ switch_to_signup_btn.click(
810
+ fn=lambda: (gr.update(visible=False), gr.update(visible=True)),
811
+ inputs=None,
812
+ outputs=[login_section, signup_section]
813
+ )
814
+
815
+ switch_to_login_btn.click(
816
+ fn=lambda: (gr.update(visible=True), gr.update(visible=False)),
817
+ inputs=None,
818
+ outputs=[login_section, signup_section]
819
  )
820
 
821
  # Run the app