JustusI commited on
Commit
a7af83a
·
verified ·
1 Parent(s): e75b4e2

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +166 -641
src/streamlit_app.py CHANGED
@@ -70,19 +70,21 @@ def generate_questions_from_text(text, num_questions):
70
  {"role": "user", "content": prompt}
71
  ]
72
  completion = client.chat.completions.create(
73
- model="gpt-4o-mini",
74
  messages=messages
75
  )
76
  return completion.choices[0].message.content.strip()
77
 
78
  def generate_flashcards_from_text(text, num_cards):
79
  prompt = (
80
- f"Generate {num_cards} flashcards based on the following document.\n\nDocument:\n\n{text}\n\n"
81
- "Return a Python dictionary where each key is a flashcard question and its corresponding value is the answer. "
82
- "Do not include any additional text."
 
 
83
  )
84
  messages = [
85
- {"role": "system", "content": "You are an educational assistant that creates study flashcards."},
86
  {"role": "user", "content": prompt}
87
  ]
88
  completion = client.chat.completions.create(
@@ -90,15 +92,37 @@ def generate_flashcards_from_text(text, num_cards):
90
  messages=messages
91
  )
92
  output = completion.choices[0].message.content.strip()
 
 
 
 
 
 
 
 
 
 
93
  try:
94
- flashcards = literal_eval(output)
95
- if isinstance(flashcards, dict):
 
96
  return flashcards
97
  else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  return {}
99
- except Exception as e:
100
- st.error(f"Error parsing flashcards: {e}")
101
- return {}
102
 
103
  # ---------------------------
104
  # Sidebar: File Upload & Mode Selection
@@ -332,46 +356,74 @@ else:
332
  elif mode == "Flashcards":
333
  st.header("🎴 Interactive Flashcards")
334
 
