matsuap commited on
Commit
4064f62
·
verified ·
1 Parent(s): 86eea89

Upload folder using huggingface_hub

Browse files
api/flashcards.py CHANGED
@@ -54,7 +54,7 @@ async def generate_flashcards(
54
  )
55
 
56
  if not cards_data:
57
- raise HTTPException(status_code=500, detail="Failed to generate flashcards")
58
 
59
  # 2. Save Flashcard Set to DB
60
  title = request.topic if request.topic else f"Flashcards {len(cards_data)}"
@@ -80,7 +80,15 @@ async def generate_flashcards(
80
  db.commit()
81
  db.refresh(db_set)
82
 
83
- return db_set
 
 
 
 
 
 
 
 
84
 
85
  except HTTPException:
86
  raise
@@ -100,7 +108,18 @@ async def list_flashcard_sets(
100
  sets = db.query(db_models.FlashcardSet).filter(
101
  db_models.FlashcardSet.user_id == current_user.id
102
  ).order_by(db_models.FlashcardSet.created_at.desc()).all()
103
- return sets
 
 
 
 
 
 
 
 
 
 
 
104
  except Exception as e:
105
  raise HTTPException(status_code=500, detail=str(e))
106
 
@@ -121,7 +140,15 @@ async def get_flashcard_set(
121
  if not db_set:
122
  raise HTTPException(status_code=404, detail="Flashcard set not found")
123
 
124
- return db_set
 
 
 
 
 
 
 
 
125
 
126
  @router.post("/explain")
127
  async def explain_flashcard(
 
54
  )
55
 
56
  if not cards_data:
57
+ raise HTTPException(status_code=500, detail="AI returned an empty response")
58
 
59
  # 2. Save Flashcard Set to DB
60
  title = request.topic if request.topic else f"Flashcards {len(cards_data)}"
 
80
  db.commit()
81
  db.refresh(db_set)
82
 
83
+ return {
84
+ "id": db_set.id,
85
+ "title": db_set.title,
86
+ "difficulty": db_set.difficulty,
87
+ "created_at": db_set.created_at,
88
+ "parent_file_id": db_set.source_id,
89
+ "parent_file_key": source.s3_key if source else None,
90
+ "flashcards": db_set.flashcards
91
+ }
92
 
93
  except HTTPException:
94
  raise
 
108
  sets = db.query(db_models.FlashcardSet).filter(
109
  db_models.FlashcardSet.user_id == current_user.id
110
  ).order_by(db_models.FlashcardSet.created_at.desc()).all()
111
+ return [
112
+ {
113
+ "id": s.id,
114
+ "title": s.title,
115
+ "difficulty": s.difficulty,
116
+ "created_at": s.created_at,
117
+ "parent_file_id": s.source_id,
118
+ "parent_file_key": s.source.s3_key if s.source else None,
119
+ "flashcards": s.flashcards
120
+ }
121
+ for s in sets
122
+ ]
123
  except Exception as e:
124
  raise HTTPException(status_code=500, detail=str(e))
125
 
 
140
  if not db_set:
141
  raise HTTPException(status_code=404, detail="Flashcard set not found")
142
 
143
+ return {
144
+ "id": db_set.id,
145
+ "title": db_set.title,
146
+ "difficulty": db_set.difficulty,
147
+ "created_at": db_set.created_at,
148
+ "parent_file_id": db_set.source_id,
149
+ "parent_file_key": db_set.source.s3_key if db_set.source else None,
150
+ "flashcards": db_set.flashcards
151
+ }
152
 
153
  @router.post("/explain")
154
  async def explain_flashcard(
api/mindmaps.py CHANGED
@@ -55,8 +55,12 @@ async def generate_mindmap(
55
  db.refresh(db_mindmap)
56
 
57
  return MindMapResponse(
 
58
  title=db_mindmap.title,
59
  mermaid_code=db_mindmap.mermaid_code,
 
 
 
60
  message="Mind map generated successfully"
61
  )
62
 
@@ -81,8 +85,12 @@ async def list_mindmaps(
81
 
82
  return [
83
  MindMapResponse(
 
84
  title=m.title,
85
  mermaid_code=m.mermaid_code,
 
 
 
86
  message="Retrieved successfully"
87
  ) for m in mindmaps
88
  ]
 
55
  db.refresh(db_mindmap)
56
 
57
  return MindMapResponse(
58
+ id=db_mindmap.id,
59
  title=db_mindmap.title,
60
  mermaid_code=db_mindmap.mermaid_code,
61
+ parent_file_id=db_mindmap.source_id,
62
+ parent_file_key=source.s3_key if source else None,
63
+ created_at=db_mindmap.created_at,
64
  message="Mind map generated successfully"
65
  )
66
 
 
85
 
86
  return [
87
  MindMapResponse(
88
+ id=m.id,
89
  title=m.title,
90
  mermaid_code=m.mermaid_code,
91
+ parent_file_id=m.source_id,
92
+ parent_file_key=m.source.s3_key if m.source else None,
93
+ created_at=m.created_at,
94
  message="Retrieved successfully"
95
  ) for m in mindmaps
96
  ]
api/podcast.py CHANGED
@@ -75,6 +75,9 @@ async def generate_podcast(
75
  ).first()
76
  if not source:
77
  raise HTTPException(status_code=403, detail="Not authorized to access this file")
 
 
 
78
 
79
  # 2. Generate Script
80
  script = await podcast_service.generate_script(
@@ -126,7 +129,8 @@ async def generate_podcast(
126
  s3_key=s3_key,
127
  s3_url=public_url,
128
  script=script,
129
- user_id=current_user.id
 
130
  )
131
  db.add(db_podcast)
132
  db.commit()
@@ -170,6 +174,8 @@ async def list_podcasts(
170
  "public_url": p.s3_url,
171
  "private_url": s3_service.get_presigned_url(p.s3_key),
172
  "script_preview": (p.script[:200] + "...") if p.script else "",
 
 
173
  "created_at": p.created_at
174
  }
175
  for p in podcasts
 
75
  ).first()
76
  if not source:
77
  raise HTTPException(status_code=403, detail="Not authorized to access this file")
78
+ source_id = source.id
79
+ else:
80
+ source_id = None
81
 
82
  # 2. Generate Script
83
  script = await podcast_service.generate_script(
 
129
  s3_key=s3_key,
130
  s3_url=public_url,
131
  script=script,
132
+ user_id=current_user.id,
133
+ source_id=source_id
134
  )
135
  db.add(db_podcast)
136
  db.commit()
 
174
  "public_url": p.s3_url,
175
  "private_url": s3_service.get_presigned_url(p.s3_key),
176
  "script_preview": (p.script[:200] + "...") if p.script else "",
177
+ "parent_file_id": p.source_id,
178
+ "parent_file_key": p.source.s3_key if p.source else None,
179
  "created_at": p.created_at
180
  }
181
  for p in podcasts
api/quizzes.py CHANGED
@@ -82,7 +82,15 @@ async def generate_quiz(
82
  db.commit()
83
  db.refresh(db_set)
84
 
85
- return db_set
 
 
 
 
 
 
 
 
86
 
87
  except HTTPException:
88
  raise
@@ -102,7 +110,18 @@ async def list_quiz_sets(
102
  sets = db.query(db_models.QuizSet).filter(
103
  db_models.QuizSet.user_id == current_user.id
104
  ).order_by(db_models.QuizSet.created_at.desc()).all()
105
- return sets
 
 
 
 
 
 
 
 
 
 
 
106
  except Exception as e:
107
  raise HTTPException(status_code=500, detail=str(e))
108
 
@@ -123,7 +142,15 @@ async def get_quiz_set(
123
  if not db_set:
124
  raise HTTPException(status_code=404, detail="Quiz set not found")
125
 
126
- return db_set
 
 
 
 
 
 
 
 
127
 
128
  @router.delete("/set/{set_id}")
129
  async def delete_quiz_set(
 
82
  db.commit()
83
  db.refresh(db_set)
84
 
85
+ return {
86
+ "id": db_set.id,
87
+ "title": db_set.title,
88
+ "difficulty": db_set.difficulty,
89
+ "created_at": db_set.created_at,
90
+ "parent_file_id": db_set.source_id,
91
+ "parent_file_key": source.s3_key if source else None,
92
+ "questions": db_set.questions
93
+ }
94
 
95
  except HTTPException:
96
  raise
 
110
  sets = db.query(db_models.QuizSet).filter(
111
  db_models.QuizSet.user_id == current_user.id
112
  ).order_by(db_models.QuizSet.created_at.desc()).all()
113
+ return [
114
+ {
115
+ "id": s.id,
116
+ "title": s.title,
117
+ "difficulty": s.difficulty,
118
+ "created_at": s.created_at,
119
+ "parent_file_id": s.source_id,
120
+ "parent_file_key": s.source.s3_key if s.source else None,
121
+ "questions": s.questions
122
+ }
123
+ for s in sets
124
+ ]
125
  except Exception as e:
126
  raise HTTPException(status_code=500, detail=str(e))
127
 
 
142
  if not db_set:
143
  raise HTTPException(status_code=404, detail="Quiz set not found")
144
 
145
+ return {
146
+ "id": db_set.id,
147
+ "title": db_set.title,
148
+ "difficulty": db_set.difficulty,
149
+ "created_at": db_set.created_at,
150
+ "parent_file_id": db_set.source_id,
151
+ "parent_file_key": db_set.source.s3_key if db_set.source else None,
152
+ "questions": db_set.questions
153
+ }
154
 
155
  @router.delete("/set/{set_id}")
156
  async def delete_quiz_set(
api/reports.py CHANGED
@@ -87,7 +87,11 @@ async def generate_report(
87
  db.commit()
88
  db.refresh(db_report)
89
 
90
- return db_report
 
 
 
 
91
 
92
  except HTTPException:
93
  raise
@@ -107,7 +111,18 @@ async def list_reports(
107
  reports = db.query(db_models.Report).filter(
108
  db_models.Report.user_id == current_user.id
109
  ).order_by(db_models.Report.created_at.desc()).all()
110
- return reports
 
 
 
 
 
 
 
 
 
 
 
111
  except Exception as e:
112
  raise HTTPException(status_code=500, detail=str(e))
113
 
@@ -128,7 +143,15 @@ async def get_report(
128
  if not report:
129
  raise HTTPException(status_code=404, detail="Report not found")
130
 
131
- return report
 
 
 
 
 
 
 
 
132
 
133
  @router.delete("/{report_id}")
134
  async def delete_report(
 
87
  db.commit()
88
  db.refresh(db_report)
89
 
90
+ return {
91
+ **db_report.__dict__,
92
+ "parent_file_id": db_report.source_id,
93
+ "parent_file_key": source.s3_key if source else None
94
+ }
95
 
96
  except HTTPException:
97
  raise
 
111
  reports = db.query(db_models.Report).filter(
112
  db_models.Report.user_id == current_user.id
113
  ).order_by(db_models.Report.created_at.desc()).all()
114
+ return [
115
+ {
116
+ "id": r.id,
117
+ "title": r.title,
118
+ "content": r.content,
119
+ "format_key": r.format_key,
120
+ "parent_file_id": r.source_id,
121
+ "parent_file_key": r.source.s3_key if r.source else None,
122
+ "created_at": r.created_at
123
+ }
124
+ for r in reports
125
+ ]
126
  except Exception as e:
127
  raise HTTPException(status_code=500, detail=str(e))
128
 
 
143
  if not report:
144
  raise HTTPException(status_code=404, detail="Report not found")
145
 
146
+ return {
147
+ "id": report.id,
148
+ "title": report.title,
149
+ "content": report.content,
150
+ "format_key": report.format_key,
151
+ "parent_file_id": report.source_id,
152
+ "parent_file_key": report.source.s3_key if report.source else None,
153
+ "created_at": report.created_at
154
+ }
155
 
156
  @router.delete("/{report_id}")
157
  async def delete_report(
api/video_generator.py CHANGED
@@ -67,6 +67,8 @@ async def generate_video_summary(
67
  "s3_key": db_summary.s3_key,
68
  "public_url": db_summary.s3_url,
69
  "private_url": s3_service.get_presigned_url(db_summary.s3_key),
 
 
70
  "created_at": db_summary.created_at
71
  }
72
 
@@ -94,6 +96,8 @@ async def list_video_summaries(
94
  "s3_key": s.s3_key,
95
  "public_url": s.s3_url,
96
  "private_url": s3_service.get_presigned_url(s.s3_key),
 
 
97
  "created_at": s.created_at
98
  }
99
  for s in summaries
 
67
  "s3_key": db_summary.s3_key,
68
  "public_url": db_summary.s3_url,
69
  "private_url": s3_service.get_presigned_url(db_summary.s3_key),
70
+ "parent_file_id": db_summary.source_id,
71
+ "parent_file_key": db_summary.source.s3_key if db_summary.source else None,
72
  "created_at": db_summary.created_at
73
  }
74
 
 
96
  "s3_key": s.s3_key,
97
  "public_url": s.s3_url,
98
  "private_url": s3_service.get_presigned_url(s.s3_key),
99
+ "parent_file_id": s.source_id,
100
+ "parent_file_key": s.source.s3_key if s.source else None,
101
  "created_at": s.created_at
102
  }
103
  for s in summaries
models/db_models.py CHANGED
@@ -34,6 +34,13 @@ class Source(Base):
34
  created_at = Column(DateTime(timezone=True), server_default=func.now())
35
 
36
  owner = relationship("User", back_populates="sources")
 
 
 
 
 
 
 
37
 
38
  class Podcast(Base):
39
  __tablename__ = "podcasts"
@@ -44,9 +51,11 @@ class Podcast(Base):
44
  s3_url = Column(String(1024), nullable=False)
45
  script = Column(UnicodeText)
46
  user_id = Column(Integer, ForeignKey("users.id"))
 
47
  created_at = Column(DateTime(timezone=True), server_default=func.now())
48
 
49
  owner = relationship("User", back_populates="podcasts")
 
50
 
51
  class FlashcardSet(Base):
52
  __tablename__ = "flashcard_sets"
@@ -59,6 +68,7 @@ class FlashcardSet(Base):
59
  created_at = Column(DateTime(timezone=True), server_default=func.now())
60
 
61
  owner = relationship("User", back_populates="flashcard_sets")
 
62
  flashcards = relationship("Flashcard", back_populates="flashcard_set", cascade="all, delete-orphan")
63
 
64
  class MindMap(Base):
@@ -72,6 +82,7 @@ class MindMap(Base):
72
  created_at = Column(DateTime(timezone=True), server_default=func.now())
73
 
74
  owner = relationship("User", back_populates="mind_maps")
 
75
 
76
  class QuizSet(Base):
77
  __tablename__ = "quiz_sets"
@@ -84,6 +95,7 @@ class QuizSet(Base):
84
  created_at = Column(DateTime(timezone=True), server_default=func.now())
85
 
86
  owner = relationship("User", back_populates="quiz_sets")
 
87
  questions = relationship("QuizQuestion", back_populates="quiz_set", cascade="all, delete-orphan")
88
 
89
  class QuizQuestion(Base):
@@ -111,6 +123,7 @@ class Report(Base):
111
  created_at = Column(DateTime(timezone=True), server_default=func.now())
112
 
113
  owner = relationship("User", back_populates="reports")
 
114
 
115
  class VideoSummary(Base):
116
  __tablename__ = "video_summaries"
@@ -124,6 +137,7 @@ class VideoSummary(Base):
124
  created_at = Column(DateTime(timezone=True), server_default=func.now())
125
 
126
  owner = relationship("User", back_populates="video_summaries")
 
127
 
128
  class Flashcard(Base):
129
  __tablename__ = "flashcards"
@@ -147,6 +161,7 @@ class RAGDocument(Base):
147
  created_at = Column(DateTime(timezone=True), server_default=func.now())
148
 
149
  owner = relationship("User", back_populates="rag_documents")
 
150
 
151
  class ChatMessage(Base):
152
  __tablename__ = "chat_messages"
 
34
  created_at = Column(DateTime(timezone=True), server_default=func.now())
35
 
36
  owner = relationship("User", back_populates="sources")
37
+ podcasts = relationship("Podcast", back_populates="source")
38
+ flashcard_sets = relationship("FlashcardSet", back_populates="source")
39
+ mind_maps = relationship("MindMap", back_populates="source")
40
+ quiz_sets = relationship("QuizSet", back_populates="source")
41
+ reports = relationship("Report", back_populates="source")
42
+ video_summaries = relationship("VideoSummary", back_populates="source")
43
+ rag_documents = relationship("RAGDocument", back_populates="source")
44
 
45
  class Podcast(Base):
46
  __tablename__ = "podcasts"
 
51
  s3_url = Column(String(1024), nullable=False)
52
  script = Column(UnicodeText)
53
  user_id = Column(Integer, ForeignKey("users.id"))
54
+ source_id = Column(Integer, ForeignKey("sources.id"), nullable=True)
55
  created_at = Column(DateTime(timezone=True), server_default=func.now())
56
 
57
  owner = relationship("User", back_populates="podcasts")
58
+ source = relationship("Source", back_populates="podcasts")
59
 
60
  class FlashcardSet(Base):
61
  __tablename__ = "flashcard_sets"
 
68
  created_at = Column(DateTime(timezone=True), server_default=func.now())
69
 
70
  owner = relationship("User", back_populates="flashcard_sets")
71
+ source = relationship("Source", back_populates="flashcard_sets")
72
  flashcards = relationship("Flashcard", back_populates="flashcard_set", cascade="all, delete-orphan")
73
 
74
  class MindMap(Base):
 
82
  created_at = Column(DateTime(timezone=True), server_default=func.now())
83
 
84
  owner = relationship("User", back_populates="mind_maps")
85
+ source = relationship("Source", back_populates="mind_maps")
86
 
87
  class QuizSet(Base):
88
  __tablename__ = "quiz_sets"
 
95
  created_at = Column(DateTime(timezone=True), server_default=func.now())
96
 
97
  owner = relationship("User", back_populates="quiz_sets")
98
+ source = relationship("Source", back_populates="quiz_sets")
99
  questions = relationship("QuizQuestion", back_populates="quiz_set", cascade="all, delete-orphan")
100
 
101
  class QuizQuestion(Base):
 
123
  created_at = Column(DateTime(timezone=True), server_default=func.now())
124
 
125
  owner = relationship("User", back_populates="reports")
126
+ source = relationship("Source", back_populates="reports")
127
 
128
  class VideoSummary(Base):
129
  __tablename__ = "video_summaries"
 
137
  created_at = Column(DateTime(timezone=True), server_default=func.now())
138
 
139
  owner = relationship("User", back_populates="video_summaries")
140
+ source = relationship("Source", back_populates="video_summaries")
141
 
142
  class Flashcard(Base):
143
  __tablename__ = "flashcards"
 
161
  created_at = Column(DateTime(timezone=True), server_default=func.now())
162
 
163
  owner = relationship("User", back_populates="rag_documents")
164
+ source = relationship("Source", back_populates="rag_documents")
165
 
166
  class ChatMessage(Base):
167
  __tablename__ = "chat_messages"
models/schemas.py CHANGED
@@ -84,6 +84,8 @@ class FlashcardSetResponse(BaseModel):
84
  title: Optional[str]
85
  difficulty: str
86
  created_at: datetime
 
 
87
  flashcards: List[FlashcardResponse]
88
 
89
  class Config:
@@ -96,8 +98,12 @@ class MindMapGenerateRequest(BaseModel):
96
  title: Optional[str] = None
97
 
98
  class MindMapResponse(BaseModel):
 
99
  title: str
100
  mermaid_code: str
 
 
 
101
  message: str
102
 
103
  # Quiz Schemas
@@ -122,6 +128,8 @@ class QuizSetResponse(BaseModel):
122
  title: Optional[str]
123
  difficulty: str
124
  created_at: datetime
 
 
125
  questions: List[QuizQuestionResponse]
126
 
127
  class Config:
@@ -148,6 +156,8 @@ class ReportResponse(BaseModel):
148
  title: str
149
  content: str
150
  format_key: str
 
 
151
  created_at: datetime
152
 
153
  class Config:
@@ -167,6 +177,8 @@ class VideoSummaryResponse(BaseModel):
167
  s3_key: str
168
  public_url: str
169
  private_url: Optional[str] = None
 
 
170
  created_at: datetime
171
 
172
  class Config:
 
84
  title: Optional[str]
85
  difficulty: str
86
  created_at: datetime
87
+ parent_file_id: Optional[int] = None
88
+ parent_file_key: Optional[str] = None
89
  flashcards: List[FlashcardResponse]
90
 
91
  class Config:
 
98
  title: Optional[str] = None
99
 
100
  class MindMapResponse(BaseModel):
101
+ id: Optional[int] = None
102
  title: str
103
  mermaid_code: str
104
+ parent_file_id: Optional[int] = None
105
+ parent_file_key: Optional[str] = None
106
+ created_at: Optional[datetime] = None
107
  message: str
108
 
109
  # Quiz Schemas
 
128
  title: Optional[str]
129
  difficulty: str
130
  created_at: datetime
131
+ parent_file_id: Optional[int] = None
132
+ parent_file_key: Optional[str] = None
133
  questions: List[QuizQuestionResponse]
134
 
135
  class Config:
 
156
  title: str
157
  content: str
158
  format_key: str
159
+ parent_file_id: Optional[int] = None
160
+ parent_file_key: Optional[str] = None
161
  created_at: datetime
162
 
163
  class Config:
 
177
  s3_key: str
178
  public_url: str
179
  private_url: Optional[str] = None
180
+ parent_file_id: Optional[int] = None
181
+ parent_file_key: Optional[str] = None
182
  created_at: datetime
183
 
184
  class Config:
scripts/migrate_all.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+
4
+ # Add the project root to the python path
5
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
6
+
7
+ from sqlalchemy import text
8
+ from core.database import engine
9
+
10
+ def migrate():
11
+ print("Starting migration: Ensuring source_id exists in all output tables...")
12
+
13
+ tables = [
14
+ "podcasts",
15
+ "video_summaries",
16
+ "mind_maps",
17
+ "flashcard_sets",
18
+ "quiz_sets",
19
+ "reports"
20
+ ]
21
+
22
+ try:
23
+ with engine.connect() as connection:
24
+ for table in tables:
25
+ print(f"Processing table: {table}...")
26
+
27
+ # Check and add column/constraint
28
+ sql = f"""
29
+ IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[{table}]') AND name = 'source_id')
30
+ BEGIN
31
+ ALTER TABLE [dbo].[{table}] ADD [source_id] INT NULL;
32
+ ALTER TABLE [dbo].[{table}] ADD CONSTRAINT [FK_{table}_sources]
33
+ FOREIGN KEY ([source_id]) REFERENCES [dbo].[sources] ([id]);
34
+ END
35
+ """
36
+ connection.execute(text(sql))
37
+
38
+ connection.commit()
39
+ print("Migration completed successfully!")
40
+ except Exception as e:
41
+ print(f"Error during migration: {e}")
42
+
43
+ if __name__ == "__main__":
44
+ migrate()
services/flashcard_service.py CHANGED
@@ -26,20 +26,18 @@ class FlashcardService:
26
  language: str = "English"
27
  ) -> List[Dict[str, str]]:
28
  """
29
- Generates flashcards from either an S3 PDF or direct text input.
30
  """
31
  try:
32
  system_prompt = get_flashcard_system_prompt(difficulty, quantity, language)
33
  if topic:
34
  system_prompt += get_flashcard_topic_prompt(topic)
35
 
36
- content_to_analyze = ""
37
-
38
  if file_key:
39
  # Download PDF from S3
40
  tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
41
  tmp_path = tmp.name
42
- tmp.close() # Close handle so other processes can access on Windows
43
 
44
  try:
45
  s3_service.s3_client.download_file(
@@ -68,14 +66,13 @@ class FlashcardService:
68
  ]
69
 
70
  response = self.openai_client.chat.completions.create(
71
- model="gpt-4o-mini", # Using 4o-mini for efficiency
72
  messages=messages,
73
  temperature=0.7
74
  )
75
 
76
  # Clean up OpenAI file
77
  self.openai_client.files.delete(uploaded_file.id)
78
-
79
  raw_content = response.choices[0].message.content
80
 
81
  finally:
@@ -98,18 +95,16 @@ class FlashcardService:
98
  raise ValueError("Either file_key or text_input must be provided")
99
 
100
  # Parse JSON
101
- # Remove markdown code blocks if present
102
  if "```json" in raw_content:
103
  raw_content = raw_content.split("```json")[1].split("```")[0].strip()
104
  elif "```" in raw_content:
105
  raw_content = raw_content.split("```")[1].split("```")[0].strip()
106
 
107
- flashcards = json.loads(raw_content)
108
- return flashcards
109
 
110
  except Exception as e:
111
- logger.error(f"Flashcard generation failed: {e}")
112
- raise
113
 
114
  async def generate_explanation(self, question: str, file_key: Optional[str] = None, language: str = "English") -> str:
115
  """
@@ -119,7 +114,6 @@ class FlashcardService:
119
  explanation_prompt = get_flashcard_explanation_prompt(question, language)
120
 
121
  if file_key:
122
- # Similar logic to generation if PDF context is needed
123
  tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
124
  tmp_path = tmp.name
125
  tmp.close()
@@ -158,7 +152,7 @@ class FlashcardService:
158
  return response.choices[0].message.content
159
 
160
  except Exception as e:
161
- logger.error(f"Explanation generation failed: {e}")
162
- raise
163
 
164
  flashcard_service = FlashcardService()
 
26
  language: str = "English"
27
  ) -> List[Dict[str, str]]:
28
  """
29
+ Generates flashcards from either an S3 PDF or direct text input (Original File-ID Method).
30
  """
31
  try:
32
  system_prompt = get_flashcard_system_prompt(difficulty, quantity, language)
33
  if topic:
34
  system_prompt += get_flashcard_topic_prompt(topic)
35
 
 
 
36
  if file_key:
37
  # Download PDF from S3
38
  tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
39
  tmp_path = tmp.name
40
+ tmp.close()
41
 
42
  try:
43
  s3_service.s3_client.download_file(
 
66
  ]
67
 
68
  response = self.openai_client.chat.completions.create(
69
+ model="gpt-4o-mini",
70
  messages=messages,
71
  temperature=0.7
72
  )
73
 
74
  # Clean up OpenAI file
75
  self.openai_client.files.delete(uploaded_file.id)
 
76
  raw_content = response.choices[0].message.content
77
 
78
  finally:
 
95
  raise ValueError("Either file_key or text_input must be provided")
96
 
97
  # Parse JSON
 
98
  if "```json" in raw_content:
99
  raw_content = raw_content.split("```json")[1].split("```")[0].strip()
100
  elif "```" in raw_content:
101
  raw_content = raw_content.split("```")[1].split("```")[0].strip()
102
 
103
+ return json.loads(raw_content)
 
104
 
105
  except Exception as e:
106
+ logger.error(f"Flashcard generation failed: {str(e)}")
107
+ raise e
108
 
109
  async def generate_explanation(self, question: str, file_key: Optional[str] = None, language: str = "English") -> str:
110
  """
 
114
  explanation_prompt = get_flashcard_explanation_prompt(question, language)
115
 
116
  if file_key:
 
117
  tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
118
  tmp_path = tmp.name
119
  tmp.close()
 
152
  return response.choices[0].message.content
153
 
154
  except Exception as e:
155
+ logger.error(f"Explanation generation failed: {str(e)}")
156
+ raise e
157
 
158
  flashcard_service = FlashcardService()