ans123 commited on
Commit
d61ddbe
·
verified ·
1 Parent(s): 29cf473

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +855 -0
app.py ADDED
@@ -0,0 +1,855 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import plotly.graph_objects as go
6
+ import plotly.express as px
7
+ from datetime import datetime, timedelta
8
+ import random
9
+ import json
10
+ import os
11
+ import time
12
+ import requests
13
+ from typing import List, Dict, Any, Optional
14
+ import logging
15
+ from dotenv import load_dotenv
16
+ import pytz
17
+ import uuid
18
+ import google.generativeai as genai
19
+ from google.generativeai.types import HarmCategory, HarmBlockThreshold
20
+ from google.generativeai.types import Tool, FunctionDeclaration, GenerateContentConfig
21
+
22
+ # Load environment variables
23
+ load_dotenv()
24
+
25
+ # Set up logging
26
+ logging.basicConfig(level=logging.INFO,
27
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
28
+ logger = logging.getLogger(__name__)
29
+
30
+ # Configure API keys
31
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "your-gemini-api-key")
32
+ SERPER_API_KEY = os.getenv("SERPER_API_KEY", "your-serper-api-key")
33
+
34
+ # Configure Google Gemini API
35
+ genai.configure(api_key=GEMINI_API_KEY)
36
+
37
+ # Constants for global app
38
+ EMOTIONS = ["Unmotivated", "Anxious", "Confused", "Excited", "Overwhelmed", "Discouraged"]
39
+ GOAL_TYPES = ["Get a job at a big company", "Find an internship", "Change careers", "Improve skills", "Network better"]
40
+ USER_DB_PATH = "user_database.json"
41
+
42
+ # Gemini model configuration
43
+ model = genai.GenerativeModel(
44
+ model_name="gemini-2.0-flash",
45
+ generation_config={
46
+ "temperature": 0.7,
47
+ "top_p": 0.95,
48
+ "top_k": 40,
49
+ "max_output_tokens": 2048,
50
+ },
51
+ safety_settings={
52
+ HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_ONLY_HIGH,
53
+ HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
54
+ HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
55
+ HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
56
+ }
57
+ )
58
+
59
+ # Function declarations for Gemini API
60
+ search_jobs = FunctionDeclaration(
61
+ name="search_jobs",
62
+ description="Search for job opportunities based on location and career goals",
63
+ parameters={
64
+ "type": "OBJECT",
65
+ "properties": {
66
+ "location": {
67
+ "type": "STRING",
68
+ "description": "The city or country where the user is located",
69
+ },
70
+ "career_goal": {
71
+ "type": "STRING",
72
+ "description": "The user's career goal or job interest",
73
+ },
74
+ "max_results": {
75
+ "type": "NUMBER",
76
+ "description": "Maximum number of job opportunities to return",
77
+ },
78
+ },
79
+ "required": ["location", "career_goal"],
80
+ },
81
+ )
82
+
83
+ generate_document = FunctionDeclaration(
84
+ name="generate_document_template",
85
+ description="Generate a document template for job applications",
86
+ parameters={
87
+ "type": "OBJECT",
88
+ "properties": {
89
+ "document_type": {
90
+ "type": "STRING",
91
+ "description": "Type of document to generate (Resume, Cover Letter, Self-introduction)",
92
+ },
93
+ "career_field": {
94
+ "type": "STRING",
95
+ "description": "The career field or industry the document is for",
96
+ },
97
+ "experience_level": {
98
+ "type": "STRING",
99
+ "description": "User's experience level (Entry, Mid, Senior)",
100
+ },
101
+ },
102
+ "required": ["document_type"],
103
+ },
104
+ )
105
+
106
+ create_routine = FunctionDeclaration(
107
+ name="create_personalized_routine",
108
+ description="Create a personalized career development routine",
109
+ parameters={
110
+ "type": "OBJECT",
111
+ "properties": {
112
+ "emotion": {
113
+ "type": "STRING",
114
+ "description": "User's current emotional state",
115
+ },
116
+ "goal": {
117
+ "type": "STRING",
118
+ "description": "User's career goal",
119
+ },
120
+ "available_time_minutes": {
121
+ "type": "NUMBER",
122
+ "description": "Available time in minutes per day",
123
+ },
124
+ "routine_length_days": {
125
+ "type": "NUMBER",
126
+ "description": "Length of routine in days",
127
+ },
128
+ },
129
+ "required": ["emotion", "goal"],
130
+ },
131
+ )
132
+
133
+ tools = [
134
+ Tool(function_declarations=[search_jobs]),
135
+ Tool(function_declarations=[generate_document]),
136
+ Tool(function_declarations=[create_routine])
137
+ ]
138
+
139
+ # User database functions
140
+ def load_user_database():
141
+ """Load user database from JSON file or create if it doesn't exist"""
142
+ try:
143
+ with open(USER_DB_PATH, 'r') as file:
144
+ return json.load(file)
145
+ except (FileNotFoundError, json.JSONDecodeError):
146
+ # Initialize empty database
147
+ db = {'users': {}}
148
+ save_user_database(db)
149
+ return db
150
+
151
+ def save_user_database(db):
152
+ """Save user database to JSON file"""
153
+ with open(USER_DB_PATH, 'w') as file:
154
+ json.dump(db, file, indent=4)
155
+
156
+ def get_user_profile(user_id):
157
+ """Get user profile from database or create new one"""
158
+ db = load_user_database()
159
+ if user_id not in db['users']:
160
+ db['users'][user_id] = {
161
+ "user_id": user_id,
162
+ "name": "",
163
+ "location": "",
164
+ "current_emotion": "",
165
+ "career_goal": "",
166
+ "progress_points": 0,
167
+ "completed_tasks": [],
168
+ "upcoming_events": [],
169
+ "routine_history": [],
170
+ "daily_emotions": [],
171
+ "joined_date": datetime.now().strftime("%Y-%m-%d")
172
+ }
173
+ save_user_database(db)
174
+ return db['users'][user_id]
175
+
176
+ def update_user_profile(user_id, updates):
177
+ """Update user profile with new information"""
178
+ db = load_user_database()
179
+ if user_id in db['users']:
180
+ for key, value in updates.items():
181
+ db['users'][user_id][key] = value
182
+ save_user_database(db)
183
+ return db['users'][user_id]
184
+
185
+ def add_task_to_user(user_id, task):
186
+ """Add a new task to user's completed tasks"""
187
+ db = load_user_database()
188
+ if user_id in db['users']:
189
+ if 'completed_tasks' not in db['users'][user_id]:
190
+ db['users'][user_id]['completed_tasks'] = []
191
+
192
+ task_with_date = {
193
+ "task": task,
194
+ "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
195
+ }
196
+ db['users'][user_id]['completed_tasks'].append(task_with_date)
197
+ db['users'][user_id]['progress_points'] += random.randint(10, 25)
198
+ save_user_database(db)
199
+ return db['users'][user_id]
200
+
201
+ def add_emotion_record(user_id, emotion):
202
+ """Add a new emotion record to user's daily emotions"""
203
+ db = load_user_database()
204
+ if user_id in db['users']:
205
+ if 'daily_emotions' not in db['users'][user_id]:
206
+ db['users'][user_id]['daily_emotions'] = []
207
+
208
+ emotion_record = {
209
+ "emotion": emotion,
210
+ "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
211
+ }
212
+ db['users'][user_id]['daily_emotions'].append(emotion_record)
213
+ db['users'][user_id]['current_emotion'] = emotion
214
+ save_user_database(db)
215
+ return db['users'][user_id]
216
+
217
+ def add_routine_to_user(user_id, routine):
218
+ """Add a new routine to user's routine history"""
219
+ db = load_user_database()
220
+ if user_id in db['users']:
221
+ if 'routine_history' not in db['users'][user_id]:
222
+ db['users'][user_id]['routine_history'] = []
223
+
224
+ routine_with_date = {
225
+ "routine": routine,
226
+ "start_date": datetime.now().strftime("%Y-%m-%d"),
227
+ "end_date": (datetime.now() + timedelta(days=routine.get('days', 7))).strftime("%Y-%m-%d"),
228
+ "completion": 0
229
+ }
230
+ db['users'][user_id]['routine_history'].append(routine_with_date)
231
+ save_user_database(db)
232
+ return db['users'][user_id]
233
+
234
+ # API Helper Functions
235
+ def search_jobs_with_serper(query, location, max_results=5):
236
+ """Search for job opportunities using Serper API"""
237
+ try:
238
+ headers = {
239
+ 'X-API-KEY': SERPER_API_KEY,
240
+ 'Content-Type': 'application/json'
241
+ }
242
+
243
+ params = {
244
+ 'q': f"{query} jobs in {location}",
245
+ 'num': max_results
246
+ }
247
+
248
+ response = requests.get(
249
+ 'https://serper.dev/search',
250
+ headers=headers,
251
+ params=params
252
+ )
253
+
254
+ if response.status_code == 200:
255
+ data = response.json()
256
+ # Extract job listings from search results
257
+ job_results = []
258
+
259
+ # Process organic results
260
+ if 'organic' in data:
261
+ for item in data['organic']:
262
+ if 'title' in item and 'link' in item and 'snippet' in item:
263
+ # Check if it looks like a job listing
264
+ if any(keyword in item['title'].lower() for keyword in ['job', 'career', 'position', 'hiring', 'work']):
265
+ job_results.append({
266
+ 'title': item['title'],
267
+ 'company': extract_company_from_title(item['title']),
268
+ 'description': item['snippet'],
269
+ 'link': item['link'],
270
+ 'location': location,
271
+ 'date_posted': 'Recent' # Serper doesn't provide this directly
272
+ })
273
+
274
+ return job_results
275
+ else:
276
+ logger.error(f"Error from Serper API: {response.status_code} - {response.text}")
277
+ return []
278
+ except Exception as e:
279
+ logger.error(f"Exception in search_jobs_with_serper: {str(e)}")
280
+ return []
281
+
282
+ def extract_company_from_title(title):
283
+ """Extract company name from job title if possible"""
284
+ # This is a simple heuristic and can be improved
285
+ if ' at ' in title:
286
+ return title.split(' at ')[1].strip()
287
+ if ' - ' in title:
288
+ return title.split(' - ')[1].strip()
289
+ return "Unknown Company"
290
+
291
+ def get_ai_response(user_id, user_input, context=None):
292
+ """Get AI response using Google Gemini API"""
293
+ try:
294
+ user_profile = get_user_profile(user_id)
295
+
296
+ system_instruction = """
297
+ You are Aishura, an emotionally intelligent AI career assistant. Your goal is to empathize with the user's emotions
298
+ and provide realistic information and actionable suggestions. Follow this structure:
299
+ 1. Recognize and acknowledge the user's emotion
300
+ 2. Respond with high-empathy message
301
+ 3. Suggest specific action based on their input
302
+ 4. Offer document support or routine
303
+
304
+ Remember to be proactive and preemptive - suggest actions before the user asks.
305
+ """
306
+
307
+ # Build conversation context
308
+ messages = []
309
+ if context:
310
+ messages.extend(context)
311
+
312
+ # Add user profile information as context
313
+ profile_context = f"""
314
+ User Profile Information:
315
+ - Current emotion: {user_profile.get('current_emotion', '')}
316
+ - Career goal: {user_profile.get('career_goal', '')}
317
+ - Location: {user_profile.get('location', '')}
318
+ """
319
+
320
+ messages.append({"role": "user", "parts": [profile_context]})
321
+ messages.append({"role": "user", "parts": [user_input]})
322
+
323
+ response = model.generate_content(
324
+ messages,
325
+ generation_config={"temperature": 0.7},
326
+ system_instruction=system_instruction,
327
+ tools=tools
328
+ )
329
+
330
+ return response.text
331
+ except Exception as e:
332
+ logger.error(f"Error in get_ai_response: {str(e)}")
333
+ return "I apologize, but I'm having trouble processing your request right now. Please try again later."
334
+
335
+ def create_personalized_routine_with_ai(user_id, emotion, goal, available_time=60, days=7):
336
+ """Create a personalized routine using AI"""
337
+ try:
338
+ prompt = f"""
339
+ Create a personalized {days}-day career development routine for a user who is feeling {emotion} and has a goal to {goal}.
340
+ They have about {available_time} minutes per day to dedicate to this routine.
341
+
342
+ For each day, suggest 1-3 specific tasks that will help them make progress toward their goal while considering their emotional state.
343
+
344
+ For each task provide:
345
+ 1. Task name
346
+ 2. Duration in minutes
347
+ 3. Points value (between 10-50)
348
+ 4. A brief description of why this task is valuable
349
+
350
+ Format the routine as a JSON object.
351
+ """
352
+
353
+ response = model.generate_content(prompt)
354
+ routine_text = response.text
355
+
356
+ # Extract JSON portion from the response
357
+ try:
358
+ # Find JSON content between ```json and ``` if present
359
+ if "```json" in routine_text and "```" in routine_text.split("```json")[1]:
360
+ json_str = routine_text.split("```json")[1].split("```")[0].strip()
361
+ else:
362
+ # Otherwise try to find anything that looks like JSON
363
+ import re
364
+ json_match = re.search(r'(\{.*\})', routine_text, re.DOTALL)
365
+ if json_match:
366
+ json_str = json_match.group(1)
367
+ else:
368
+ json_str = routine_text
369
+
370
+ routine = json.loads(json_str)
371
+
372
+ # Add to user's routines
373
+ user_profile = add_routine_to_user(user_id, routine)
374
+ return routine
375
+ except json.JSONDecodeError:
376
+ logger.error(f"Failed to parse JSON from AI response: {routine_text}")
377
+ # Fallback to a basic routine
378
+ return generate_basic_routine(emotion, goal, available_time, days)
379
+ except Exception as e:
380
+ logger.error(f"Error in create_personalized_routine_with_ai: {str(e)}")
381
+ # Fallback to a basic routine
382
+ return generate_basic_routine(emotion, goal, available_time, days)
383
+
384
+ def generate_basic_routine(emotion, goal, available_time=60, days=7):
385
+ """Generate a basic routine as fallback"""
386
+ routine_types = {
387
+ "job_search": [
388
+ {"name": "Research target companies", "points": 10, "duration": 20, "description": "Identify potential employers that align with your career goals"},
389
+ {"name": "Update LinkedIn profile", "points": 15, "duration": 30, "description": "Keep your professional presence current and compelling"},
390
+ {"name": "Practice interview questions", "points": 20, "duration": 45, "description": "Build confidence and prepare for upcoming opportunities"},
391
+ {"name": "Reach out to a contact", "points": 25, "duration": 15, "description": "Grow your network and gather industry insights"}
392
+ ],
393
+ "skill_building": [
394
+ {"name": "Complete one tutorial", "points": 20, "duration": 60, "description": "Develop practical skills in your field"},
395
+ {"name": "Read industry article", "points": 10, "duration": 15, "description": "Stay current with trends and developments"},
396
+ {"name": "Work on portfolio project", "points": 30, "duration": 90, "description": "Create tangible evidence of your abilities"},
397
+ {"name": "Watch expert talk", "points": 15, "duration": 30, "description": "Learn from leaders in your field"}
398
+ ],
399
+ "motivation": [
400
+ {"name": "Write in gratitude journal", "points": 10, "duration": 10, "description": "Cultivate a positive mindset to enhance motivation"},
401
+ {"name": "Set 3 goals for the day", "points": 15, "duration": 15, "description": "Focus your energy on achievable tasks"},
402
+ {"name": "Exercise break", "points": 20, "duration": 20, "description": "Boost energy and mood with physical activity"},
403
+ {"name": "Reflect on progress", "points": 15, "duration": 15, "description": "Acknowledge achievements and identify next steps"}
404
+ ]
405
+ }
406
+
407
+ # Select routine type based on goal
408
+ if "job" in goal.lower() or "company" in goal.lower():
409
+ routine_type = "job_search"
410
+ elif "skill" in goal.lower() or "learn" in goal.lower():
411
+ routine_type = "skill_building"
412
+ else:
413
+ # Default to motivation if feeling negative emotions
414
+ if emotion.lower() in ["unmotivated", "anxious", "confused", "overwhelmed", "discouraged"]:
415
+ routine_type = "motivation"
416
+ else:
417
+ routine_type = random.choice(list(routine_types.keys()))
418
+
419
+ # Create daily plan
420
+ daily_tasks = []
421
+ for day in range(1, days + 1):
422
+ # Randomly select 1-3 tasks for the day that fit within available time
423
+ available_tasks = routine_types[routine_type].copy()
424
+ random.shuffle(available_tasks)
425
+ day_tasks = []
426
+ remaining_time = available_time
427
+
428
+ for task in available_tasks:
429
+ if task["duration"] <= remaining_time and len(day_tasks) < 3:
430
+ day_tasks.append(task)
431
+ remaining_time -= task["duration"]
432
+
433
+ if remaining_time < 10 or len(day_tasks) >= 3:
434
+ break
435
+
436
+ daily_tasks.append({
437
+ "day": day,
438
+ "tasks": day_tasks
439
+ })
440
+
441
+ routine = {
442
+ "name": f"{days}-Day {routine_type.replace('_', ' ').title()} Plan",
443
+ "description": f"A personalized routine to help you {goal} while managing feelings of {emotion}.",
444
+ "days": days,
445
+ "daily_tasks": daily_tasks
446
+ }
447
+
448
+ return routine
449
+
450
+ def generate_document_template_with_ai(document_type, career_field="", experience_level=""):
451
+ """Generate document templates using AI"""
452
+ try:
453
+ prompt = f"""
454
+ Create a detailed template for a {document_type} for someone in the {career_field} field
455
+ with {experience_level} experience level.
456
+
457
+ The template should include all necessary sections and sample content that can be replaced.
458
+ Format it in markdown.
459
+ """
460
+
461
+ response = model.generate_content(prompt)
462
+ return response.text
463
+ except Exception as e:
464
+ logger.error(f"Error in generate_document_template_with_ai: {str(e)}")
465
+ return f"Error generating {document_type} template. Please try again later."
466
+
467
+ # Chart and visualization functions
468
+ def create_emotion_chart(user_id):
469
+ """Create a chart of user's emotions over time"""
470
+ user_profile = get_user_profile(user_id)
471
+ emotion_records = user_profile.get('daily_emotions', [])
472
+
473
+ if not emotion_records:
474
+ # Return empty chart if no data
475
+ fig = px.line(title="Emotion Tracking: No data available yet")
476
+ return fig
477
+
478
+ # Prepare data
479
+ emotion_values = {
480
+ "Unmotivated": 1,
481
+ "Anxious": 2,
482
+ "Confused": 3,
483
+ "Discouraged": 4,
484
+ "Overwhelmed": 5,
485
+ "Excited": 6
486
+ }
487
+
488
+ dates = []
489
+ emotion_scores = []
490
+ emotion_names = []
491
+
492
+ for record in emotion_records:
493
+ dates.append(datetime.strptime(record['date'], "%Y-%m-%d %H:%M:%S"))
494
+ emotion = record['emotion']
495
+ emotion_names.append(emotion)
496
+ emotion_scores.append(emotion_values.get(emotion, 3))
497
+
498
+ df = pd.DataFrame({
499
+ 'Date': dates,
500
+ 'Emotion Score': emotion_scores,
501
+ 'Emotion': emotion_names
502
+ })
503
+
504
+ # Create chart
505
+ fig = px.line(df, x='Date', y='Emotion Score', markers=True,
506
+ labels={"Emotion Score": "Emotional State"},
507
+ title="Your Emotional Journey")
508
+
509
+ # Add emotion names as hover text
510
+ fig.update_traces(hovertemplate='%{x}<br>Feeling: %{text}', text=df['Emotion'])
511
+
512
+ # Customize y-axis to show emotion names instead of numbers
513
+ fig.update_yaxes(
514
+ tickvals=list(emotion_values.values()),
515
+ ticktext=list(emotion_values.keys())
516
+ )
517
+
518
+ return fig
519
+
520
+ def create_progress_chart(user_id):
521
+ """Create a chart showing user's progress over time"""
522
+ user_profile = get_user_profile(user_id)
523
+ tasks = user_profile.get('completed_tasks', [])
524
+
525
+ if not tasks:
526
+ # Return empty chart if no data
527
+ fig = px.line(title="Progress Tracking: No data available yet")
528
+ return fig
529
+
530
+ # Prepare data
531
+ dates = []
532
+ points = []
533
+ cumulative_points = 0
534
+ task_labels = []
535
+
536
+ for task in tasks:
537
+ dates.append(datetime.strptime(task['date'], "%Y-%m-%d %H:%M:%S"))
538
+ # Increment points (assuming each task has inherent points)
539
+ cumulative_points += 20
540
+ points.append(cumulative_points)
541
+ task_labels.append(task['task'])
542
+
543
+ df = pd.DataFrame({
544
+ 'Date': dates,
545
+ 'Points': points,
546
+ 'Task': task_labels
547
+ })
548
+
549
+ # Create chart
550
+ fig = px.line(df, x='Date', y='Points', markers=True,
551
+ title="Your Career Journey Progress")
552
+
553
+ # Add task names as hover text
554
+ fig.update_traces(hovertemplate='%{x}<br>Points: %{y}<br>Task: %{text}', text=df['Task'])
555
+
556
+ return fig
557
+
558
+ def create_routine_completion_gauge(user_id):
559
+ """Create a gauge chart showing routine completion percentage"""
560
+ user_profile = get_user_profile(user_id)
561
+ routines = user_profile.get('routine_history', [])
562
+
563
+ if not routines:
564
+ # Return empty chart if no data
565
+ fig = go.Figure()
566
+ fig.add_annotation(text="No active routines yet", showarrow=False)
567
+ return fig
568
+
569
+ # Get the most recent routine
570
+ latest_routine = routines[-1]
571
+ completion = latest_routine.get('completion', 0)
572
+
573
+ # Create gauge chart
574
+ fig = go.Figure(go.Indicator(
575
+ mode = "gauge+number",
576
+ value = completion,
577
+ domain = {'x': [0, 1], 'y': [0, 1]},
578
+ title = {'text': "Current Routine Completion"},
579
+ gauge = {
580
+ 'axis': {'range': [None, 100]},
581
+ 'bar': {'color': "darkblue"},
582
+ 'steps': [
583
+ {'range': [0, 30], 'color': "lightgray"},
584
+ {'range': [30, 70], 'color': "gray"},
585
+ {'range': [70, 100], 'color': "darkgray"}
586
+ ],
587
+ 'threshold': {
588
+ 'line': {'color': "red", 'width': 4},
589
+ 'thickness': 0.75,
590
+ 'value': 90
591
+ }
592
+ }
593
+ ))
594
+
595
+ return fig
596
+
597
+ # Gradio interface components
598
+ def create_interface():
599
+ """Create the Gradio interface for Aishura MVP"""
600
+
601
+ # Generate a unique user ID for this session
602
+ session_user_id = str(uuid.uuid4())
603
+
604
+ # Welcome page
605
+ def welcome(name, location, emotion, goal):
606
+ if not name or not location or not emotion or not goal:
607
+ return ("Please fill out all fields to continue.",
608
+ gr.update(visible=True),
609
+ gr.update(visible=False))
610
+
611
+ # Update user profile
612
+ update_user_profile(session_user_id, {
613
+ "name": name,
614
+ "location": location,
615
+ "career_goal": goal
616
+ })
617
+
618
+ # Record emotion
619
+ add_emotion_record(session_user_id, emotion)
620
+
621
+ # Generate initial AI response
622
+ response = get_ai_response(
623
+ session_user_id,
624
+ f"I'm {name} from {location}. I'm feeling {emotion} and my career goal is to {goal}."
625
+ )
626
+
627
+ return (response,
628
+ gr.update(visible=False),
629
+ gr.update(visible=True))
630
+
631
+ # Chat function
632
+ def chat(message, history):
633
+ # Record the user message for context
634
+ if not history:
635
+ history = []
636
+
637
+ # Add user's message to history
638
+ history.append({"role": "user", "parts": [message]})
639
+
640
+ # Get AI response
641
+ response = get_ai_response(session_user_id, message, history)
642
+
643
+ # Add AI's response to history
644
+ history.append({"role": "assistant", "parts": [response]})
645
+
646
+ return history, ""
647
+
648
+ # Function to search for jobs
649
+ def search_jobs_interface(query, location, max_results=5):
650
+ jobs = search_jobs_with_serper(query, location, int(max_results))
651
+
652
+ if not jobs:
653
+ return "No job opportunities found. Try adjusting your search terms."
654
+
655
+ result = "## Job Opportunities Found\n\n"
656
+ for i, job in enumerate(jobs, 1):
657
+ result += f"### {i}. {job['title']}\n"
658
+ result += f"**Company:** {job['company']}\n"
659
+ result += f"**Location:** {job['location']}\n"
660
+ result += f"**Description:** {job['description']}\n"
661
+ result += f"**Link:** [Apply Here]({job['link']})\n\n"
662
+
663
+ return result
664
+
665
+ # Function to generate document templates
666
+ def generate_template(document_type, career_field, experience_level):
667
+ template = generate_document_template_with_ai(document_type, career_field, experience_level)
668
+ return template
669
+
670
+ # Function to create personal routine
671
+ def create_personal_routine(emotion, goal, available_time, days):
672
+ routine = create_personalized_routine_with_ai(
673
+ session_user_id, emotion, goal, int(available_time), int(days)
674
+ )
675
+
676
+ # Format routine for display
677
+ result = f"# Your {routine['name']}\n\n"
678
+ result += f"{routine['description']}\n\n"
679
+
680
+ for day_plan in routine['daily_tasks']:
681
+ result += f"## Day {day_plan['day']}\n\n"
682
+ for task in day_plan['tasks']:
683
+ result += f"- **{task['name']}** ({task['duration']} mins, {task['points']} points)\n"
684
+ result += f" *{task['description']}*\n\n"
685
+
686
+ # Add the routine to user's profile
687
+ add_routine_to_user(session_user_id, routine)
688
+
689
+ return result
690
+
691
+ # Function to mark a task as complete
692
+ def complete_task(task_name):
693
+ if not task_name:
694
+ return "Please enter a task name."
695
+
696
+ user_profile = add_task_to_user(session_user_id, task_name)
697
+
698
+ # Update completion percentage of current routine
699
+ if user_profile.get('routine_history'):
700
+ latest_routine = user_profile['routine_history'][-1]
701
+ # Simple approach: increase completion by random amount between 5-15%
702
+ new_completion = min(100, latest_routine.get('completion', 0) + random.randint(5, 15))
703
+ latest_routine['completion'] = new_completion
704
+ update_user_profile(session_user_id, {"routine_history": user_profile['routine_history']})
705
+
706
+ # Create updated charts
707
+ emotion_fig = create_emotion_chart(session_user_id)
708
+ progress_fig = create_progress_chart(session_user_id)
709
+ gauge_fig = create_routine_completion_gauge(session_user_id)
710
+
711
+ return (
712
+ f"Task '{task_name}' completed! You earned {random.randint(10, 25)} points.",
713
+ "",
714
+ emotion_fig,
715
+ progress_fig,
716
+ gauge_fig
717
+ )
718
+
719
+ # Function to update emotion
720
+ def update_emotion(emotion):
721
+ add_emotion_record(session_user_id, emotion)
722
+
723
+ # Create updated emotion chart
724
+ emotion_fig = create_emotion_chart(session_user_id)
725
+
726
+ return (
727
+ f"Your emotional state has been updated to: {emotion}",
728
+ emotion_fig
729
+ )
730
+
731
+ # Create the interface
732
+ with gr.Blocks(theme=gr.themes.Soft()) as app:
733
+ gr.Markdown("# Aishura - Your AI Career Assistant")
734
+
735
+ # Welcome page
736
+ with gr.Group(visible=True) as welcome_group:
737
+ gr.Markdown("## Welcome to Aishura")
738
+ gr.Markdown("Let's start by getting to know you a little better.")
739
+
740
+ name_input = gr.Textbox(label="Your Name")
741
+ location_input = gr.Textbox(label="Your Location (City/Country)")
742
+ emotion_dropdown = gr.Dropdown(choices=EMOTIONS, label="How are you feeling today?")
743
+ goal_dropdown = gr.Dropdown(choices=GOAL_TYPES, label="What's your career goal?")
744
+
745
+ welcome_button = gr.Button("Get Started")
746
+ welcome_output = gr.Markdown()
747
+
748
+ # Main interface
749
+ with gr.Group(visible=False) as main_interface:
750
+ with gr.Tabs() as tabs:
751
+ # Chat tab
752
+ with gr.TabItem("Chat with Aishura"):
753
+ chatbot = gr.Chatbot(height=400, avatar_images=["👤", "🤖"])
754
+ msg = gr.Textbox(show_label=False, placeholder="Type your message here...", container=False)
755
+
756
+ msg.submit(chat, [msg, chatbot], [chatbot, msg])
757
+
758
+ # Job Search tab
759
+ with gr.TabItem("Find Opportunities"):
760
+ gr.Markdown("## Search for Job Opportunities")
761
+ job_query = gr.Textbox(label="What kind of job are you looking for?")
762
+ job_location = gr.Textbox(label="Location")
763
+ job_results = gr.Slider(minimum=5, maximum=20, value=10, step=5, label="Number of Results")
764
+
765
+ search_button = gr.Button("Search")
766
+ job_output = gr.Markdown()
767
+
768
+ search_button.click(search_jobs_interface, [job_query, job_location, job_results], job_output)
769
+
770
+ # Document Templates tab
771
+ with gr.TabItem("Document Templates"):
772
+ gr.Markdown("## Generate Document Templates")
773
+ doc_type = gr.Dropdown(
774
+ choices=["Resume", "Cover Letter", "Self-Introduction", "LinkedIn Profile", "Portfolio"],
775
+ label="Document Type"
776
+ )
777
+ career_field = gr.Textbox(label="Career Field/Industry")
778
+ experience = gr.Dropdown(
779
+ choices=["Entry Level", "Mid-Career", "Senior"],
780
+ label="Experience Level"
781
+ )
782
+
783
+ template_button = gr.Button("Generate Template")
784
+ template_output = gr.Markdown()
785
+
786
+ template_button.click(generate_template, [doc_type, career_field, experience], template_output)
787
+
788
+ # Personal Routine tab
789
+ with gr.TabItem("Personal Routine"):
790
+ gr.Markdown("## Create Your Personal Development Routine")
791
+ routine_emotion = gr.Dropdown(choices=EMOTIONS, label="Current Emotional State")
792
+ routine_goal = gr.Textbox(label="What specific goal are you working toward?")
793
+ time_available = gr.Slider(minimum=15, maximum=120, value=60, step=15, label="Minutes Available Per Day")
794
+ routine_days = gr.Slider(minimum=3, maximum=30, value=7, step=1, label="Length of Routine (Days)")
795
+
796
+ routine_button = gr.Button("Create Routine")
797
+ routine_output = gr.Markdown()
798
+
799
+ routine_button.click(create_personal_routine,
800
+ [routine_emotion, routine_goal, time_available, routine_days],
801
+ routine_output)
802
+
803
+ # Progress Tracking tab
804
+ with gr.TabItem("Track Progress"):
805
+ with gr.Row():
806
+ with gr.Column():
807
+ gr.Markdown("## Mark Tasks as Complete")
808
+ task_input = gr.Textbox(label="Enter Task Name")
809
+ complete_button = gr.Button("Mark as Complete")
810
+ task_output = gr.Markdown()
811
+
812
+ with gr.Column():
813
+ gr.Markdown("## Update Your Emotional State")
814
+ new_emotion = gr.Dropdown(choices=EMOTIONS, label="How are you feeling now?")
815
+ emotion_button = gr.Button("Update")
816
+ emotion_output = gr.Markdown()
817
+
818
+ with gr.Row():
819
+ with gr.Column():
820
+ emotion_chart = gr.Plot(label="Emotional Journey")
821
+
822
+ with gr.Column():
823
+ progress_chart = gr.Plot(label="Progress Journey")
824
+
825
+ with gr.Row():
826
+ gauge_chart = gr.Plot(label="Routine Completion")
827
+
828
+ complete_button.click(
829
+ complete_task,
830
+ [task_input],
831
+ [task_output, task_input, emotion_chart, progress_chart, gauge_chart]
832
+ )
833
+
834
+ emotion_button.click(
835
+ update_emotion,
836
+ [new_emotion],
837
+ [emotion_output, emotion_chart]
838
+ )
839
+
840
+ # Welcome button action
841
+ welcome_button.click(
842
+ welcome,
843
+ [name_input, location_input, emotion_dropdown, goal_dropdown],
844
+ [welcome_output, welcome_group, main_interface]
845
+ )
846
+
847
+ return app
848
+
849
+ # Main function to launch the app
850
+ def main():
851
+ app = create_interface()
852
+ app.launch(share=True)
853
+
854
+ if __name__ == "__main__":
855
+ main()