MalikShehram commited on
Commit
a627782
·
verified ·
1 Parent(s): 6ad70ca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +414 -229
app.py CHANGED
@@ -1,31 +1,51 @@
1
  import gradio as gr
2
- import requests
3
  import os
 
 
 
 
 
 
 
 
 
 
4
 
5
- # Load API key from Gradio Secrets
6
- GROQ_API_KEY = os.getenv("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")
 
 
7
 
8
- # Constants
9
  COUNTRIES = [
10
- "Pakistan", "India", "Nigeria", "United States", "United Kingdom",
11
- "Germany", "Canada", "Australia", "Bangladesh", "Egypt"
12
  ]
13
  DEGREES = ["Bachelor", "Master", "PhD"]
14
- EMAIL_PURPOSES = ["Inquiry", "Follow-up", "Recommendation"]
15
  COMMON_QUESTIONS = [
16
  "Tell me about yourself.",
17
  "Why are you interested in this scholarship?",
18
- "What are your strengths and weaknesses?",
19
- "Describe a challenge you’ve overcome.",
20
- "Where do you see yourself in 5 years?"
 
21
  ]
22
 
23
- # === Helper to call Groq API ===
 
 
 
 
 
 
 
24
  def call_groq_api(messages, model="llama3-8b-8192", timeout=60):
 
25
  if not GROQ_API_KEY:
26
- return "Error: GROQ_API_KEY is missing. Set it in Gradio Secrets."
27
  try:
