prasanthr0416 commited on
Commit
b7fcd00
Β·
verified Β·
1 Parent(s): 9d6e800

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +162 -30
app.py CHANGED
@@ -429,6 +429,8 @@ def extract_and_fix_json(text, subject="General"):
429
  dict: Parsed JSON or generic plan
430
  """
431
  if not text or not isinstance(text, str):
 
 
432
  return None
433
 
434
  # Store raw response for debug
@@ -443,31 +445,50 @@ def extract_and_fix_json(text, subject="General"):
443
  text = re.sub(r'```javascript\s*', '', text, flags=re.IGNORECASE)
444
  text = re.sub(r'```python\s*', '', text, flags=re.IGNORECASE)
445
 
 
 
 
 
 
446
  # Find JSON boundaries
447
  start_idx = text.find('{')
448
  end_idx = text.rfind('}')
449
 
450
  if start_idx == -1 or end_idx == -1 or end_idx <= start_idx:
 
 
451
  return None
452
 
453
  json_str = text[start_idx:end_idx+1]
454
 
455
- # DEBUG: Show what we're trying to parse
456
  if st.session_state.show_debug:
457
  with st.expander("πŸ” JSON String Being Parsed", expanded=False):
458
- st.code(json_str[:2000] + "..." if len(json_str) > 2000 else json_str)
459
 
460
  try:
461
  # First try: Direct parse
462
- return json.loads(json_str)
 
 
 
463
  except json.JSONDecodeError as e:
 
 
 
 
464
  # Second try: Apply fixes
465
  json_str = fix_json_string(json_str)
466
  try:
467
- return json.loads(json_str)
468
- except json.JSONDecodeError:
 
 
 
 
 
469
  # Third try: Create generic plan
470
- return create_generic_plan(subject, 60)
471
 
472
  def fix_json_string(json_str):
473
  """
@@ -643,7 +664,10 @@ def generate_study_plan(api_key, **kwargs):
643
  """
644
  Generate study plan using Gemini
645
  """
646
- genai.configure(api_key=api_key)
 
 
 
647
 
648
  # Extract parameters
649
  subject = kwargs.get('subject', 'General Learning')
@@ -690,11 +714,23 @@ Return valid JSON with this exact structure:
690
  "success_metrics": ["Metric 1", "Metric 2"]
691
  }}
692
 
693
- IMPORTANT: Make sure topics_allocation values are NUMBERS (not strings like "10 hours").
694
- Include ALL {weeks} weeks in the weekly_schedule array."""
 
 
 
695
 
696
  try:
 
 
697
  model = genai.GenerativeModel('gemini-2.5-flash')
 
 
 
 
 
 
 
698
  response = model.generate_content(
699
  prompt,
700
  generation_config=genai.GenerationConfig(
@@ -702,15 +738,34 @@ Include ALL {weeks} weeks in the weekly_schedule array."""
702
  temperature=0.7
703
  )
704
  )
 
705
  raw_text = response.text
706
  st.session_state.raw_response = raw_text
707
 
 
 
 
 
 
 
708
  plan = extract_and_fix_json(raw_text, subject)
709
 
710
  if plan:
 
 
 
 
 
 
 
 
 
 
 
711
  # Post-process the plan
712
  plan['generated_at'] = datetime.now().isoformat()
713
  plan['total_days'] = days_available
 
714
 
715
  # Initialize week tracking
716
  for week in plan.get('weekly_schedule', []):