335
- # Custom CSS for flashcard styling
336
  st.markdown("""
337
  <style>
338
- .flashcard-container {
339
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  border-radius: 15px;
341
  padding: 40px;
342
- margin: 20px 0;
343
  box-shadow: 0 10px 30px rgba(0,0,0,0.3);
344
- min-height: 250px;
345
  display: flex;
346
  align-items: center;
347
  justify-content: center;
348
  text-align: center;
349
- transition: transform 0.3s ease;
350
  }
351
- .flashcard-container:hover {
352
- transform: translateY(-5px);
 
353
  }
354
- .flashcard-question {
 
355
  color: white;
 
 
 
356
  font-size: 24px;
357
  font-weight: bold;
358
- margin-bottom: 20px;
359
  }
360
  .flashcard-answer {
361
- background: rgba(255,255,255,0.95);
362
- border-radius: 10px;
363
- padding: 25px;
364
- margin-top: 20px;
365
- color: #333;
366
- font-size: 18px;
367
- box-shadow: 0 4px 15px rgba(0,0,0,0.2);
 
 
 
 
 
 
 
 
 
 
368
  }
369
  .score-card {
370
- background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
371
  border-radius: 10px;
372
  padding: 20px;
373
  color: white;
374
- font-size: 20px;
375
  font-weight: bold;
376
  text-align: center;
377
  margin: 20px 0;
@@ -379,20 +431,26 @@ else:
379
  .progress-bar-container {
380
  background: #e0e0e0;
381
  border-radius: 10px;
382
- height: 20px;
383
  margin: 20px 0;
384
  overflow: hidden;
 
385
  }
386
  .progress-bar {
387
  background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
388
  height: 100%;
389
  transition: width 0.3s ease;
390
- }
391
- .card-number {
 
392
  color: white;
393
- font-size: 16px;
394
- margin-bottom: 10px;
395
- opacity: 0.9;
 
 
 
 
396
  }
397
  .stButton > button {
398
  border-radius: 8px;
@@ -495,62 +553,106 @@ else:
495
  st.session_state.user_answers = {}
496
  st.rerun()
497
  else:
498
- # Progress bar
499
  progress = (st.session_state.current_card / total_cards) * 100
500
  st.markdown(f"""
501
  <div class="progress-bar-container">
502
- <div class="progress-bar" style="width: {progress}%"></div>
503
  </div>
504
  """, unsafe_allow_html=True)
505
 
506
- # Display current card
507
  current_key = st.session_state.flashcard_keys[st.session_state.current_card]
508
  current_answer = st.session_state.flashcards[current_key]
509
 
 
 
510
  st.markdown(f"""
511
- <div class="flashcard-container">
512
- <div>
513
- <div class="card-number">Card {st.session_state.current_card + 1} of {total_cards}</div>
514
- <div class="flashcard-question">{current_key}</div>
 
 
 
 
 
 
 
 
 
 
 
515
  </div>
516
  </div>
517
  """, unsafe_allow_html=True)
518
 
519
- # Show/Hide answer button
520
- if not st.session_state.show_answer:
521
- if st.button("🔍 Show Answer", use_container_width=True, key="show_btn"):
522
- st.session_state.show_answer = True
 
523
  st.rerun()
524
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  st.markdown(f"""
526
- <div class="flashcard-answer">
527
- <strong>Answer:</strong><br><br>
528
- {current_answer}
529
  </div>
530
  """, unsafe_allow_html=True)
531
-
 
 
 
 
 
 
 
 
 
532
  st.markdown("### Did you get it right?")
533
- col1, col2, col3 = st.columns([1, 1, 1])
534
 
535
- with col1:
536
- if st.button("✅ Got it!", use_container_width=True, type="primary"):
537
  st.session_state.score += 1
538
  st.session_state.user_answers[current_key] = True
539
- st.session_state.current_card += 1
540
- st.session_state.show_answer = False
 
 
 
541
  st.rerun()
542
 
543
- with col2:
544
- if st.button("❌ Missed it", use_container_width=True):
545
  st.session_state.user_answers[current_key] = False
546
- st.session_state.current_card += 1
547
- st.session_state.show_answer = False
 
 
 
548
  st.rerun()
549
 
550
- with col3:
551
- if st.button("⏭️ Skip", use_container_width=True):
552
- st.session_state.current_card += 1
553
- st.session_state.show_answer = False
 
 
 
554
  st.rerun()
555
 
556
  # Current score display
@@ -565,580 +667,3 @@ else:
565
 
566
 
567
 
568
- # # Import the CrewAI flashcard module (modified below to remove page range)
569
- # from crewai_flashcard import generate_flashcards
570
-
571
- # # ---------------------------
572
- # # Helper Function: Extract text from PDF
573
- # # ---------------------------
574
- # def extract_text(uploaded_file):
575
- # # Ensure file size is less than 10MB
576
- # uploaded_file.seek(0, os.SEEK_END)
577
- # if uploaded_file.tell() > 10 * 1024 * 1024:
578
- # st.error("File exceeds 10MB limit.")
579
- # return ""
580
- # uploaded_file.seek(0)
581
- # pdf_reader = PdfReader(uploaded_file)
582
- # text = ""
583
- # for page in pdf_reader.pages:
584
- # page_text = page.extract_text()
585
- # if page_text:
586
- # text += page_text + "\n"
587
- # return text
588
-
589
- # # ---------------------------
590
- # # OpenAI Response Functions
591
- # # ---------------------------
592
- # def generate_summary_from_text(text):
593
- # prompt = (
594
- # f"Summarize the following document in a concise manner, highlighting the key points that a student should know:\n\n{text}"
595
- # )
596
- # messages = [
597
- # {"role": "system", "content": "You are an educational assistant."},
598
- # {"role": "user", "content": prompt}
599
- # ]
600
- # completion = client.chat.completions.create(
601
- # model="gpt-4o-mini",
602
- # messages=messages
603
- # )
604
- # return completion.choices[0].message.content.strip()
605
-
606
- # def chat_with_document(text, conversation_history, user_query):
607
- # messages = conversation_history + [
608
- # {"role": "user", "content": f"Based on the following document:\n\n{text}\n\nQuestion: {user_query}"}
609
- # ]
610
- # completion = client.chat.completions.create(
611
- # model="gpt-4o-mini",
612
- # messages=messages
613
- # )
614
- # return completion.choices[0].message.content.strip()
615
-
616
- # def generate_questions_from_text(text, num_questions):
617
- # prompt = (
618
- # f"Generate {num_questions} study questions with answers based on the following document. "
619
- # "Return the output as a table in CSV format with two columns: 'Question' and 'Answer'.\n\nDocument:\n\n{text}"
620
- # )
621
- # messages = [
622
- # {"role": "system", "content": "You are an educational assistant that generates study questions."},
623
- # {"role": "user", "content": prompt}
624
- # ]
625
- # completion = client.chat.completions.create(
626
- # model="gpt-4o-mini",
627
- # messages=messages
628
- # )
629
- # # Expecting CSV output (with header: Question,Answer)
630
- # return completion.choices[0].message.content.strip()
631
-
632
- # # ---------------------------
633
- # # Sidebar: File Upload & Mode Selection
634
- # # ---------------------------
635
- # st.sidebar.title("Study Companion Setup")
636
-
637
- # uploaded_pdf = st.sidebar.file_uploader("Upload your study PDF (max 10MB)", type="pdf")
638
- # mode = st.sidebar.radio("Select Mode", ("Chat", "Test Your Knowledge", "Flashcards"))
639
-
640
- # # For Test Your Knowledge: number of questions (max 50)
641
- # num_questions = None
642
- # if mode == "Test Your Knowledge":
643
- # num_questions = st.sidebar.number_input("Number of questions to generate (max 50):", min_value=1, max_value=50, value=10, step=1)
644
- # if st.sidebar.button("Generate Questions"):
645
- # st.session_state.gen_questions = True
646
-
647
- # # For Flashcards: number of flashcards (max 5)
648
- # num_flashcards = None
649
- # if mode == "Flashcards":
650
- # num_flashcards = st.sidebar.number_input("Number of flashcards to generate (max 5):", min_value=1, max_value=5, value=3, step=1)
651
- # if st.sidebar.button("Generate Flashcards"):
652
- # st.session_state.gen_flashcards = True
653
-
654
- # # ---------------------------
655
- # # Session State Initialization
656
- # # ---------------------------
657
- # if "pdf_text" not in st.session_state:
658
- # st.session_state.pdf_text = None
659
- # if "summary" not in st.session_state:
660
- # st.session_state.summary = None
661
- # if "chat_history" not in st.session_state:
662
- # st.session_state.chat_history = [{"role": "assistant", "content": "Hi, how can I help you with your study material?"}]
663
- # if "questions_table" not in st.session_state:
664
- # st.session_state.questions_table = None
665
- # if "flashcards" not in st.session_state:
666
- # st.session_state.flashcards = {}
667
- # if "current_card" not in st.session_state:
668
- # st.session_state.current_card = 0
669
- # if "score" not in st.session_state:
670
- # st.session_state.score = 0
671
- # if "show_answer" not in st.session_state:
672
- # st.session_state.show_answer = False
673
-
674
- # # ---------------------------
675
- # # Process PDF Upload
676
- # # ---------------------------
677
- # if uploaded_pdf is not None:
678
- # with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
679
- # tmp.write(uploaded_pdf.read())
680
- # st.session_state.pdf_file_path = tmp.name
681
- # st.session_state.pdf_text = extract_text(uploaded_pdf)
682
- # if st.session_state.pdf_text:
683
- # st.sidebar.success("PDF uploaded and processed successfully!")
684
- # else:
685
- # st.sidebar.error("Failed to extract text from the PDF.")
686
-
687
- # # ---------------------------
688
- # # Main Area: Mode-Based Display (using side menu)
689
- # # ---------------------------
690
- # st.title("Study Companion: PDF-based Learning")
691
-
692
- # if st.session_state.pdf_text is None:
693
- # st.info("Please upload a PDF from the sidebar to begin.")
694
- # else:
695
- # if mode == "Chat":
696
- # st.header("Chat with Your Study Companion")
697
- # for msg in st.session_state.chat_history:
698
- # st.chat_message(msg["role"]).write(msg["content"])
699
- # user_question = st.chat_input("Ask a question about the document:")
700
- # if user_question:
701
- # st.session_state.chat_history.append({"role": "user", "content": user_question})
702
- # st.chat_message("user").write(user_question)
703
- # with st.spinner("Processing your question..."):
704
- # response = chat_with_document(st.session_state.pdf_text, st.session_state.chat_history, user_question)
705
- # st.session_state.chat_history.append({"role": "assistant", "content": response})
706
- # st.chat_message("assistant").write(response)
707
-
708
- # elif mode == "Test Your Knowledge":
709
- # st.header("Test Your Knowledge")
710
- # if num_questions is None or not st.session_state.get("gen_questions", False):
711
- # st.info("Enter the number of questions and press 'Generate Questions' from the sidebar.")
712
- # else:
713
- # with st.spinner("Generating questions..."):
714
- # questions_csv = generate_questions_from_text(st.session_state.pdf_text, num_questions)
715
- # # Convert CSV output into a table (assuming header row "Question,Answer")
716
- # try:
717
- # lines = questions_csv.splitlines()
718
- # if len(lines) < 2:
719
- # st.error("Failed to generate questions properly.")
720
- # else:
721
- # header = lines[0].split(",")
722
- # data = [line.split(",") for line in lines[1:]]
723
- # st.table(data, headers=header)
724
- # st.session_state.questions_table = data
725
- # except Exception as e:
726
- # st.error(f"Error processing questions: {e}")
727
-
728
- # elif mode == "Flashcards":
729
- # st.header("Practice Flashcards")
730
- # if not st.session_state.get("gen_flashcards", False):
731
- # st.info("Enter the number of flashcards and press 'Generate Flashcards' from the sidebar.")
732
- # else:
733
- # if st.button("Reset Flashcards"):
734
- # st.session_state.flashcards = {}
735
- # st.session_state.current_card = 0
736
- # st.session_state.score = 0
737
- # st.session_state.show_answer = False
738
- # st.session_state.gen_flashcards = False
739
- # if st.session_state.get("gen_flashcards", False):
740
- # # Generate flashcards using the CrewAI module (which returns a Python dictionary)
741
- # flashcards = generate_flashcards(st.session_state.pdf_file_path, num_flashcards)
742
- # st.session_state.flashcards = flashcards
743
- # st.session_state.current_card = 0
744
- # st.session_state.score = 0
745
- # st.session_state.show_answer = False
746
- # st.success("Flashcards generated successfully!")
747
- # st.session_state.gen_flashcards = False # reset flag after generation
748
-
749
- # if not st.session_state.flashcards:
750
- # st.info("No flashcards available. Click the 'Generate Flashcards' button in the sidebar.")
751
- # else:
752
- # total_cards = len(st.session_state.flashcards)
753
- # if st.session_state.current_card >= total_cards:
754
- # st.success(f"You've completed all flashcards! Final Score: {st.session_state.score} / {total_cards}")
755
- # st.info("Restart the session or generate new flashcards from the sidebar.")
756
- # else:
757
- # flashcards = st.session_state.flashcards
758
- # current_keys = list(flashcards.keys())
759
- # current_question = current_keys[st.session_state.current_card]
760
- # current_answer = flashcards[current_question]
761
- # st.write(f"**Question:** {current_question}")
762
- # if st.button("Show Answer"):
763
- # st.session_state.show_answer = True
764
- # if st.session_state.show_answer:
765
- # st.write(f"**Answer:** {current_answer}")
766
- # col1, col2 = st.columns(2)
767
- # with col1:
768
- # if st.button("Correct"):
769
- # st.session_state.score += 1
770
- # st.success("Correct!")
771
- # with col2:
772
- # if st.button("Wrong"):
773
- # st.error("Incorrect!")
774
- # if st.button("Next Card"):
775
- # st.session_state.current_card += 1
776
- # st.session_state.show_answer = False
777
- # st.rerun()
778
- # st.write(f"**Current Score:** {st.session_state.score} / {total_cards}")
779
-
780
-
781
-
782
-
783
- ######################################################################################################
784
-
785
-
786
- # # ---------------------------
787
- # # Helper Function: Extract text from PDF
788
- # # ---------------------------
789
- # def extract_text(uploaded_file):
790
- # pdf_reader = PdfReader(uploaded_file)
791
- # text = ""
792
- # for page in pdf_reader.pages:
793
- # page_text = page.extract_text()
794
- # if page_text:
795
- # text += page_text
796
- # return text
797
-
798
- # # ---------------------------
799
- # # OpenAI Response Functions (using new style)
800
- # # ---------------------------
801
- # def generate_summary_from_text(text):
802
- # prompt = (
803
- # f"Summarize the following document in a concise manner, "
804
- # "highlighting the key points that a student should know:\n\n{text}"
805
- # )
806
- # messages = [
807
- # {"role": "system", "content": "You are an educational assistant."},
808
- # {"role": "user", "content": prompt}
809
- # ]
810
- # completion = client.chat.completions.create(
811
- # model="gpt-4o-mini",
812
- # messages=messages
813
- # )
814
- # return completion.choices[0].message.content.strip()
815
-
816
- # def chat_with_document(text, conversation_history, user_query):
817
- # messages = conversation_history + [
818
- # {"role": "user", "content": f"Based on the following document:\n\n{text}\n\nQuestion: {user_query}"}
819
- # ]
820
- # completion = client.chat.completions.create(
821
- # model="gpt-4o-mini",
822
- # messages=messages
823
- # )
824
- # return completion.choices[0].message.content.strip()
825
-
826
- # def generate_flashcards_from_text(text, num_cards):
827
- # prompt = (
828
- # f"Generate {num_cards} flashcards based on the following document. \n\nDocument:\n\n{text} "
829
- # "Return a Python dictionary where each key is a flashcard question and its corresponding value is the answer. "
830
- # #"Do not include any additional text.\n\nDocument:\n\n{text}"
831
- # )
832
- # messages = [
833
- # {"role": "system", "content": "You are an educational assistant that creates study flashcards."},
834
- # {"role": "user", "content": prompt}
835
- # ]
836
- # completion = client.chat.completions.create(
837
- # model="gpt-4o-mini",
838
- # messages=messages
839
- # )
840
- # output = completion.choices[0].message.content.strip()
841
- # try:
842
- # # Use literal_eval to safely evaluate the string as a Python dictionary.
843
- # flashcards = literal_eval(output)
844
- # if isinstance(flashcards, dict):
845
- # return flashcards
846
- # else:
847
- # return {}
848
- # except Exception as e:
849
- # st.error(f"Error parsing flashcards: {e}")
850
- # return {}
851
-
852
- # # ---------------------------
853
- # # Sidebar: File Upload & Mode Selection
854
- # # ---------------------------
855
- # st.sidebar.title("Study Companion Setup")
856
-
857
- # uploaded_pdf = st.sidebar.file_uploader("Upload your study PDF", type="pdf")
858
- # mode = st.sidebar.radio("Select Mode", ("Summary", "Chat", "Flashcards"))
859
-
860
- # num_flashcards = None
861
- # if mode == "Flashcards":
862
- # num_flashcards = st.sidebar.number_input("Number of flashcards to generate:", min_value=1, max_value=20, value=5, step=1)
863
-
864
- # # ---------------------------
865
- # # Session State Initialization
866
- # # ---------------------------
867
- # if "pdf_text" not in st.session_state:
868
- # st.session_state.pdf_text = None
869
- # if "summary" not in st.session_state:
870
- # st.session_state.summary = None
871
- # if "chat_history" not in st.session_state:
872
- # st.session_state.chat_history = [{"role": "assistant", "content": "Hi, how can I help you with your study material?"}]
873
- # if "flashcards" not in st.session_state:
874
- # st.session_state.flashcards = {}
875
- # if "current_card" not in st.session_state:
876
- # st.session_state.current_card = 0
877
- # if "score" not in st.session_state:
878
- # st.session_state.score = 0
879
- # if "show_answer" not in st.session_state:
880
- # st.session_state.show_answer = False
881
-
882
- # # ---------------------------
883
- # # Process PDF Upload
884
- # # ---------------------------
885
- # if uploaded_pdf is not None:
886
- # with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
887
- # tmp.write(uploaded_pdf.read())
888
- # st.session_state.pdf_file_path = tmp.name
889
- # st.session_state.pdf_text = extract_text(uploaded_pdf)
890
- # st.sidebar.success("PDF uploaded and processed successfully!")
891
-
892
- # # ---------------------------
893
- # # Main Area: Mode-Based Display
894
- # # ---------------------------
895
- # st.title("Study Companion: PDF-based Learning")
896
-
897
- # if st.session_state.pdf_text is None:
898
- # st.info("Please upload a PDF from the sidebar to begin.")
899
- # else:
900
- # if mode == "Summary":
901
- # st.header("Summary & Key Points")
902
- # if st.session_state.summary is None:
903
- # with st.spinner("Generating summary..."):
904
- # st.session_state.summary = generate_summary_from_text(st.session_state.pdf_text)
905
- # st.write(st.session_state.summary)
906
-
907
- # elif mode == "Chat":
908
- # st.header("Chat with Your Study Companion")
909
- # for msg in st.session_state.chat_history:
910
- # st.chat_message(msg["role"]).write(msg["content"])
911
- # user_question = st.chat_input("Ask a question about the document:")
912
- # if user_question:
913
- # st.session_state.chat_history.append({"role": "user", "content": user_question})
914
- # st.chat_message("user").write(user_question)
915
- # with st.spinner("Processing your question..."):
916
- # response = chat_with_document(st.session_state.pdf_text, st.session_state.chat_history, user_question)
917
- # st.session_state.chat_history.append({"role": "assistant", "content": response})
918
- # st.chat_message("assistant").write(response)
919
-
920
- # elif mode == "Flashcards":
921
- # st.header("Practice Flashcards")
922
- # if st.button("Generate Flashcards"):
923
- # with st.spinner("Generating flashcards..."):
924
- # flashcards = generate_flashcards_from_text(st.session_state.pdf_text, num_flashcards)
925
- # st.session_state.flashcards = flashcards
926
- # st.session_state.current_card = 0
927
- # st.session_state.score = 0
928
- # st.session_state.show_answer = False
929
- # st.success("Flashcards generated successfully!")
930
-
931
- # if not st.session_state.flashcards:
932
- # st.info("No flashcards available. Click the button above to generate flashcards.")
933
- # else:
934
- # total_cards = len(st.session_state.flashcards)
935
- # if st.session_state.current_card >= total_cards:
936
- # st.success(f"You've completed all flashcards! Final Score: {st.session_state.score} / {total_cards}")
937
- # st.info("Restart the session or generate new flashcards from the sidebar.")
938
- # else:
939
- # flashcards = st.session_state.flashcards
940
- # current_keys = list(flashcards.keys())
941
- # current_question = current_keys[st.session_state.current_card]
942
- # current_answer = flashcards[current_question]
943
- # st.write(f"**Question:** {current_question}")
944
- # if st.button("Show Answer"):
945
- # st.session_state.show_answer = True
946
- # if st.session_state.show_answer:
947
- # st.write(f"**Answer:** {current_answer}")
948
- # col1, col2 = st.columns(2)
949
- # with col1:
950
- # if st.button("Correct"):
951
- # st.session_state.score += 1
952
- # st.success("Correct!")
953
- # with col2:
954
- # if st.button("Wrong"):
955
- # st.error("Incorrect!")
956
- # if st.button("Next Card"):
957
- # st.session_state.current_card += 1
958
- # st.session_state.show_answer = False
959
- # st.rerun()
960
- # st.write(f"**Current Score:** {st.session_state.score} / {total_cards}")
961
-
962
-
963
-
964
-
965
- # # ---------------------------
966
- # # Helper Function: Extract text from PDF
967
- # # ---------------------------
968
- # def extract_text(uploaded_file):
969
- # pdf_reader = PdfReader(uploaded_file)
970
- # text = ""
971
- # for page in pdf_reader.pages:
972
- # page_text = page.extract_text()
973
- # if page_text:
974
- # text += page_text
975
- # return text
976
-
977
- # # ---------------------------
978
- # # OpenAI Response Functions (using new style)
979
- # # ---------------------------
980
- # def generate_summary_from_text(text):
981
- # prompt = (
982
- # f"Summarize the following document in a concise manner, "
983
- # "highlighting the key points that a student should know:\n\n{text}"
984
- # )
985
- # messages = [
986
- # {"role": "system", "content": "You are an educational assistant."},
987
- # {"role": "user", "content": prompt}
988
- # ]
989
- # completion = client.chat.completions.create(
990
- # model="gpt-4o-mini",
991
- # messages=messages
992
- # )
993
- # return completion.choices[0].message.content.strip()
994
-
995
- # def chat_with_document(text, conversation_history, user_query):
996
- # # Build a message list that includes the conversation history plus the new query with context.
997
- # messages = conversation_history + [
998
- # {"role": "user", "content": f"Based on the following document:\n\n{text}\n\nQuestion: {user_query}"}
999
- # ]
1000
- # completion = client.chat.completions.create(
1001
- # model="gpt-4o-mini",
1002
- # messages=messages
1003
- # )
1004
- # return completion.choices[0].message.content.strip()
1005
-
1006
- # def generate_flashcards_from_text(text, num_cards):
1007
- # prompt = (
1008
- # f"Generate {num_cards} flashcards based on the following document. "
1009
- # "Return a Python dictionary (in valid JSON format) where each key is a flashcard question and its value is the corresponding answer. "
1010
- # f"Document:\n\n{text}"
1011
- # )
1012
- # messages = [
1013
- # {"role": "system", "content": "You are an educational assistant that creates study flashcards."},
1014
- # {"role": "user", "content": prompt}
1015
- # ]
1016
- # completion = client.chat.completions.create(
1017
- # model="gpt-4o-mini",
1018
- # messages=messages
1019
- # )
1020
- # output = completion.choices[0].message.content.strip()
1021
- # try:
1022
- # flashcards = json.loads(output)
1023
- # if isinstance(flashcards, dict):
1024
- # return flashcards
1025
- # else:
1026
- # return {}
1027
- # except Exception as e:
1028
- # st.error(f"Error parsing flashcards: {e}")
1029
- # return {}
1030
-
1031
- # # ---------------------------
1032
- # # Sidebar: File Upload & Mode Selection
1033
- # # ---------------------------
1034
- # st.sidebar.title("Study Companion Setup")
1035
-
1036
- # uploaded_pdf = st.sidebar.file_uploader("Upload your study PDF", type="pdf")
1037
- # mode = st.sidebar.radio("Select Mode", ("Summary", "Chat", "Flashcards"))
1038
-
1039
- # # For Flashcards, allow user to input number of flashcards
1040
- # num_flashcards = None
1041
- # if mode == "Flashcards":
1042
- # num_flashcards = st.sidebar.number_input("Number of flashcards to generate:", min_value=1, max_value=20, value=5, step=1)
1043
-
1044
- # # ---------------------------
1045
- # # Session State Initialization
1046
- # # ---------------------------
1047
- # if "pdf_text" not in st.session_state:
1048
- # st.session_state.pdf_text = None
1049
- # if "summary" not in st.session_state:
1050
- # st.session_state.summary = None
1051
- # if "chat_history" not in st.session_state:
1052
- # st.session_state.chat_history = [{"role": "assistant", "content": "Hi, how can I help you with your study material?"}]
1053
- # if "flashcards" not in st.session_state:
1054
- # st.session_state.flashcards = {}
1055
- # if "current_card" not in st.session_state:
1056
- # st.session_state.current_card = 0
1057
- # if "score" not in st.session_state:
1058
- # st.session_state.score = 0
1059
- # if "show_answer" not in st.session_state:
1060
- # st.session_state.show_answer = False
1061
-
1062
- # # ---------------------------
1063
- # # Process PDF Upload
1064
- # # ---------------------------
1065
- # if uploaded_pdf is not None:
1066
- # with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
1067
- # tmp.write(uploaded_pdf.read())
1068
- # pdf_file_path = tmp.name
1069
- # # Extract text from the PDF (all pages)
1070
- # st.session_state.pdf_text = extract_text(pdf_file_path)
1071
- # st.sidebar.success("PDF uploaded and processed successfully!")
1072
-
1073
- # # ---------------------------
1074
- # # Main Area: Mode-Based Display
1075
- # # ---------------------------
1076
- # st.title("Study Companion: PDF-based Learning")
1077
-
1078
- # if st.session_state.pdf_text is None:
1079
- # st.info("Please upload a PDF from the sidebar to begin.")
1080
- # else:
1081
- # if mode == "Summary":
1082
- # st.header("Summary & Key Points")
1083
- # if st.session_state.summary is None:
1084
- # with st.spinner("Generating summary..."):
1085
- # st.session_state.summary = generate_summary_from_text(st.session_state.pdf_text)
1086
- # st.write(st.session_state.summary)
1087
-
1088
- # elif mode == "Chat":
1089
- # st.header("Chat with Your Study Companion")
1090
- # # Display persistent chat history
1091
- # for msg in st.session_state.chat_history:
1092
- # st.chat_message(msg["role"]).write(msg["content"])
1093
- # user_question = st.chat_input("Ask a question about the document:")
1094
- # if user_question:
1095
- # st.session_state.chat_history.append({"role": "user", "content": user_question})
1096
- # st.chat_message("user").write(user_question)
1097
- # with st.spinner("Processing your question..."):
1098
- # response = chat_with_document(st.session_state.pdf_text, st.session_state.chat_history, user_question)
1099
- # st.session_state.chat_history.append({"role": "assistant", "content": response})
1100
- # st.chat_message("assistant").write(response)
1101
-
1102
- # elif mode == "Flashcards":
1103
- # st.header("Practice Flashcards")
1104
- # # Provide a button to generate flashcards on demand.
1105
- # if st.button("Generate Flashcards"):
1106
- # with st.spinner("Generating flashcards..."):
1107
- # flashcards = generate_flashcards_from_text(st.session_state.pdf_text, num_flashcards)
1108
- # st.session_state.flashcards = flashcards
1109
- # st.session_state.current_card = 0
1110
- # st.session_state.score = 0
1111
- # st.session_state.show_answer = False
1112
- # st.success("Flashcards generated successfully!")
1113
-
1114
- # if not st.session_state.flashcards:
1115
- # st.info("No flashcards available. Click the button above to generate flashcards.")
1116
- # else:
1117
- # total_cards = len(st.session_state.flashcards)
1118
- # if st.session_state.current_card >= total_cards:
1119
- # st.success(f"You've completed all flashcards! Final Score: {st.session_state.score} / {total_cards}")
1120
- # st.info("Restart the session or generate new flashcards from the sidebar.")
1121
- # else:
1122
- # flashcards = st.session_state.flashcards
1123
- # # Get the current flashcard key-value pair.
1124
- # current_keys = list(flashcards.keys())
1125
- # current_key = current_keys[st.session_state.current_card]
1126
- # current_answer = flashcards[current_key]
1127
- # st.write(f"**Question:** {current_key}")
1128
- # if st.button("Show Answer"):
1129
- # st.session_state.show_answer = True
1130
- # if st.session_state.show_answer:
1131
- # st.write(f"**Answer:** {current_answer}")
1132
- # col1, col2 = st.columns(2)
1133
- # with col1:
1134
- # if st.button("Correct"):
1135
- # st.session_state.score += 1
1136
- # st.success("Correct!")
1137
- # with col2:
1138
- # if st.button("Wrong"):
1139
- # st.error("Incorrect!")
1140
- # if st.button("Next Card"):
1141
- # st.session_state.current_card += 1
1142
- # st.session_state.show_answer = False
1143
- # st.rerun()
1144
- # st.write(f"**Current Score:** {st.session_state.score} / {total_cards}")
 
70
  {"role": "user", "content": prompt}
71
  ]
72
  completion = client.chat.completions.create(
73
+ model="gpt-5-mini",
74
  messages=messages
75
  )
76
  return completion.choices[0].message.content.strip()
77
 
78
  def generate_flashcards_from_text(text, num_cards):
79
  prompt = (
80
+ f"Generate exactly {num_cards} flashcards based on the following document.\n\n"
81
+ f"Document:\n\n{text}\n\n"
82
+ "Return ONLY a valid JSON object (not a code block) where each key is a flashcard question and its value is the answer. "
83
+ "Format: {\"Question 1?\": \"Answer 1\", \"Question 2?\": \"Answer 2\"}. "
84
+ "Do not include ```json or any other text, just the raw JSON object."
85
  )
86
  messages = [
87
+ {"role": "system", "content": "You are an educational assistant that creates study flashcards. Always return valid JSON only, without code blocks or additional text."},
88
  {"role": "user", "content": prompt}
89
  ]
90
  completion = client.chat.completions.create(
 
92
  messages=messages
93
  )
94
  output = completion.choices[0].message.content.strip()
95
+
96
+ # Clean up the output - remove code block markers if present
97
+ if output.startswith("```json"):
98
+ output = output[7:] # Remove ```json
99
+ elif output.startswith("```"):
100
+ output = output[3:] # Remove ```
101
+ if output.endswith("```"):
102
+ output = output[:-3] # Remove trailing ```
103
+ output = output.strip()
104
+
105
  try:
106
+ # Try JSON first (more reliable)
107
+ flashcards = json.loads(output)
108
+ if isinstance(flashcards, dict) and len(flashcards) > 0:
109
  return flashcards
110
  else:
111
+ st.error("Generated flashcards are empty or invalid format.")
112
+ return {}
113
+ except json.JSONDecodeError:
114
+ # Fallback to literal_eval for Python dict syntax
115
+ try:
116
+ flashcards = literal_eval(output)
117
+ if isinstance(flashcards, dict) and len(flashcards) > 0:
118
+ return flashcards
119
+ else:
120
+ st.error("Generated flashcards are empty or invalid format.")
121
+ return {}
122
+ except Exception as e:
123
+ st.error(f"Error parsing flashcards: {e}")
124
+ st.error(f"Received output: {output[:200]}...") # Show first 200 chars for debugging
125
  return {}
 
 
 
126
 
127
  # ---------------------------
128
  # Sidebar: File Upload & Mode Selection
 
356
  elif mode == "Flashcards":
357
  st.header("🎴 Interactive Flashcards")
358
 
359
+ # Custom CSS for flashcard styling with flip effect
360
  st.markdown("""
361
  <style>
362
+ .flashcard-3d-container {
363
+ perspective: 1000px;
364
+ margin: 30px 0;
365
+ }
366
+ .flashcard-flip {
367
+ position: relative;
368
+ min-height: 300px;
369
+ transition: transform 0.6s;
370
+ transform-style: preserve-3d;
371
+ }
372
+ .flashcard-flip.flipped {
373
+ transform: rotateY(180deg);
374
+ }
375
+ .flashcard-face {
376
+ position: absolute;
377
+ width: 100%;
378
+ min-height: 300px;
379
+ backface-visibility: hidden;
380
  border-radius: 15px;
381
  padding: 40px;
 
382
  box-shadow: 0 10px 30px rgba(0,0,0,0.3);
 
383
  display: flex;
384
  align-items: center;
385
  justify-content: center;
386
  text-align: center;
 
387
  }
388
+ .flashcard-front {
389
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
390
+ color: white;
391
  }
392
+ .flashcard-back {
393
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
394
  color: white;
395
+ transform: rotateY(180deg);
396
+ }
397
+ .flashcard-question {
398
  font-size: 24px;
399
  font-weight: bold;
400
+ line-height: 1.4;
401
  }
402
  .flashcard-answer {
403
+ font-size: 20px;
404
+ line-height: 1.6;
405
+ }
406
+ .card-number {
407
+ position: absolute;
408
+ top: 15px;
409
+ left: 20px;
410
+ font-size: 14px;
411
+ opacity: 0.9;
412
+ }
413
+ .flip-instruction {
414
+ position: absolute;
415
+ bottom: 15px;
416
+ width: 100%;
417
+ text-align: center;
418
+ font-size: 14px;
419
+ opacity: 0.8;
420
  }
421
  .score-card {
422
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
423
  border-radius: 10px;
424
  padding: 20px;
425
  color: white;
426
+ font-size: 18px;
427
  font-weight: bold;
428
  text-align: center;
429
  margin: 20px 0;
 
431
  .progress-bar-container {
432
  background: #e0e0e0;
433
  border-radius: 10px;
434
+ height: 25px;
435
  margin: 20px 0;
436
  overflow: hidden;
437
+ position: relative;
438
  }
439
  .progress-bar {
440
  background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);
441
  height: 100%;
442
  transition: width 0.3s ease;
443
+ display: flex;
444
+ align-items: center;
445
+ justify-content: center;
446
  color: white;
447
+ font-weight: bold;
448
+ font-size: 14px;
449
+ }
450
+ .navigation-buttons {
451
+ display: flex;
452
+ gap: 10px;
453
+ margin: 20px 0;
454
  }
455
  .stButton > button {
456
  border-radius: 8px;
 
553
  st.session_state.user_answers = {}
554
  st.rerun()
555
  else:
556
+ # Progress bar with percentage
557
  progress = (st.session_state.current_card / total_cards) * 100
558
  st.markdown(f"""
559
  <div class="progress-bar-container">
560
+ <div class="progress-bar" style="width: {progress}%">{int(progress)}%</div>
561
  </div>
562
  """, unsafe_allow_html=True)
563
 
564
+ # Display current card with flip effect
565
  current_key = st.session_state.flashcard_keys[st.session_state.current_card]
566
  current_answer = st.session_state.flashcards[current_key]
567
 
568
+ # Flip card display
569
+ flip_class = "flipped" if st.session_state.show_answer else ""
570
  st.markdown(f"""
571
+ <div class="flashcard-3d-container">
572
+ <div class="flashcard-flip {flip_class}">
573
+ <div class="flashcard-face flashcard-front">
574
+ <div>
575
+ <div class="card-number">Card {st.session_state.current_card + 1} of {total_cards}</div>
576
+ <div class="flashcard-question">{current_key}</div>
577
+ <div class="flip-instruction">👆 Click below to flip</div>
578
+ </div>
579
+ </div>
580
+ <div class="flashcard-face flashcard-back">
581
+ <div>
582
+ <div class="card-number">Card {st.session_state.current_card + 1} of {total_cards}</div>
583
+ <div class="flashcard-answer"><strong>Answer:</strong><br><br>{current_answer}</div>
584
+ </div>
585
+ </div>
586
  </div>
587
  </div>
588
  """, unsafe_allow_html=True)
589
 
590
+ # Flip button
591
+ col_flip1, col_flip2, col_flip3 = st.columns([1, 2, 1])
592
+ with col_flip2:
593
+ if st.button("🔄 Flip Card", use_container_width=True, key="flip_btn"):
594
+ st.session_state.show_answer = not st.session_state.show_answer
595
  st.rerun()
596
+
597
+ # Navigation buttons (Previous/Next)
598
+ st.markdown("### Navigation")
599
+ nav_col1, nav_col2, nav_col3 = st.columns([1, 1, 1])
600
+
601
+ with nav_col1:
602
+ if st.button("⬅️ Previous", use_container_width=True, key="prev_btn",
603
+ disabled=(st.session_state.current_card == 0)):
604
+ st.session_state.current_card -= 1
605
+ st.session_state.show_answer = False
606
+ st.rerun()
607
+
608
+ with nav_col2:
609
+ # Show current position
610
  st.markdown(f"""
611
+ <div style="text-align: center; padding: 10px; font-size: 16px; font-weight: bold;">
612
+ {st.session_state.current_card + 1} / {total_cards}
 
613
  </div>
614
  """, unsafe_allow_html=True)
615
+
616
+ with nav_col3:
617
+ if st.button("Next ➡️", use_container_width=True, key="next_btn",
618
+ disabled=(st.session_state.current_card >= total_cards - 1)):
619
+ st.session_state.current_card += 1
620
+ st.session_state.show_answer = False
621
+ st.rerun()
622
+
623
+ # Self-assessment buttons (only when answer is shown)
624
+ if st.session_state.show_answer:
625
  st.markdown("### Did you get it right?")
626
+ assess_col1, assess_col2, assess_col3 = st.columns([1, 1, 1])
627
 
628
+ with assess_col1:
629
+ if st.button("✅ Got it!", use_container_width=True, type="primary", key="correct_btn"):
630
  st.session_state.score += 1
631
  st.session_state.user_answers[current_key] = True
632
+ if st.session_state.current_card < total_cards - 1:
633
+ st.session_state.current_card += 1
634
+ st.session_state.show_answer = False
635
+ else:
636
+ st.session_state.current_card += 1
637
  st.rerun()
638
 
639
+ with assess_col2:
640
+ if st.button("❌ Missed it", use_container_width=True, key="wrong_btn"):
641
  st.session_state.user_answers[current_key] = False
642
+ if st.session_state.current_card < total_cards - 1:
643
+ st.session_state.current_card += 1
644
+ st.session_state.show_answer = False
645
+ else:
646
+ st.session_state.current_card += 1
647
  st.rerun()
648
 
649
+ with assess_col3:
650
+ if st.button("⏭️ Skip", use_container_width=True, key="skip_btn"):
651
+ if st.session_state.current_card < total_cards - 1:
652
+ st.session_state.current_card += 1
653
+ st.session_state.show_answer = False
654
+ else:
655
+ st.session_state.current_card += 1
656
  st.rerun()
657
 
658
  # Current score display
 
667
 
668
 
669