28
- resp = requests.post(
29
  "https://api.groq.com/openai/v1/chat/completions",
30
  headers={
31
  "Authorization": f"Bearer {GROQ_API_KEY}",
@@ -34,236 +54,401 @@ def call_groq_api(messages, model="llama3-8b-8192", timeout=60):
34
  json={"model": model, "messages": messages},
35
  timeout=timeout
36
  )
37
- if resp.status_code != 200:
38
- return f"API error {resp.status_code}: {resp.text}"
39
- data = resp.json()
40
- choices = data.get("choices")
41
- if not choices:
42
- return f"No choices returned. Full response: {data}"
43
- return choices[0]["message"]["content"].strip()
44
  except Exception as e:
45
- return f"Request failed: {e}"
46
 
47
- # === Feature Functions ===
48
 
49
- def ai_generate_scholarships(country, university):
50
- if not country or not university:
51
- return "Please select a country and enter a university."
52
- system = (
53
- "You are ScholarGPT, an expert assistant that helps students explore scholarships. "
54
- "Provide 3‑5 fully funded scholarships formatted as HTML cards."
55
- )
56
- user = f"List 3‑5 fully funded scholarships at {university} in {country}."
57
- return call_groq_api([
58
- {"role": "system", "content": system},
59
- {"role": "user", "content": user}
60
- ])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- def scholarship_finder(country, degree, gpa, field):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  if not all([country, degree, field]) or gpa is None:
64
- return "Please fill in all fields."
65
- try:
66
- gpa_val = float(gpa)
67
- except:
68
- return "Enter a valid numeric GPA."
69
- system = "You are ScholarGPT, an expert assistant that helps students find scholarships."
70
- user = (
71
- f"I am from {country}, seeking {degree} scholarships; "
72
- f"GPA: {gpa_val}, Field: {field}. "
73
- "List matching scholarships with name, university, country, required GPA, and field."
74
  )
75
- return call_groq_api([
76
- {"role": "system", "content": system},
77
- {"role": "user", "content": user}
78
- ])
79
-
80
- def sop_generator(full_name, university, degree_program, background, academic_goals, ambitions):
81
- if not all([full_name.strip(), university.strip(), degree_program.strip(),
82
- background.strip(), academic_goals.strip(), ambitions.strip()]):
83
- return "Please fill in all fields."
84
- system = "You are ScholarGPT, an expert assistant that writes formal SOPs for scholarship applications."
85
- user = (
86
- f"Write a Statement of Purpose for {full_name} applying to {university} for {degree_program}. "
87
- f"Background: {background}. Goals: {academic_goals}. Ambitions: {ambitions}."
 
 
 
 
 
 
88
  )
89
- return call_groq_api([
90
- {"role": "system", "content": system},
91
- {"role": "user", "content": user}
92
- ], timeout=90)
93
-
94
- def eligibility_checker(gpa, country, degree_completed):
95
- if gpa is None or not country or not degree_completed:
96
- return "Please fill in all fields."
97
- try:
98
- gpa_val = float(gpa)
99
- except:
100
- return "Enter a valid numeric GPA."
101
- if gpa_val >= 3.0 and degree_completed == "Bachelor":
102
- return f" Eligible! (GPA: {gpa_val}, Degree: {degree_completed})"
103
- else:
104
- return f" Not eligible. (GPA: {gpa_val}, Degree: {degree_completed})"
105
-
106
- def mentor_chat(user_message, chat_history):
107
- history = chat_history or []
108
- history.append(("user", user_message))
109
- messages = [{"role": "system", "content": "You are MentorGPT, a friendly academic mentor."}]
110
- for role, msg in history:
111
- messages.append({"role": role, "content": msg})
112
- reply = call_groq_api(messages)
113
- history.append(("assistant", reply))
114
- # Convert to list of (user, assistant) pairs
115
- pairs = [(history[i][1], history[i+1][1]) for i in range(0, len(history)-1, 2)]
116
- return pairs, history
117
-
118
- def email_template(recipient, university, purpose):
119
- if not all([recipient.strip(), university.strip(), purpose.strip()]):
120
- return "Please fill in all fields."
121
- system = "You are ScholarGPT, an assistant that drafts formal academic emails."
122
- user = (
123
- f"Draft a formal email to {recipient} at {university}. Purpose: {purpose}. "
124
- "Include subject, greeting, body, and closing."
125
  )
126
- return call_groq_api([
127
- {"role": "system", "content": system},
128
- {"role": "user", "content": user}
129
- ])
130
-
131
- def sop_resume_generator(university, field_study, background):
132
- if not all([university.strip(), field_study.strip(), background.strip()]):
133
- return "", ""
134
- system = "You are ScholarGPT, an assistant that generates SOPs and resumes."
135
- user = (
136
- f"Generate an SOP and a resume draft for someone applying to {university} in {field_study}. "
137
- f"Background: {background}."
 
 
 
 
 
138
  )
139
- content = call_groq_api([
140
- {"role": "system", "content": system},
141
- {"role": "user", "content": user}
142
- ], timeout=90)
143
- # Split by marker
144
- if "### Resume" in content:
145
- sop, resume = content.split("### Resume", 1)
146
- return sop.replace("### SOP", "").strip(), resume.strip()
147
- else:
148
- return content, "No resume generated."
149
-
150
- def checklist_generator(degree_type, country, scholarship_name):
151
- if not all([degree_type, country, scholarship_name.strip()]):
152
  return []
153
- system = "You are ScholarGPT, an assistant that creates application document checklists."
154
- user = (
155
- f"Generate a checklist of required documents for a {degree_type} scholarship application "
156
- f"to {scholarship_name} in {country}."
157
  )
158
- content = call_groq_api([
159
- {"role": "system", "content": system},
160
- {"role": "user", "content": user}
161
- ])
162
- # Parse bullets
163
- items = [line.strip("- ").strip() for line in content.splitlines() if line.strip()]
 
 
164
  return items
165
 
166
- def improve_answer(question, user_answer):
167
- if not question or not user_answer.strip():
168
- return "Please select a question and provide your answer."
169
- system = "You are MentorGPT, an interview coach."
170
- user = (
171
- f"Interview Question: {question}\n"
172
- f"User Answer: {user_answer}\n"
173
- "Suggest improvements to make the answer more confident and professional."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  )
175
- return call_groq_api([
176
- {"role": "system", "content": system},
177
- {"role": "user", "content": user}
178
- ], timeout=60)
179
-
180
- # === Build Gradio Interface ===
181
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
182
- gr.Markdown("# 🎓 ScholarGPT Assistant\n*Your AI mentor for fully funded scholarships*")
183
- with gr.Tab("Scholarship Explorer"):
184
- ex_country = gr.Dropdown(label="Country", choices=COUNTRIES, value=COUNTRIES[0])
185
- ex_univ = gr.Textbox(label="University Name", placeholder="e.g. MIT")
186
- ex_btn = gr.Button("Explore Scholarships")
187
- ex_out = gr.HTML()
188
- ex_btn.click(ai_generate_scholarships, [ex_country, ex_univ], ex_out)
189
-
190
- with gr.Tab("Scholarship Finder"):
191
- sf_country = gr.Dropdown(label="Country", choices=COUNTRIES, value=COUNTRIES[0])
192
- sf_degree = gr.Dropdown(label="Degree Level", choices=DEGREES, value=DEGREES[0])
193
- sf_gpa = gr.Number(label="Your GPA", value=3.0, precision=2)
194
- sf_field = gr.Textbox(label="Field of Interest", placeholder="e.g. Computer Science")
195
- sf_btn = gr.Button("Find Scholarships")
196
- sf_out = gr.Markdown()
197
- sf_btn.click(scholarship_finder, [sf_country, sf_degree, sf_gpa, sf_field], sf_out)
198
-
199
- with gr.Tab("SOP Generator"):
200
- so_name = gr.Textbox(label="Full Name")
201
- so_univ = gr.Textbox(label="University")
202
- so_prog = gr.Textbox(label="Degree Program")
203
- so_back = gr.Textbox(label="Personal Background", lines=2)
204
- so_goals = gr.Textbox(label="Academic Goals", lines=2)
205
- so_amb = gr.Textbox(label="Future Ambitions", lines=2)
206
- so_btn = gr.Button("Generate SOP")
207
- so_out = gr.Textbox(lines=12, interactive=True, show_copy_button=True)
208
- so_btn.click(sop_generator,
209
- [so_name, so_univ, so_prog, so_back, so_goals, so_amb],
210
- so_out)
211
-
212
- with gr.Tab("Eligibility Checker"):
213
- ec_gpa = gr.Number(label="GPA", value=3.0, precision=2)
214
- ec_country = gr.Dropdown(label="Country", choices=COUNTRIES, value=COUNTRIES[0])
215
- ec_degree = gr.Dropdown(label="Degree Completed", choices=DEGREES, value=DEGREES[0])
216
- ec_btn = gr.Button("Check Eligibility")
217
- ec_out = gr.Markdown()
218
- ec_btn.click(eligibility_checker, [ec_gpa, ec_country, ec_degree], ec_out)
219
-
220
- with gr.Tab("Mentor Chat"):
221
- chat_bot = gr.Chatbot()
222
- user_msg = gr.Textbox(label="Your Question", placeholder="Ask anything…")
223
- chat_state = gr.State([])
224
- send_btn = gr.Button("Send")
225
- send_btn.click(mentor_chat, [user_msg, chat_state], [chat_bot, chat_state])
226
-
227
- with gr.Tab("Email Templates"):
228
- et_recipient = gr.Textbox(label="Recipient Name", placeholder="e.g. Dr. Smith")
229
- et_university = gr.Textbox(label="University Name", placeholder="e.g. MIT")
230
- et_purpose = gr.Dropdown(label="Purpose", choices=EMAIL_PURPOSES, value=EMAIL_PURPOSES[0])
231
- et_btn = gr.Button("Generate Email")
232
- et_out = gr.Textbox(lines=12, interactive=True, show_copy_button=True)
233
- et_btn.click(email_template, [et_recipient, et_university, et_purpose], et_out)
234
-
235
- with gr.Tab("SOP & Resume Generator"):
236
- sr_univ = gr.Textbox(label="University Name", placeholder="e.g. MIT")
237
- sr_field = gr.Textbox(label="Field of Study", placeholder="e.g. AI")
238
- sr_back = gr.Textbox(label="Personal Background", placeholder="Briefly describe yourself")
239
- sr_btn = gr.Button("Generate SOP & Resume")
240
- sop_out = gr.Textbox(lines=12, interactive=True, show_copy_button=True, label="Generated SOP")
241
- resume_out = gr.Textbox(lines=12, interactive=True, show_copy_button=True, label="Generated Resume")
242
- sr_btn.click(sop_resume_generator, [sr_univ, sr_field, sr_back], [sop_out, resume_out])
243
-
244
- with gr.Tab("Document Checklist"):
245
- dc_degree = gr.Dropdown(label="Degree Type", choices=DEGREES, value=DEGREES[1])
246
- dc_country = gr.Dropdown(label="Country Applying To", choices=COUNTRIES, value=COUNTRIES[0])
247
- dc_scholarship = gr.Textbox(label="Scholarship Name", placeholder="e.g. Fulbright")
248
- dc_btn = gr.Button("Generate Checklist")
249
- dc_out = gr.CheckboxGroup(label="Checklist Items", choices=[])
250
- dc_btn.click(checklist_generator, [dc_degree, dc_country, dc_scholarship], dc_out)
251
-
252
- with gr.Tab("Interview Guide"):
253
- iq_question = gr.Dropdown(label="Select Question", choices=COMMON_QUESTIONS)
254
- iq_answer = gr.Textbox(label="Your Answer", lines=4, placeholder="Type your answer here…")
255
- iq_btn = gr.Button("Improve Answer")
256
- iq_out = gr.Markdown(label="Suggestions")
257
- iq_tips = gr.Markdown(
258
- "**Interview Tips:**\n"
259
- "- Practice aloud using the STAR method.\n"
260
- "- Keep answers concise and structured.\n"
261
- "- Maintain confidence and good posture.\n"
262
- "- Research the scholarship thoroughly."
263
- )
264
- iq_btn.click(improve_answer, [iq_question, iq_answer], iq_out)
265
 
266
- gr.Markdown("\n---\n_Powered by Gradio & Groq API_")
267
 
268
  if __name__ == "__main__":
269
- demo.launch()
 
1
  import gradio as gr
 
2
  import os
3
+ import json
4
+ import datetime
5
+ import hashlib
6
+ import requests
7
+ from pathlib import Path
8
+
9
+ # --- Configuration & Constants ---
10
+
11
+ # Load API key from Gradio Secrets (Ensure this is set in your deployment environment)
12
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
13
 
14
+ # User data storage configuration
15
+ SECURE_DATA_DIR = Path(".secure_data")
16
+ USERS_FILE = SECURE_DATA_DIR / "users_encrypted.json"
17
+ USER_DATA_DIR = Path("user_data")
18
 
19
+ # Constants for UI
20
  COUNTRIES = [
21
+ "United States", "United Kingdom", "Canada", "Australia", "Germany",
22
+ "Pakistan", "India", "Nigeria", "Bangladesh", "Egypt"
23
  ]
24
  DEGREES = ["Bachelor", "Master", "PhD"]
25
+ EMAIL_PURPOSES = ["Scholarship Inquiry", "Follow-up on Application", "Requesting Recommendation Letter"]
26
  COMMON_QUESTIONS = [
27
  "Tell me about yourself.",
28
  "Why are you interested in this scholarship?",
29
+ "What are your academic strengths and weaknesses?",
30
+ "Describe a significant challenge you have overcome.",
31
+ "What are your long-term career goals?",
32
+ "How will this scholarship help you achieve your goals?"
33
  ]
34
 
35
+ # --- Initial Setup ---
36
+ # Create necessary directories
37
+ SECURE_DATA_DIR.mkdir(exist_ok=True)
38
+ USER_DATA_DIR.mkdir(exist_ok=True)
39
+
40
+
41
+ # === Core Helper Functions ===
42
+
43
  def call_groq_api(messages, model="llama3-8b-8192", timeout=60):
44
+ """Helper function to make API calls to Groq."""
45
  if not GROQ_API_KEY:
46
+ return "Error: GROQ_API_KEY is not configured. Please set it in your environment's secrets."
47
  try:
48
+ response = requests.post(
49
  "https://api.groq.com/openai/v1/chat/completions",
50
  headers={
51
  "Authorization": f"Bearer {GROQ_API_KEY}",
 
54
  json={"model": model, "messages": messages},
55
  timeout=timeout
56
  )
57
+ response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
58
+ data = response.json()
59
+ if "choices" in data and data["choices"]:
60
+ return data["choices"][0]["message"]["content"].strip()
61
+ return f"API returned an unexpected response structure: {data}"
62
+ except requests.exceptions.RequestException as e:
63
+ return f"API request failed: {e}"
64
  except Exception as e:
65
+ return f"An unexpected error occurred: {e}"
66
 
 
67
 
68
+ # === User Management Functions (from Streamlit app) ===
69
+
70
+ def hash_password(password):
71
+ """Hashes a password with a salt for secure storage."""
72
+ salt = "scholar_gpt_gradio_salt_2025" # A unique salt for this application
73
+ return hashlib.sha256((password + salt).encode()).hexdigest()
74
+
75
+ def load_users():
76
+ """Loads the user database from a JSON file."""
77
+ if USERS_FILE.exists():
78
+ try:
79
+ with open(USERS_FILE, "r") as f:
80
+ return json.load(f)
81
+ except (json.JSONDecodeError, FileNotFoundError):
82
+ return {}
83
+ return {}
84
+
85
+ def save_users(users):
86
+ """Saves the user database to a JSON file."""
87
+ with open(USERS_FILE, "w") as f:
88
+ json.dump(users, f, indent=4)
89
+
90
+ def get_user_data_path(username, filename):
91
+ """Gets the path to a specific user's data file."""
92
+ user_dir = USER_DATA_DIR / username
93
+ user_dir.mkdir(exist_ok=True)
94
+ return user_dir / filename
95
+
96
+ def signup_user(username, password, email):
97
+ """Registers a new user."""
98
+ if not all([username, password, email]):
99
+ return "Please fill out all fields.", False
100
+ if "@" not in email or "." not in email:
101
+ return "Please enter a valid email address.", False
102
+
103
+ users = load_users()
104
+ if username in users:
105
+ return "Username already exists. Please choose another one.", False
106
 
107
+ users[username] = {
108
+ "password": hash_password(password),
109
+ "email": email,
110
+ "created_at": datetime.datetime.now().isoformat()
111
+ }
112
+ save_users(users)
113
+ # Create the user's directory upon signup
114
+ (USER_DATA_DIR / username).mkdir(exist_ok=True)
115
+ return "Account created successfully! You can now log in.", True
116
+
117
+ def login_user(username, password):
118
+ """Logs in an existing user."""
119
+ if not all([username, password]):
120
+ return "Please enter both username and password.", False
121
+
122
+ users = load_users()
123
+ if username not in users or users[username]["password"] != hash_password(password):
124
+ return "Invalid username or password.", False
125
+
126
+ return f"Welcome back, {username}!", True
127
+
128
+ def save_user_journal(username, user_input, response):
129
+ """Saves an interaction to the user's journal."""
130
+ if not username:
131
+ return
132
+
133
+ journal_path = get_user_data_path(username, "journal.json")
134
+ entry = {
135
+ "timestamp": datetime.datetime.now().isoformat(),
136
+ "your_query": user_input,
137
+ "ai_response": response
138
+ }
139
+ journal = []
140
+ if journal_path.exists():
141
+ with open(journal_path, "r") as f:
142
+ try:
143
+ journal = json.load(f)
144
+ except json.JSONDecodeError:
145
+ journal = [] # Reset if file is corrupted
146
+ journal.append(entry)
147
+ with open(journal_path, "w") as f:
148
+ json.dump(journal, f, indent=4)
149
+
150
+ def load_user_journal(username):
151
+ """Loads a user's journal and formats it for display."""
152
+ if not username:
153
+ return "Please log in to see your journal."
154
+
155
+ journal_path = get_user_data_path(username, "journal.json")
156
+ if not journal_path.exists():
157
+ return "No journal entries yet. Start interacting with the tools!"
158
+
159
+ with open(journal_path, "r") as f:
160
+ try:
161
+ journal = json.load(f)
162
+ except json.JSONDecodeError:
163
+ return "Could not load journal."
164
+
165
+ if not journal:
166
+ return "No journal entries yet."
167
+
168
+ # Format for display in Markdown
169
+ formatted_journal = "## Your Interaction History\n\n"
170
+ for entry in reversed(journal): # Show most recent first
171
+ ts = datetime.datetime.fromisoformat(entry['timestamp']).strftime('%Y-%m-%d %H:%M:%S')
172
+ formatted_journal += f"**On {ts}, you asked:**\n"
173
+ formatted_journal += f"> _{entry['your_query']}_\n\n"
174
+ formatted_journal += f"**ScholarGPT Responded:**\n"
175
+ formatted_journal += f"_{entry['ai_response']}_\n\n---\n\n"
176
+ return formatted_journal
177
+
178
+
179
+ # === AI Feature Functions ===
180
+
181
+ def scholarship_finder(country, degree, gpa, field, username):
182
  if not all([country, degree, field]) or gpa is None:
183
+ return "Please fill in all fields to find scholarships."
184
+ prompt = (
185
+ f"I am a student from {country} looking for a {degree} program in the field of {field}. "
186
+ f"My GPA is {gpa}. Please find 5 fully-funded scholarship opportunities that match my profile. "
187
+ "For each, provide the scholarship name, the university, the country, and a brief description. "
188
+ "Format the output clearly in Markdown."
 
 
 
 
189
  )
190
+ messages = [
191
+ {"role": "system", "content": "You are ScholarGPT, an expert AI assistant that finds scholarships for students based on their profile."},
192
+ {"role": "user", "content": prompt}
193
+ ]
194
+ response = call_groq_api(messages)
195
+ save_user_journal(username, f"Find scholarships for {degree} in {field} from {country} with GPA {gpa}", response)
196
+ return response
197
+
198
+ def sop_generator(full_name, university, degree_program, background, goals, username):
199
+ if not all([full_name, university, degree_program, background, goals]):
200
+ return "Please provide all details to generate a Statement of Purpose."
201
+ prompt = (
202
+ f"Please write a compelling and professional Statement of Purpose (SOP). Here are the details:\n"
203
+ f"- **Applicant Name:** {full_name}\n"
204
+ f"- **Target University:** {university}\n"
205
+ f"- **Degree Program:** {degree_program}\n"
206
+ f"- **Personal & Academic Background:** {background}\n"
207
+ f"- **Future Goals & Ambitions:** {goals}\n\n"
208
+ "The SOP should be well-structured, formal, and highlight the applicant's suitability for the program."
209
  )
210
+ messages = [
211
+ {"role": "system", "content": "You are ScholarGPT, an expert academic writer specializing in creating powerful Statements of Purpose for university applications."},
212
+ {"role": "user", "content": prompt}
213
+ ]
214
+ response = call_groq_api(messages, timeout=90)
215
+ save_user_journal(username, f"Generate SOP for {degree_program} at {university}", response)
216
+ return response
217
+
218
+ def email_template_generator(recipient, university, purpose, username):
219
+ if not all([recipient, university, purpose]):
220
+ return "Please fill in all fields to generate an email."
221
+ prompt = (
222
+ f"Please draft a formal and respectful email for the following purpose:\n"
223
+ f"- **Recipient:** {recipient}\n"
224
+ f"- **University:** {university}\n"
225
+ f"- **Purpose of Email:** {purpose}\n\n"
226
+ "The email should include a clear subject line, a proper greeting, a concise body, and a professional closing."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  )
228
+ messages = [
229
+ {"role": "system", "content": "You are ScholarGPT, an AI assistant that drafts professional academic emails."},
230
+ {"role": "user", "content": prompt}
231
+ ]
232
+ response = call_groq_api(messages)
233
+ save_user_journal(username, f"Draft email to {recipient} for {purpose}", response)
234
+ return response
235
+
236
+ def interview_coach(question, user_answer, username):
237
+ if not question or not user_answer:
238
+ return "Please select a question and provide your answer for feedback."
239
+ prompt = (
240
+ f"I am preparing for a scholarship interview. Please provide constructive feedback on my answer.\n\n"
241
+ f"**Interview Question:** \"{question}\"\n\n"
242
+ f"**My Answer:** \"{user_answer}\"\n\n"
243
+ "Please analyze my answer and suggest improvements to make it more impactful, confident, and professional. "
244
+ "Structure your feedback into 'Strengths' and 'Areas for Improvement'."
245
  )
246
+ messages = [
247
+ {"role": "system", "content": "You are MentorGPT, an expert interview coach who helps students excel in scholarship interviews."},
248
+ {"role": "user", "content": prompt}
249
+ ]
250
+ response = call_groq_api(messages, timeout=90)
251
+ save_user_journal(username, f"Get feedback for interview question: {question}", response)
252
+ return response
253
+
254
+ def checklist_generator(degree_type, country, scholarship_name, username):
255
+ if not all([degree_type, country, scholarship_name]):
 
 
 
256
  return []
257
+ prompt = (
258
+ f"Please generate a comprehensive checklist of required documents for a {degree_type} scholarship application "
259
+ f"for the '{scholarship_name}' in {country}. List only the document names."
 
260
  )
261
+ messages = [
262
+ {"role": "system", "content": "You are ScholarGPT, an assistant that creates application document checklists. Provide only a list of items."},
263
+ {"role": "user", "content": prompt}
264
+ ]
265
+ content = call_groq_api(messages)
266
+ # Parse the response into a list of checklist items
267
+ items = [line.strip("-* ").strip() for line in content.splitlines() if line.strip() and len(line) > 2]
268
+ save_user_journal(username, f"Generate checklist for {scholarship_name}", str(items))
269
  return items
270
 
271
+ # --- Gradio UI ---
272
+
273
+ # Define custom CSS for styling the application
274
+ custom_css = """
275
+ body { background-color: #F0F2F6; }
276
+ .gradio-container {
277
+ font-family: 'Inter', sans-serif;
278
+ border-radius: 16px !important;
279
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.17);
280
+ }
281
+ #app_title {
282
+ text-align: center;
283
+ font-size: 3em;
284
+ color: #1E3A8A; /* Dark Blue */
285
+ font-weight: 700;
286
+ }
287
+ #app_subtitle {
288
+ text-align: center;
289
+ font-size: 1.2em;
290
+ color: #4B5563; /* Gray */
291
+ margin-top: -15px;
292
+ }
293
+ .gr-button {
294
+ background-image: linear-gradient(to right, #1E3A8A 0%, #3B82F6 100%);
295
+ color: white;
296
+ font-weight: bold;
297
+ border-radius: 8px;
298
+ }
299
+ .gr-button:hover {
300
+ box-shadow: 0 4px 15px 0 rgba(59, 130, 246, 0.4);
301
+ }
302
+ #auth_message {
303
+ text-align: center;
304
+ font-weight: bold;
305
+ padding: 10px;
306
+ border-radius: 8px;
307
+ }
308
+ .success { color: #15803D; background-color: #F0FDF4; }
309
+ .error { color: #B91C1C; background-color: #FEF2F2; }
310
+ #logout_button { background: #DC2626; }
311
+ """
312
+
313
+ with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo:
314
+ # --- State Management ---
315
+ logged_in_user = gr.State(None) # Stores the username of the logged-in user
316
+
317
+ # --- UI Definition ---
318
+ gr.Markdown("# 🎓 ScholarGPT", elem_id="app_title")
319
+ gr.Markdown("_Your AI-Powered Mentor for Securing Global Scholarships_", elem_id="app_subtitle")
320
+
321
+ # --- Main Application Interface (Initially Hidden) ---
322
+ with gr.Column(visible=False) as app_container:
323
+ with gr.Row():
324
+ welcome_text = gr.Markdown()
325
+ with gr.Column(scale=1, min_width=150):
326
+ logout_button = gr.Button("Logout", elem_id="logout_button")
327
+
328
+ with gr.Tabs() as app_tabs:
329
+ # --- Scholarship Finder Tab ---
330
+ with gr.TabItem("🔎 Scholarship Finder"):
331
+ with gr.Row():
332
+ sf_country = gr.Dropdown(label="Your Country of Origin", choices=COUNTRIES, value=COUNTRIES[5])
333
+ sf_degree = gr.Dropdown(label="Desired Degree Level", choices=DEGREES, value=DEGREES[1])
334
+ sf_gpa = gr.Number(label="Your Current GPA (out of 4.0)", value=3.5, precision=2)
335
+ sf_field = gr.Textbox(label="Your Field of Interest", placeholder="e.g., Artificial Intelligence, Public Health")
336
+ sf_btn = gr.Button("Find My Scholarships")
337
+ sf_out = gr.Markdown(label="Matching Scholarships")
338
+
339
+ # --- SOP Generator Tab ---
340
+ with gr.TabItem("✍️ SOP Generator"):
341
+ so_name = gr.Textbox(label="Your Full Name")
342
+ with gr.Row():
343
+ so_univ = gr.Textbox(label="Target University")
344
+ so_prog = gr.Textbox(label="Target Degree Program")
345
+ so_back = gr.Textbox(label="Your Personal & Academic Background", lines=4, placeholder="Describe your journey, key achievements, and relevant experiences.")
346
+ so_goals = gr.Textbox(label="Your Future Goals & Ambitions", lines=3, placeholder="What do you want to achieve after this degree?")
347
+ so_btn = gr.Button("Generate My SOP")
348
+ so_out = gr.Textbox(label="Generated Statement of Purpose", lines=15, interactive=True, show_copy_button=True)
349
+
350
+ # --- Email Generator Tab ---
351
+ with gr.TabItem("✉️ Email Generator"):
352
+ et_recipient = gr.Textbox(label="Recipient's Name & Title", placeholder="e.g., Dr. Jane Doe, Admissions Committee")
353
+ et_university = gr.Textbox(label="University Name")
354
+ et_purpose = gr.Dropdown(label="Purpose of Email", choices=EMAIL_PURPOSES, value=EMAIL_PURPOSES[0])
355
+ et_btn = gr.Button("Generate Formal Email")
356
+ et_out = gr.Textbox(label="Generated Email Draft", lines=15, interactive=True, show_copy_button=True)
357
+
358
+ # --- Interview Coach Tab ---
359
+ with gr.TabItem("💬 Interview Coach"):
360
+ iq_question = gr.Dropdown(label="Select a Common Interview Question", choices=COMMON_QUESTIONS, value=COMMON_QUESTIONS[0])
361
+ iq_answer = gr.Textbox(label="Your Answer", lines=5, placeholder="Type your response here...")
362
+ iq_btn = gr.Button("Get Feedback on My Answer")
363
+ iq_out = gr.Markdown(label="AI Coach's Feedback")
364
+
365
+ # --- Document Checklist Tab ---
366
+ with gr.TabItem("✅ Application Checklist"):
367
+ dc_degree = gr.Dropdown(label="Degree Type", choices=DEGREES, value=DEGREES[1])
368
+ dc_country = gr.Dropdown(label="Country Applying To", choices=COUNTRIES, value=COUNTRIES[0])
369
+ dc_scholarship = gr.Textbox(label="Specific Scholarship Name", placeholder="e.g., Fulbright Scholarship")
370
+ dc_btn = gr.Button("Generate Document Checklist")
371
+ dc_out = gr.CheckboxGroup(label="Required Documents", choices=[])
372
+
373
+ # --- My Journal Tab ---
374
+ with gr.TabItem("📓 My Journal"):
375
+ journal_out = gr.Markdown(label="Your Saved Interactions")
376
+ # The journal is loaded when the tab is selected
377
+
378
+
379
+ # --- Authentication Interface (Initially Visible) ---
380
+ with gr.Column(visible=True) as auth_container:
381
+ with gr.Tabs() as auth_tabs:
382
+ # --- Login Tab ---
383
+ with gr.TabItem("Login"):
384
+ login_user_input = gr.Textbox(label="Username")
385
+ login_pass_input = gr.Textbox(label="Password", type="password")
386
+ login_btn = gr.Button("Login")
387
+ login_message = gr.Markdown(elem_classes=["auth_message"])
388
+
389
+ # --- Signup Tab ---
390
+ with gr.TabItem("Sign Up"):
391
+ signup_user_input = gr.Textbox(label="Choose a Username")
392
+ signup_email_input = gr.Textbox(label="Email Address")
393
+ signup_pass_input = gr.Textbox(label="Create a Password", type="password")
394
+ signup_btn = gr.Button("Create Account")
395
+ signup_message = gr.Markdown(elem_classes=["auth_message"])
396
+
397
+ # --- Event Handling & Logic ---
398
+
399
+ # --- Authentication Logic ---
400
+ def handle_login(username, password):
401
+ message, success = login_user(username, password)
402
+ if success:
403
+ # Hide auth UI, show app UI
404
+ return {
405
+ auth_container: gr.update(visible=False),
406
+ app_container: gr.update(visible=True),
407
+ login_message: gr.update(value=f"<p class='success'>{message}</p>"),
408
+ welcome_text: gr.update(value=f"### Welcome, {username}!"),
409
+ logged_in_user: username
410
+ }
411
+ else:
412
+ return {login_message: gr.update(value=f"<p class='error'>{message}</p>")}
413
+
414
+ def handle_signup(username, password, email):
415
+ message, success = signup_user(username, password, email)
416
+ if success:
417
+ return {signup_message: gr.update(value=f"<p class='success'>{message}</p>")}
418
+ else:
419
+ return {signup_message: gr.update(value=f"<p class='error'>{message}</p>")}
420
+
421
+ def handle_logout():
422
+ # Show auth UI, hide app UI, reset state
423
+ return {
424
+ auth_container: gr.update(visible=True),
425
+ app_container: gr.update(visible=False),
426
+ logged_in_user: None,
427
+ login_user_input: "", # Clear fields
428
+ login_pass_input: ""
429
+ }
430
+
431
+ # Bind auth functions to buttons
432
+ login_btn.click(handle_login, inputs=[login_user_input, login_pass_input], outputs=[auth_container, app_container, login_message, welcome_text, logged_in_user])
433
+ signup_btn.click(handle_signup, inputs=[signup_user_input, signup_pass_input, signup_email_input], outputs=[signup_message])
434
+ logout_button.click(handle_logout, outputs=[auth_container, app_container, logged_in_user, login_user_input, login_pass_input])
435
+
436
+
437
+ # --- App Feature Logic ---
438
+ # Bind AI functions to their respective buttons
439
+ sf_btn.click(scholarship_finder, inputs=[sf_country, sf_degree, sf_gpa, sf_field, logged_in_user], outputs=sf_out)
440
+ so_btn.click(sop_generator, inputs=[so_name, so_univ, so_prog, so_back, so_goals, logged_in_user], outputs=so_out)
441
+ et_btn.click(email_template_generator, inputs=[et_recipient, et_university, et_purpose, logged_in_user], outputs=et_out)
442
+ iq_btn.click(interview_coach, inputs=[iq_question, iq_answer, logged_in_user], outputs=iq_out)
443
+ dc_btn.click(checklist_generator, inputs=[dc_degree, dc_country, dc_scholarship, logged_in_user], outputs=dc_out)
444
+
445
+ # Load journal when the 'My Journal' tab is selected
446
+ app_tabs.select(
447
+ lambda tab_index, username: load_user_journal(username) if tab_index == 5 else gr.update(),
448
+ inputs=[app_tabs, logged_in_user],
449
+ outputs=journal_out
450
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
 
452
 
453
  if __name__ == "__main__":
454
+ demo.launch(debug=True)