@@ -728,9 +783,16 @@ Include ALL {weeks} weeks in the weekly_schedule array."""
728
 
729
  return plan
730
  else:
 
 
 
731
  return create_generic_plan(subject, days_available)
732
 
733
  except Exception as e:
 
 
 
 
734
  return create_generic_plan(subject, days_available)
735
 
736
  def generate_weekly_test(api_key, week_number, weekly_tasks, subject):
@@ -1124,16 +1186,46 @@ tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs([
1124
  "πŸ“€ Export"
1125
  ])
1126
 
 
1127
  # ============================================
1128
- # TAB 1: DEFINE GOAL - SIMPLIFIED
1129
  # ============================================
1130
 
1131
  with tab1:
1132
  st.markdown('<h2 class="sub-header">Define Your Learning Goal</h2>', unsafe_allow_html=True)
1133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1134
  # Option 1: Upload Existing Plan
1135
  st.info("πŸ“€ **Option 1: Upload Existing Plan**")
1136
- uploaded_file = st.file_uploader("Upload saved study plan (JSON)", type=['json'])
1137
 
1138
  if uploaded_file is not None and not st.session_state.plan_loaded:
1139
  try:
@@ -1142,8 +1234,8 @@ with tab1:
1142
  st.session_state.subject = plan['subject']
1143
  st.session_state.plan_loaded = True
1144
  st.success(f"βœ… Plan loaded: {plan['subject']}")
1145
- except:
1146
- st.error("❌ Invalid plan file")
1147
 
1148
  st.markdown("---")
1149
 
@@ -1156,7 +1248,8 @@ with tab1:
1156
  subject = st.text_input(
1157
  "What do you want to learn?",
1158
  placeholder="e.g., Data Science, Python, Web Development...",
1159
- help="Enter the subject or topic"
 
1160
  )
1161
 
1162
  col1a, col2a = st.columns(2)
@@ -1165,7 +1258,8 @@ with tab1:
1165
  "Hours per day:",
1166
  min_value=1,
1167
  max_value=8,
1168
- value=2
 
1169
  )
1170
 
1171
  with col2a:
@@ -1173,30 +1267,35 @@ with tab1:
1173
  "Days available:",
1174
  min_value=7,
1175
  max_value=365,
1176
- value=60
 
1177
  )
1178
 
1179
  study_days = st.multiselect(
1180
  "Study days:",
1181
  ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1182
- default=["Mon", "Tue", "Wed", "Thu", "Fri"]
 
1183
  )
1184
 
1185
  with col2:
1186
  current_level = st.selectbox(
1187
  "Your level:",
1188
  ["Beginner", "Intermediate", "Advanced"],
1189
- index=0
 
1190
  )
1191
 
 
 
1192
  # Generate Plan Button
1193
- if st.button("πŸš€ Generate AI Study Plan", type="primary", use_container_width=True):
1194
  if not st.session_state.api_key:
1195
- st.error("⚠️ API Key Required")
1196
  elif not subject:
1197
- st.error("⚠️ Please enter a subject")
1198
  elif not study_days:
1199
- st.error("⚠️ Please select study days")
1200
  else:
1201
  with st.spinner("πŸ€– AI is creating your personalized study plan..."):
1202
  try:
@@ -1216,34 +1315,67 @@ with tab1:
1216
  'study_days': study_days
1217
  }
1218
 
 
 
 
 
 
1219
  # Generate plan
1220
  plan = generate_study_plan(st.session_state.api_key, **plan_data)
1221
 
1222
  if plan:
1223
  st.session_state.study_plan = plan
1224
  st.session_state.subject = subject
 
 
 
1225
  st.session_state.plan_loaded = True
1226
 
1227
- # Show debug info
1228
- if st.session_state.show_debug:
1229
- with st.expander("πŸ” Raw AI Response", expanded=True):
1230
- st.text_area("Full Response", st.session_state.raw_response, height=300)
1231
-
1232
  st.success("βœ… Study plan generated successfully!")
1233
  st.balloons()
1234
 
1235
  # Show plan summary
1236
  st.info(f"πŸ“… **Schedule:** {len(study_days)} days/week ({', '.join(study_days)}) Γ— {hours_per_day} hours/day")
1237
- st.info(f"πŸ“Š **Duration:** {plan.get('total_weeks', 0)} weeks")
1238
 
1239
  # Show learning quality
1240
- st.info(f"🎯 **Learning Quality:** {learning_quality['quality_score']:.0f}/100")
 
 
 
 
 
1241
 
1242
  else:
1243
- st.error("❌ Failed to generate plan")
1244
 
1245
  except Exception as e:
1246
  st.error(f"❌ Error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1247
 
1248
  # ============================================
1249
  # TAB 2: STUDY PLAN
 
429
  dict: Parsed JSON or generic plan
430
  """
431
  if not text or not isinstance(text, str):
432
+ if st.session_state.show_debug:
433
+ st.warning("⚠️ No text to parse in extract_and_fix_json")
434
  return None
435
 
436
  # Store raw response for debug
 
445
  text = re.sub(r'```javascript\s*', '', text, flags=re.IGNORECASE)
446
  text = re.sub(r'```python\s*', '', text, flags=re.IGNORECASE)
447
 
