NinjainPJs commited on
Commit
05caa21
Β·
verified Β·
1 Parent(s): 5fb9146

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +317 -74
app.py CHANGED
@@ -2,9 +2,11 @@ from openai import OpenAI, RateLimitError
2
  import streamlit as st
3
  import time
4
  import os
 
5
  from typing import Dict, Optional
 
6
 
7
- # Page configuration
8
  st.set_page_config(
9
  page_title="LinkedIn Recommendation Generator",
10
  page_icon="πŸ‘”",
@@ -12,7 +14,132 @@ st.set_page_config(
12
  initial_sidebar_state="collapsed"
13
  )
14
 
15
- # Custom CSS for professional styling
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  st.markdown("""
17
  <style>
18
  /* Import LinkedIn-style font */
@@ -246,6 +373,19 @@ st.markdown("""
246
  .form-field-container {
247
  padding: 0.5rem 0;
248
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  </style>
250
  """, unsafe_allow_html=True)
251
 
@@ -262,7 +402,6 @@ def create_star_rating(label, key, help_text=None):
262
 
263
  with col2:
264
  pass
265
-
266
  rating = st.select_slider(
267
  "",
268
  options=[1, 2, 3, 4, 5],
@@ -271,15 +410,13 @@ def create_star_rating(label, key, help_text=None):
271
  label_visibility="collapsed"
272
  )
273
 
274
- # Create visual stars
275
  stars = "".join(["⭐" if i < rating else "β˜†" for i in range(5)])
276
  st.markdown(f"<div style='font-size: 1.5rem; text-align: center; margin-top: -35px;'>{stars}</div>", unsafe_allow_html=True)
277
  return rating
278
 
279
- def generate_recommendation(employee_name, employee_type, relationship, time_worked, linkedin_url, ratings):
280
- """Generate recommendation using a simple template-based approach"""
281
 
282
- # Organize ratings into categories for analysis
283
  performance_areas = {
284
  "Technical Competence": {
285
  "Domain Knowledge": ratings['domain'],
@@ -301,39 +438,90 @@ def generate_recommendation(employee_name, employee_type, relationship, time_wor
301
  }
302
  }
303
 
304
- # Calculate category averages
305
  category_scores = {}
306
  for category, metrics in performance_areas.items():
307
  category_scores[category] = sum(metrics.values()) / len(metrics)
308
 
309
- # Identify top strengths (ratings of 4 or 5)
310
- strengths = [k.replace('_', ' ').title() for k, v in ratings.items() if v >= 4]
311
-
312
- # Generate a simple template-based recommendation
313
- avg_rating = sum(ratings.values()) / len(ratings)
314
-
315
- if avg_rating >= 4.5:
316
- performance_descriptor = "exceptional"
317
- impact_descriptor = "consistently exceeded expectations"
318
- elif avg_rating >= 4.0:
319
- performance_descriptor = "outstanding"
320
- impact_descriptor = "consistently delivered excellent results"
321
- elif avg_rating >= 3.5:
322
- performance_descriptor = "strong"
323
- impact_descriptor = "consistently met and often exceeded expectations"
324
- else:
325
- performance_descriptor = "solid"
326
- impact_descriptor = "demonstrated reliable performance"
327
-
328
- recommendation = f"""I had the pleasure of working with {employee_name} as their {relationship.lower()} for {time_worked.lower()}. During this time, {employee_name} served as a {employee_type} and {impact_descriptor}.
329
-
330
- {employee_name} demonstrated {performance_descriptor} performance across multiple areas. Their key strengths include {', '.join(strengths[:3]) if strengths else 'dedication and professionalism'}. What impressed me most was their ability to tackle complex challenges with creativity and persistence.
331
-
332
- {employee_name} was not only technically competent but also a valuable team player who contributed positively to our work environment. Their communication skills and collaborative approach made them a pleasure to work with.
333
-
334
- I would highly recommend {employee_name} for any role that values {performance_descriptor} performance and team collaboration. They would be a valuable addition to any organization."""
335
-
336
- return recommendation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
 
338
  def render_header():
339
  """Renders the main header of the application."""
@@ -368,8 +556,8 @@ def render_input_form() -> Dict:
368
  with col2:
369
  employee_type = st.selectbox(
370
  "Employee Role/Department",
371
- ["", "Software Developer", "AI Engineer", "Marketing Specialist", "Sales Representative",
372
- "Project Manager", "Data Analyst", "UI/UX Designer", "Customer Support", "Business Analyst",
373
  "Product Manager", "DevOps Engineer", "Content Creator", "HR Specialist", "Other"],
374
  key="employee_type"
375
  )
@@ -378,7 +566,7 @@ def render_input_form() -> Dict:
378
  ["", "Less than 6 months", "6 months - 1 year", "1-2 years", "2-3 years", "3-5 years", "More than 5 years"],
379
  key="time_worked"
380
  )
381
-
382
  linkedin_url = st.text_input(
383
  "Enter LinkedIn Profile URL",
384
  key="linkedin_url",
@@ -391,49 +579,49 @@ def render_input_form() -> Dict:
391
  ratings = {}
392
  st.markdown("<div class='sub-section-header'>Core Competencies</div>", unsafe_allow_html=True)
393
  ratings['domain'] = create_star_rating(
394
- "How would you rate the employee's knowledge and expertise in their specific field or role?",
395
  "domain"
396
  )
397
  ratings['problem_solving'] = create_star_rating(
398
- "How effectively does the employee address challenges and find solutions?",
399
  "problem_solving"
400
  )
401
  ratings['initiative'] = create_star_rating(
402
- "How proactive is the employee in taking initiative and contributing to company objectives?",
403
  "initiative"
404
  )
405
-
406
  st.markdown("<div class='sub-section-header'>Professional Skills</div>", unsafe_allow_html=True)
407
  ratings['adaptability'] = create_star_rating(
408
- "How well does the employee handle change or take on new responsibilities?",
409
  "adaptability"
410
  )
411
  ratings['communication'] = create_star_rating(
412
- "How clearly and professionally does the employee communicate ideas or information?",
413
  "communication"
414
  )
415
-
416
  st.markdown("<div class='sub-section-header'>Interpersonal Skills</div>", unsafe_allow_html=True)
417
  ratings['teamwork'] = create_star_rating(
418
- "How well does the employee work with colleagues or teams to achieve goals?",
419
  "teamwork"
420
  )
421
  ratings['support'] = create_star_rating(
422
- "How well does the employee support or guide others in the work environment?",
423
  "support"
424
  )
425
-
426
  st.markdown("<div class='sub-section-header'>Performance & Potential</div>", unsafe_allow_html=True)
427
  ratings['reliability'] = create_star_rating(
428
- "How consistently does the employee demonstrate dedication and reliability?",
429
  "reliability"
430
  )
431
  ratings['overall'] = create_star_rating(
432
- "How would you rate the employee's overall contribution to their role and the team?",
433
  "overall"
434
  )
435
  ratings['potential'] = create_star_rating(
436
- "How would you rate the employee's potential for further growth or advancement within the organization?",
437
  "potential"
438
  )
439
 
@@ -458,7 +646,6 @@ def render_results_section(ratings: Dict[str, int]):
458
  </div>
459
  """, unsafe_allow_html=True)
460
 
461
- # Action buttons
462
  col1, col2 = st.columns(2)
463
  with col1:
464
  if st.button("πŸ“‹ Show Text for Copying"):
@@ -470,7 +657,13 @@ def render_results_section(ratings: Dict[str, int]):
470
  st.session_state.recommendation_generated = False
471
  st.rerun()
472
 
473
- # Instructions
 
 
 
 
 
 
474
  st.markdown("""
475
  <div class="result-container">
476
  <h4 style="color: white; margin-bottom: 1rem;">πŸ“– How to Post on LinkedIn</h4>
@@ -483,8 +676,7 @@ def render_results_section(ratings: Dict[str, int]):
483
  </ol>
484
  </div>
485
  """, unsafe_allow_html=True)
486
-
487
- # Analytics section
488
  st.markdown('<h4 style="color: #0077B5;">πŸ“Š Rating Summary</h4>', unsafe_allow_html=True)
489
 
490
  col1, col2, col3, col4 = st.columns(4)
@@ -504,24 +696,65 @@ def render_results_section(ratings: Dict[str, int]):
504
 
505
  def main():
506
  """Main function to run the Streamlit application."""
507
- # Skip all authentication - go directly to the app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  render_header()
509
-
510
- # Initialize session state
511
  if 'recommendation_generated' not in st.session_state:
512
  st.session_state.recommendation_generated = False
513
  if 'generated_text' not in st.session_state:
514
  st.session_state.generated_text = ""
515
  if 'saved_linkedin_url' not in st.session_state:
516
  st.session_state.saved_linkedin_url = ""
517
-
518
  form_data = render_input_form()
519
 
520
- # Generate recommendation button
521
  col1, col2, col3 = st.columns([1, 2, 1])
522
  with col2:
523
  if st.button("πŸš€ Generate LinkedIn Recommendation", type="primary"):
524
- # Validate required fields
525
  required_fields = ["employee_name", "employee_type", "relationship", "time_worked"]
526
  if not all(form_data[field] for field in required_fields):
527
  st.error("Please fill in all required fields in the 'Basic Information' section.")
@@ -533,18 +766,28 @@ def main():
533
 
534
  recommendation = generate_recommendation(**form_data)
535
 
536
- progress_bar.progress(100, text="Done!")
537
- time.sleep(0.5)
538
- progress_bar.empty()
539
-
540
- if recommendation:
541
- st.session_state.recommendation_generated = True
542
- st.session_state.generated_text = recommendation
543
- st.session_state.saved_linkedin_url = form_data["linkedin_url"]
544
- st.success("βœ… Recommendation generated successfully!")
545
- st.rerun()
546
-
547
- # Display results in a separate container
 
 
 
 
 
 
 
 
 
 
548
  render_results_section(form_data["ratings"])
549
 
550
  if __name__ == "__main__":
 
2
  import streamlit as st
3
  import time
4
  import os
5
+ import re
6
  from typing import Dict, Optional
7
+ from datetime import datetime
8
 
9
+ # Page configuration (moved to top to ensure it's called only once)
10
  st.set_page_config(
11
  page_title="LinkedIn Recommendation Generator",
12
  page_icon="πŸ‘”",
 
14
  initial_sidebar_state="collapsed"
15
  )
16
 
17
+ # Authentication functions
18
+ def is_valid_email(email: str) -> bool:
19
+ """Validate email format"""
20
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
21
+ return re.match(pattern, email) is not None
22
+
23
+ def check_access(email: str) -> tuple[bool, str]:
24
+ """Check if user has access"""
25
+ email = email.strip().lower()
26
+
27
+ if not is_valid_email(email):
28
+ return False, "Please enter a valid email address."
29
+
30
+ # Get authorized emails from environment variables
31
+ paid_customers = os.getenv("PAID_CUSTOMERS", "").split(",")
32
+ paid_customers = [e.strip().lower() for e in paid_customers if e.strip()]
33
+ admin_email = os.getenv("ADMIN_EMAIL", "").lower()
34
+
35
+ if email in paid_customers or email == admin_email:
36
+ return True, f"βœ… Access granted! Welcome to the LinkedIn Recommendation Generator."
37
+ else:
38
+ return False, f"❌ Access denied. Your email is not authorized to use this system."
39
+
40
+ def render_login_page():
41
+ """Render the authentication page"""
42
+ # Login page CSS
43
+ st.markdown("""
44
+ <style>
45
+ @import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;600;700&display=swap');
46
+
47
+ .login-container {
48
+ max-width: 500px;
49
+ margin: 100px auto;
50
+ padding: 3rem;
51
+ background: white;
52
+ border-radius: 20px;
53
+ box-shadow: 0 8px 32px rgba(0,0,0,0.1);
54
+ text-align: center;
55
+ }
56
+
57
+ .login-header {
58
+ font-family: 'Source Sans Pro', sans-serif;
59
+ font-size: 2.5rem;
60
+ font-weight: 700;
61
+ color: #0077B5;
62
+ margin-bottom: 0.5rem;
63
+ }
64
+
65
+ .login-subtitle {
66
+ font-family: 'Source Sans Pro', sans-serif;
67
+ font-size: 1.2rem;
68
+ color: #666;
69
+ margin-bottom: 2rem;
70
+ }
71
+
72
+ .linkedin-logo {
73
+ width: 60px;
74
+ height: 60px;
75
+ background: linear-gradient(135deg, #0077B5 0%, #005885 100%);
76
+ border-radius: 15px;
77
+ display: inline-flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ margin-bottom: 1rem;
81
+ box-shadow: 0 4px 15px rgba(0,119,181,0.3);
82
+ }
83
+
84
+ .stButton > button {
85
+ background: linear-gradient(135deg, #0077B5 0%, #005885 100%);
86
+ color: white;
87
+ border: none;
88
+ padding: 0.8rem 2rem;
89
+ border-radius: 25px;
90
+ font-weight: 600;
91
+ font-family: 'Source Sans Pro', sans-serif;
92
+ font-size: 1rem;
93
+ width: 100%;
94
+ margin-top: 1rem;
95
+ }
96
+
97
+ .stTextInput > div > div > input {
98
+ background: #f8f9ff;
99
+ border: 1px solid #e8f4f8;
100
+ border-radius: 12px;
101
+ font-family: 'Source Sans Pro', sans-serif;
102
+ padding: 12px 16px;
103
+ }
104
+ </style>
105
+ """, unsafe_allow_html=True)
106
+
107
+ st.markdown("""
108
+ <div class="login-container">
109
+ <div class="linkedin-logo">
110
+ <svg width="35" height="35" viewBox="0 0 24 24" fill="white">
111
+ <path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
112
+ </svg>
113
+ </div>
114
+ <h1 class="login-header">πŸ” Access Portal</h1>
115
+ <p class="login-subtitle">Enter your registered email to access the LinkedIn Recommendation Generator</p>
116
+ </div>
117
+ """, unsafe_allow_html=True)
118
+
119
+ with st.form("login_form"):
120
+ email_input = st.text_input(
121
+ "πŸ“§ Email Address",
122
+ placeholder="Enter your registered email..."
123
+ )
124
+ login_button = st.form_submit_button("πŸš€ Access System")
125
+
126
+ if login_button:
127
+ if email_input:
128
+ success, message = check_access(email_input)
129
+ if success:
130
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
131
+ print(f"[{timestamp}] Access granted to: {email_input}")
132
+ st.session_state.authenticated = True
133
+ st.session_state.user_email = email_input.strip().lower()
134
+ st.success(message)
135
+ time.sleep(1)
136
+ st.rerun()
137
+ else:
138
+ st.error(message)
139
+ else:
140
+ st.error("Please enter your email address.")
141
+
142
+ # Original app CSS
143
  st.markdown("""
144
  <style>
145
  /* Import LinkedIn-style font */
 
373
  .form-field-container {
374
  padding: 0.5rem 0;
375
  }
376
+
377
+ .user-info {
378
+ background: linear-gradient(135deg, #0077B5 0%, #005885 100%);
379
+ color: white;
380
+ padding: 10px;
381
+ border-radius: 5px;
382
+ margin-bottom: 20px;
383
+ text-align: center;
384
+ }
385
+
386
+ .logout-btn {
387
+ background: #dc3545 !important;
388
+ }
389
  </style>
390
  """, unsafe_allow_html=True)
391
 
 
402
 
403
  with col2:
404
  pass
 
405
  rating = st.select_slider(
406
  "",
407
  options=[1, 2, 3, 4, 5],
 
410
  label_visibility="collapsed"
411
  )
412
 
 
413
  stars = "".join(["⭐" if i < rating else "β˜†" for i in range(5)])
414
  st.markdown(f"<div style='font-size: 1.5rem; text-align: center; margin-top: -35px;'>{stars}</div>", unsafe_allow_html=True)
415
  return rating
416
 
417
+ def generate_recommendation(ratings: Dict[str, int], employee_type: str, employee_name: str, relationship: str, time_worked: str, linkedin_url: str) -> Optional[str]:
418
+ """Generate recommendation using OpenRouter API with input summary"""
419
 
 
420
  performance_areas = {
421
  "Technical Competence": {
422
  "Domain Knowledge": ratings['domain'],
 
438
  }
439
  }
440
 
 
441
  category_scores = {}
442
  for category, metrics in performance_areas.items():
443
  category_scores[category] = sum(metrics.values()) / len(metrics)
444
 
445
+ strengths = [k for k, v in ratings.items() if v >= 4]
446
+
447
+ analysis_text = ""
448
+ for category, score in category_scores.items():
449
+ analysis_text += f"\n- {category}: {score:.1f}/5"
450
+
451
+ recommendation_prompt = f"""
452
+ You are an expert in writing professional LinkedIn recommendations.
453
+ Your task is to generate a recommendation for {employee_name}.
454
+ First, silently analyze the provided performance data. Do not output this analysis.
455
+ - Employee: {employee_name}
456
+ - Role: {employee_type}
457
+ - My Relationship to them: {relationship}
458
+ - Duration we worked together: {time_worked}
459
+ - Performance Summary by Category:{analysis_text}
460
+ - Employee's LinkedIn Profile (for context, do not mention the URL in the output): {linkedin_url or 'Not provided'}
461
+ - Key Strengths (rated 4 or 5): {', '.join(strengths) if strengths else 'None specified'}
462
+
463
+ Now, using that analysis, write a detailed and comprehensive LinkedIn recommendation of 200-250 words. The recommendation must:
464
+ - Be complete, with no abrupt endings or incomplete sentences.
465
+ - Be professional, warm, and authentic in tone.
466
+ - Be free of grammatical errors, spelling mistakes, or awkward phrasing.
467
+ - End with a strong, forward-looking statement about the employee's potential.
468
+
469
+ Instructions for the recommendation:
470
+ 1. Start by clearly stating the working relationship ({relationship}) and the duration ({time_worked}).
471
+ 2. Highlight their role as a {employee_type} and their key responsibilities.
472
+ 3. Weave their key strengths ({', '.join(strengths) if strengths else 'None specified'}) into a brief narrative or specific example that illustrates their positive impact (e.g., how their 'Problem Solving' skills unblocked a project or how their 'Team Collaboration' improved team morale).
473
+ 4. Conclude with a clear, confident statement about their future potential and value to any organization.
474
+ 5. Use vivid, descriptive language to make the recommendation personal and human.
475
+ 6. Ensure the recommendation is a complete text, ending with a full sentence and a period, and meets the 200-250 word requirement.
476
+ """
477
+
478
+ try:
479
+ client_kwargs = {
480
+ "base_url": "https://openrouter.ai/api/v1",
481
+ "api_key": os.environ.get('OPENROUTER_API_KEY')
482
+ }
483
+
484
+ try:
485
+ client = OpenAI(**client_kwargs)
486
+ except TypeError as e:
487
+ if "proxies" in str(e) or "unexpected keyword argument" in str(e):
488
+ import openai
489
+ openai.api_key = os.environ.get('OPENROUTER_API_KEY')
490
+ openai.base_url = "https://openrouter.ai/api/v1"
491
+ client = openai
492
+ else:
493
+ raise e
494
+
495
+ try:
496
+ final_response = client.chat.completions.create(
497
+ model="openai/gpt-3.5-turbo",
498
+ messages=[
499
+ {"role": "system", "content": "You are an expert in writing professional, warm, and authentic LinkedIn recommendations. Ensure the output is complete, polished, and free of errors."},
500
+ {"role": "user", "content": recommendation_prompt}
501
+ ],
502
+ max_tokens=400, # Increased to accommodate 200-250 words
503
+ temperature=0.75
504
+ )
505
+ return final_response.choices[0].message.content.strip()
506
+ except AttributeError:
507
+ final_response = client.ChatCompletion.create(
508
+ model="openai/gpt-3.5-turbo",
509
+ messages=[
510
+ {"role": "system", "content": "You are an expert in writing professional, warm, and authentic LinkedIn recommendations. Ensure the output is complete, polished, and free of errors."},
511
+ {"role": "user", "content": recommendation_prompt}
512
+ ],
513
+ max_tokens=400, # Increased to accommodate 200-250 words
514
+ temperature=0.75
515
+ )
516
+ return final_response['choices'][0]['message']['content'].strip()
517
+
518
+ except RateLimitError:
519
+ st.error("API rate limit or quota exceeded. Please check your OpenRouter account and billing details.")
520
+ return None
521
+ except Exception as e:
522
+ st.error(f"An error occurred while generating the recommendation: {str(e)}")
523
+ st.error(f"Error details: {type(e).__name__}")
524
+ return None
525
 
526
  def render_header():
527
  """Renders the main header of the application."""
 
556
  with col2:
557
  employee_type = st.selectbox(
558
  "Employee Role/Department",
559
+ ["", "Software Developer", "AI Engineer", "Marketing Specialist", "Sales Representative",
560
+ "Project Manager", "Data Analyst", "UI/UX Designer", "Customer Support", "Business Analyst",
561
  "Product Manager", "DevOps Engineer", "Content Creator", "HR Specialist", "Other"],
562
  key="employee_type"
563
  )
 
566
  ["", "Less than 6 months", "6 months - 1 year", "1-2 years", "2-3 years", "3-5 years", "More than 5 years"],
567
  key="time_worked"
568
  )
569
+
570
  linkedin_url = st.text_input(
571
  "Enter LinkedIn Profile URL",
572
  key="linkedin_url",
 
579
  ratings = {}
580
  st.markdown("<div class='sub-section-header'>Core Competencies</div>", unsafe_allow_html=True)
581
  ratings['domain'] = create_star_rating(
582
+ "How would you rate the employee's knowledge and expertise in their specific field or role?",
583
  "domain"
584
  )
585
  ratings['problem_solving'] = create_star_rating(
586
+ "How effectively does the employee address challenges and find solutions?",
587
  "problem_solving"
588
  )
589
  ratings['initiative'] = create_star_rating(
590
+ "How proactive is the employee in taking initiative and contributing to company objectives?",
591
  "initiative"
592
  )
593
+
594
  st.markdown("<div class='sub-section-header'>Professional Skills</div>", unsafe_allow_html=True)
595
  ratings['adaptability'] = create_star_rating(
596
+ "How well does the employee handle change or take on new responsibilities?",
597
  "adaptability"
598
  )
599
  ratings['communication'] = create_star_rating(
600
+ "How clearly and professionally does the employee communicate ideas or information?",
601
  "communication"
602
  )
603
+
604
  st.markdown("<div class='sub-section-header'>Interpersonal Skills</div>", unsafe_allow_html=True)
605
  ratings['teamwork'] = create_star_rating(
606
+ "How well does the employee work with colleagues or teams to achieve goals?",
607
  "teamwork"
608
  )
609
  ratings['support'] = create_star_rating(
610
+ "How well does the employee support or guide others in the work environment?",
611
  "support"
612
  )
613
+
614
  st.markdown("<div class='sub-section-header'>Performance & Potential</div>", unsafe_allow_html=True)
615
  ratings['reliability'] = create_star_rating(
616
+ "How consistently does the employee demonstrate dedication and reliability?",
617
  "reliability"
618
  )
619
  ratings['overall'] = create_star_rating(
620
+ "How would you rate the employee's overall contribution to their role and the team?",
621
  "overall"
622
  )
623
  ratings['potential'] = create_star_rating(
624
+ "How would you rate the employee's potential for further growth or advancement within the organization?",
625
  "potential"
626
  )
627
 
 
646
  </div>
647
  """, unsafe_allow_html=True)
648
 
 
649
  col1, col2 = st.columns(2)
650
  with col1:
651
  if st.button("πŸ“‹ Show Text for Copying"):
 
657
  st.session_state.recommendation_generated = False
658
  st.rerun()
659
 
660
+ if st.session_state.saved_linkedin_url:
661
+ st.markdown(f"""
662
+ <div style="background: linear-gradient(135deg, #0077B5 0%, #005885 100%); color: white; padding: 8px; border-radius: 5px; margin: 1rem 0; text-align: center; font-family: 'Source Sans Pro', sans-serif; font-size: 1rem;">
663
+ Click on the Employee's LinkedIn Profile: <a href="{st.session_state.saved_linkedin_url}" target="_blank" style="color: #ffffff; text-decoration: none;">{st.session_state.saved_linkedin_url}</a>
664
+ </div>
665
+ """, unsafe_allow_html=True)
666
+
667
  st.markdown("""
668
  <div class="result-container">
669
  <h4 style="color: white; margin-bottom: 1rem;">πŸ“– How to Post on LinkedIn</h4>
 
676
  </ol>
677
  </div>
678
  """, unsafe_allow_html=True)
679
+
 
680
  st.markdown('<h4 style="color: #0077B5;">πŸ“Š Rating Summary</h4>', unsafe_allow_html=True)
681
 
682
  col1, col2, col3, col4 = st.columns(4)
 
696
 
697
  def main():
698
  """Main function to run the Streamlit application."""
699
+ # Initialize session state for authentication
700
+ if 'authenticated' not in st.session_state:
701
+ st.session_state.authenticated = False
702
+ if 'user_email' not in st.session_state:
703
+ st.session_state.user_email = ""
704
+
705
+ # If not authenticated, show login page
706
+ if not st.session_state.authenticated:
707
+ render_login_page()
708
+ return
709
+
710
+ # If authenticated, show user info and logout button
711
+ col1, col2 = st.columns([4, 1])
712
+ with col1:
713
+ st.markdown(f"""
714
+ <div class="user-info">
715
+ πŸ‘€ Logged in as: <strong>{st.session_state.user_email}</strong>
716
+ </div>
717
+ """, unsafe_allow_html=True)
718
+
719
+ with col2:
720
+ if st.button("πŸšͺ Logout", key="logout_btn", help="Click to log out"):
721
+ st.session_state.authenticated = False
722
+ st.session_state.user_email = ""
723
+ st.session_state.recommendation_generated = False
724
+ st.session_state.generated_text = ""
725
+ st.session_state.saved_linkedin_url = ""
726
+ st.rerun()
727
+
728
+ # Original main function logic
729
+ api_key = os.environ.get('OPENROUTER_API_KEY')
730
+
731
+ if not api_key:
732
+ try:
733
+ if hasattr(st, 'secrets') and 'OPENROUTER_API_KEY' in st.secrets:
734
+ api_key = st.secrets['OPENROUTER_API_KEY']
735
+ os.environ['OPENROUTER_API_KEY'] = api_key
736
+ except Exception:
737
+ pass
738
+
739
+ if not api_key:
740
+ st.error("πŸ”‘ OpenRouter API key not found. Please add it to your Hugging Face Space secrets in the 'Settings' tab.")
741
+ st.info("Debug: Check that your OPENROUTER_API_KEY is properly set in the Hugging Face Space settings under 'Repository secrets'.")
742
+ st.stop()
743
+
744
  render_header()
745
+
 
746
  if 'recommendation_generated' not in st.session_state:
747
  st.session_state.recommendation_generated = False
748
  if 'generated_text' not in st.session_state:
749
  st.session_state.generated_text = ""
750
  if 'saved_linkedin_url' not in st.session_state:
751
  st.session_state.saved_linkedin_url = ""
752
+
753
  form_data = render_input_form()
754
 
 
755
  col1, col2, col3 = st.columns([1, 2, 1])
756
  with col2:
757
  if st.button("πŸš€ Generate LinkedIn Recommendation", type="primary"):
 
758
  required_fields = ["employee_name", "employee_type", "relationship", "time_worked"]
759
  if not all(form_data[field] for field in required_fields):
760
  st.error("Please fill in all required fields in the 'Basic Information' section.")
 
766
 
767
  recommendation = generate_recommendation(**form_data)
768
 
769
+ # Validate recommendation
770
+ if recommendation:
771
+ word_count = len(recommendation.split())
772
+ # Check if recommendation is within 150-300 words and ends with a period
773
+ if word_count < 150 or not recommendation.strip().endswith('.'):
774
+ st.error("The generated recommendation is incomplete or too short. Please try again.")
775
+ st.session_state.recommendation_generated = False
776
+ progress_bar.empty()
777
+ time.sleep(0.5)
778
+ st.rerun()
779
+ else:
780
+ st.session_state.recommendation_generated = True
781
+ st.session_state.generated_text = recommendation
782
+ st.session_state.saved_linkedin_url = form_data["linkedin_url"]
783
+ st.success("βœ… Recommendation generated successfully!")
784
+ progress_bar.progress(100, text="Done!")
785
+ time.sleep(0.5)
786
+ progress_bar.empty()
787
+ st.rerun()
788
+ else:
789
+ progress_bar.empty()
790
+
791
  render_results_section(form_data["ratings"])
792
 
793
  if __name__ == "__main__":