448
+ # Show cleaned text in debug
449
+ if st.session_state.show_debug:
450
+ with st.expander("🧹 Cleaned Text (first 500 chars)", expanded=False):
451
+ st.code(text[:500] + "..." if len(text) > 500 else text)
452
+
453
  # Find JSON boundaries
454
  start_idx = text.find('{')
455
  end_idx = text.rfind('}')
456
 
457
  if start_idx == -1 or end_idx == -1 or end_idx <= start_idx:
458
+ if st.session_state.show_debug:
459
+ st.error(f"❌ No JSON found. Start: {start_idx}, End: {end_idx}")
460
  return None
461
 
462
  json_str = text[start_idx:end_idx+1]
463
 
464
+ # Show JSON string in debug
465
  if st.session_state.show_debug:
466
  with st.expander("πŸ” JSON String Being Parsed", expanded=False):
467
+ st.code(json_str[:1000] + "..." if len(json_str) > 1000 else json_str)
468
 
469
  try:
470
  # First try: Direct parse
471
+ parsed_json = json.loads(json_str)
472
+ if st.session_state.show_debug:
473
+ st.success("βœ… JSON parsed successfully on first try")
474
+ return parsed_json
475
  except json.JSONDecodeError as e:
476
+ if st.session_state.show_debug:
477
+ st.warning(f"⚠️ First parse failed: {str(e)}")
478
+ st.info("Trying to fix JSON...")
479
+
480
  # Second try: Apply fixes
481
  json_str = fix_json_string(json_str)
482
  try:
483
+ parsed_json = json.loads(json_str)
484
+ if st.session_state.show_debug:
485
+ st.success("βœ… JSON parsed successfully after fixes")
486
+ return parsed_json
487
+ except json.JSONDecodeError as e2:
488
+ if st.session_state.show_debug:
489
+ st.error(f"❌ Second parse failed: {str(e2)}")
490
  # Third try: Create generic plan
491
+ return None
492
 
493
  def fix_json_string(json_str):
494
  """
 
664
  """
665
  Generate study plan using Gemini
666
  """
667
+ if not api_key:
668
+ st.error("❌ No API key provided")
669
+ return create_generic_plan(kwargs.get('subject', 'General'),
670
+ kwargs.get('days_available', 60))
671
 
672
  # Extract parameters
673
  subject = kwargs.get('subject', 'General Learning')
 
714
  "success_metrics": ["Metric 1", "Metric 2"]
715
  }}
716
 
717
+ IMPORTANT:
718
+ 1. Make sure topics_allocation values are NUMBERS (not strings like "10 hours")
719
+ 2. Include ALL {weeks} weeks in the weekly_schedule array
720
+ 3. Return ONLY JSON, no additional text
721
+ 4. Make tasks specific and actionable for {subject}"""
722
 
723
  try:
724
+ # Configure API
725
+ genai.configure(api_key=api_key)
726
  model = genai.GenerativeModel('gemini-2.5-flash')
727
+
728
+ # Show prompt in debug mode
729
+ if st.session_state.show_debug:
730
+ with st.expander("πŸ“ Prompt Sent to AI", expanded=False):
731
+ st.code(prompt[:1000] + "..." if len(prompt) > 1000 else prompt)
732
+
733
+ # Generate response
734
  response = model.generate_content(
735
  prompt,
736
  generation_config=genai.GenerationConfig(
 
738
  temperature=0.7
739
  )
740
  )
741
+
742
  raw_text = response.text
743
  st.session_state.raw_response = raw_text
744
 
745
+ # Show raw response in debug mode
746
+ if st.session_state.show_debug:
747
+ with st.expander("πŸ” Raw AI Response", expanded=False):
748
+ st.text_area("AI Response", raw_text, height=200, key="ai_response_raw")
749
+
750
+ # Extract JSON
751
  plan = extract_and_fix_json(raw_text, subject)
752
 
753
  if plan:
754
+ # Show success in debug mode
755
+ if st.session_state.show_debug:
756
+ st.success("βœ… JSON successfully extracted")
757
+ with st.expander("πŸ“‹ Extracted Plan Preview", expanded=False):
758
+ st.json({
759
+ "subject": plan.get('subject'),
760
+ "total_weeks": plan.get('total_weeks'),
761
+ "weekly_schedule_length": len(plan.get('weekly_schedule', [])),
762
+ "first_week_focus": plan.get('weekly_schedule', [{}])[0].get('focus', 'N/A') if plan.get('weekly_schedule') else 'N/A'
763
+ })
764
+
765
  # Post-process the plan
766
  plan['generated_at'] = datetime.now().isoformat()
767
  plan['total_days'] = days_available
768
+ plan['user_level'] = current_level
769
 
770
  # Initialize week tracking
771
  for week in plan.get('weekly_schedule', []):
 
783
 
784
  return plan
785
  else:
786
+ st.warning("⚠️ Could not extract valid JSON from AI response")
787
+ if st.session_state.show_debug:
788
+ st.info("Falling back to generic plan")
789
  return create_generic_plan(subject, days_available)
790
 
791
  except Exception as e:
792
+ st.error(f"❌ AI Generation Error: {str(e)}")
793
+ if st.session_state.show_debug:
794
+ with st.expander("πŸ” Error Details", expanded=False):
795
+ st.exception(e)
796
  return create_generic_plan(subject, days_available)
797
 
798
  def generate_weekly_test(api_key, week_number, weekly_tasks, subject):
 
1186
  "πŸ“€ Export"
1187
  ])
1188
 
1189
+
1190
  # ============================================
1191
+ # TAB 1: DEFINE GOAL
1192
  # ============================================
1193
 
1194
  with tab1:
1195
  st.markdown('<h2 class="sub-header">Define Your Learning Goal</h2>', unsafe_allow_html=True)
1196
 
1197
+ # Debug Mode Toggle
1198
+ col_debug1, col_debug2 = st.columns([1, 3])
1199
+ with col_debug1:
1200
+ st.session_state.show_debug = st.checkbox("πŸ” Debug Mode", value=False)
1201
+
1202
+ # API Status
1203
+ with col_debug2:
1204
+ if not st.session_state.api_key:
1205
+ st.error("⚠️ **API Key Missing:** Set GEMINI_API_KEY in Hugging Face Secrets")
1206
+ else:
1207
+ st.success("βœ… **API Key Loaded**")
1208
+
1209
+ # Test API Button
1210
+ if st.session_state.api_key and st.session_state.show_debug:
1211
+ if st.button("Test API Connection", type="secondary", key="test_api"):
1212
+ try:
1213
+ genai.configure(api_key=st.session_state.api_key)
1214
+ model = genai.GenerativeModel('gemini-2.5-flash')
1215
+ test_response = model.generate_content("Say 'API is working'",
1216
+ generation_config=genai.GenerationConfig(max_output_tokens=10))
1217
+ if test_response.text:
1218
+ st.success(f"βœ… **API Connected:** {test_response.text}")
1219
+ else:
1220
+ st.error("❌ No response from API")
1221
+ except Exception as e:
1222
+ st.error(f"❌ **API Error:** {str(e)}")
1223
+
1224
+ st.markdown("---")
1225
+
1226
  # Option 1: Upload Existing Plan
1227
  st.info("πŸ“€ **Option 1: Upload Existing Plan**")
1228
+ uploaded_file = st.file_uploader("Upload saved study plan (JSON)", type=['json'], key="upload_plan")
1229
 
1230
  if uploaded_file is not None and not st.session_state.plan_loaded:
1231
  try:
 
1234
  st.session_state.subject = plan['subject']
1235
  st.session_state.plan_loaded = True
1236
  st.success(f"βœ… Plan loaded: {plan['subject']}")
1237
+ except Exception as e:
1238
+ st.error(f"❌ Invalid plan file: {str(e)}")
1239
 
1240
  st.markdown("---")
1241
 
 
1248
  subject = st.text_input(
1249
  "What do you want to learn?",
1250
  placeholder="e.g., Data Science, Python, Web Development...",
1251
+ help="Enter the subject or topic",
1252
+ key="subject_input"
1253
  )
1254
 
1255
  col1a, col2a = st.columns(2)
 
1258
  "Hours per day:",
1259
  min_value=1,
1260
  max_value=8,
1261
+ value=2,
1262
+ key="hours_slider"
1263
  )
1264
 
1265
  with col2a:
 
1267
  "Days available:",
1268
  min_value=7,
1269
  max_value=365,
1270
+ value=60,
1271
+ key="days_slider"
1272
  )
1273
 
1274
  study_days = st.multiselect(
1275
  "Study days:",
1276
  ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
1277
+ default=["Mon", "Tue", "Wed", "Thu", "Fri"],
1278
+ key="study_days_select"
1279
  )
1280
 
1281
  with col2:
1282
  current_level = st.selectbox(
1283
  "Your level:",
1284
  ["Beginner", "Intermediate", "Advanced"],
1285
+ index=0,
1286
+ key="level_select"
1287
  )
1288
 
1289
+ st.markdown("---")
1290
+
1291
  # Generate Plan Button
1292
+ if st.button("πŸš€ Generate AI Study Plan", type="primary", use_container_width=True, key="generate_plan_btn"):
1293
  if not st.session_state.api_key:
1294
+ st.error("⚠️ **API Key Required:** Please set GEMINI_API_KEY in Hugging Face Secrets.")
1295
  elif not subject:
1296
+ st.error("⚠️ Please enter a subject/topic")
1297
  elif not study_days:
1298
+ st.error("⚠️ Please select at least one study day")
1299
  else:
1300
  with st.spinner("πŸ€– AI is creating your personalized study plan..."):
1301
  try:
 
1315
  'study_days': study_days
1316
  }
1317
 
1318
+ # Show plan data in debug mode
1319
+ if st.session_state.show_debug:
1320
+ with st.expander("πŸ“‹ Plan Data Sent to AI", expanded=True):
1321
+ st.json(plan_data)
1322
+
1323
  # Generate plan
1324
  plan = generate_study_plan(st.session_state.api_key, **plan_data)
1325
 
1326
  if plan:
1327
  st.session_state.study_plan = plan
1328
  st.session_state.subject = subject
1329
+ st.session_state.current_week = 1
1330
+ st.session_state.weekly_streak = 0
1331
+ st.session_state.longest_weekly_streak = 0
1332
  st.session_state.plan_loaded = True
1333
 
 
 
 
 
 
1334
  st.success("βœ… Study plan generated successfully!")
1335
  st.balloons()
1336
 
1337
  # Show plan summary
1338
  st.info(f"πŸ“… **Schedule:** {len(study_days)} days/week ({', '.join(study_days)}) Γ— {hours_per_day} hours/day")
1339
+ st.info(f"πŸ“Š **Duration:** {plan.get('total_weeks', 0)} weeks ({days_available} days)")
1340
 
1341
  # Show learning quality
1342
+ if learning_quality['is_optimal']:
1343
+ st.success(f"🎯 **Learning Quality:** {learning_quality['quality_score']:.0f}/100 (Excellent)")
1344
+ else:
1345
+ st.warning(f"πŸ“Š **Learning Quality:** {learning_quality['quality_score']:.0f}/100 (Needs improvement)")
1346
+
1347
+ st.caption(f"Coverage: {learning_quality['coverage_percentage']:.0f}% | Total hours: {learning_quality['total_hours']}h")
1348
 
1349
  else:
1350
+ st.error("❌ Failed to generate plan. Check debug mode for details.")
1351
 
1352
  except Exception as e:
1353
  st.error(f"❌ Error: {str(e)}")
1354
+ if st.session_state.show_debug:
1355
+ with st.expander("πŸ” Error Details", expanded=True):
1356
+ st.exception(e)
1357
+
1358
+ # Debug Info Section
1359
+ if st.session_state.show_debug and st.session_state.raw_response:
1360
+ st.markdown("---")
1361
+ st.subheader("πŸ” Debug Information")
1362
+
1363
+ col_debug_a, col_debug_b = st.columns(2)
1364
+
1365
+ with col_debug_a:
1366
+ st.metric("Raw Response Length", f"{len(st.session_state.raw_response)} chars")
1367
+
1368
+ with col_debug_b:
1369
+ if st.session_state.study_plan:
1370
+ plan_type = "AI Generated" if st.session_state.study_plan.get('generated_at') else "Generic"
1371
+ st.metric("Plan Type", plan_type)
1372
+
1373
+ with st.expander("πŸ“‹ Raw AI Response", expanded=False):
1374
+ st.text_area("Full Response", st.session_state.raw_response, height=300, key="raw_response_display")
1375
+
1376
+ if st.session_state.study_plan:
1377
+ with st.expander("πŸ“Š Parsed Plan Structure", expanded=False):
1378
+ st.json(st.session_state.study_plan)
1379
 
1380
  # ============================================
1381
  # TAB 2: STUDY PLAN