openfree commited on
Commit
7da37b6
ยท
verified ยท
1 Parent(s): f33dcf6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -60
app.py CHANGED
@@ -732,16 +732,17 @@ class NovelDatabase:
732
 
733
  char_latest_states = {}
734
  for row in cursor.fetchall():
735
- char_name = row['character_name']
736
- if char_name not in char_latest_states or row['chapter'] > char_latest_states[char_name].last_seen_chapter:
 
737
  char_state = CharacterState(
738
  name=char_name,
739
- alive=bool(row['is_alive']),
740
- location=row['location'] or "",
741
- injuries=json.loads(row['injuries']) if row['injuries'] else [],
742
- emotional_state=row['emotional_state'] or "",
743
- relationships=json.loads(row['relationships']) if row['relationships'] else {},
744
- last_seen_chapter=row['chapter']
745
  )
746
  char_latest_states[char_name] = char_state
747
 
@@ -755,12 +756,13 @@ class NovelDatabase:
755
  ''', (session_id,))
756
 
757
  for row in cursor.fetchall():
 
758
  plot_point = PlotPoint(
759
- chapter=row['chapter'],
760
- event_type=row['event_type'],
761
- description=row['description'],
762
- characters_involved=json.loads(row['characters_involved']) if row['characters_involved'] else [],
763
- impact_level=row['impact_level']
764
  )
765
  tracker.plot_points.append(plot_point)
766
 
@@ -980,6 +982,13 @@ class NovelDatabase:
980
  return datetime.fromisoformat(datetime_str)
981
  except:
982
  return datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
 
 
 
 
 
 
 
983
 
984
 
985
  class NovelWritingSystem:
@@ -2187,9 +2196,10 @@ Five years ago, her younger sister Seojin had died in a car accident. The memory
2187
  self.current_session_id = session_id
2188
  session = NovelDatabase.get_session(session_id)
2189
  if session:
2190
- query = session['user_query']
2191
- language = session['language']
2192
- resume_from_stage = session['current_stage'] + 1
 
2193
  # Load existing state tracker
2194
  self.state_tracker = NovelDatabase.load_session_state_tracker(session_id)
2195
  logger.info(f"Resuming session {session_id} from stage {resume_from_stage}")
@@ -2213,11 +2223,13 @@ Five years ago, her younger sister Seojin had died in a car accident. The memory
2213
  if resume_from_stage > 0:
2214
  existing_stages = NovelDatabase.get_stages(self.current_session_id)
2215
  for stage_data in existing_stages:
 
 
2216
  stages.append({
2217
- "name": stage_data['stage_name'],
2218
- "status": stage_data['status'],
2219
- "content": stage_data['content'] or "",
2220
- "quality_score": stage_data.get('quality_score', 0.0)
2221
  })
2222
 
2223
  # Define all stages for 10 writers
@@ -2601,11 +2613,12 @@ def get_active_sessions(language: str) -> List[Tuple[str, str]]:
2601
 
2602
  choices = []
2603
  for session in sessions:
2604
- created = NovelDatabase.parse_datetime(session['created_at'])
 
2605
  date_str = created.strftime("%Y-%m-%d %H:%M")
2606
- query_preview = session['user_query'][:50] + "..." if len(session['user_query']) > 50 else session['user_query']
2607
- label = f"[{date_str}] {query_preview} (Stage {session['current_stage']})"
2608
- choices.append((label, session['session_id']))
2609
 
2610
  return choices
2611
  except Exception as e:
@@ -2631,11 +2644,12 @@ def auto_recover_session(language: str) -> Tuple[str, str]:
2631
  try:
2632
  latest_session = NovelDatabase.get_latest_active_session()
2633
  if latest_session:
 
2634
  if language == "Korean":
2635
- message = f"โœ… ์ž๋™ ๋ณต๊ตฌ: '{latest_session['user_query'][:30]}...' (Stage {latest_session['current_stage']})"
2636
  else:
2637
- message = f"โœ… Auto recovered: '{latest_session['user_query'][:30]}...' (Stage {latest_session['current_stage']})"
2638
- return latest_session['session_id'], message
2639
  else:
2640
  return None, ""
2641
  except Exception as e:
@@ -2691,8 +2705,11 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2691
  logger.error(f"Session not found: {session_id}")
2692
  return None
2693
 
 
 
 
2694
  # ์„ธ์…˜์˜ ์‹ค์ œ ์–ธ์–ด ์‚ฌ์šฉ
2695
- actual_language = session['language']
2696
  logger.info(f"Using session language: {actual_language}")
2697
 
2698
  # DB์—์„œ ๋ชจ๋“  ์Šคํ…Œ์ด์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
@@ -2700,13 +2717,13 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2700
  logger.info(f"Found {len(stages)} stages in database")
2701
 
2702
  # ํ’ˆ์งˆ ์ ์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
2703
- quality_scores = json.loads(session.get('quality_scores', '{}')) if session.get('quality_scores') else {}
2704
 
2705
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ๊ฐ์ง€
2706
  is_test_mode = False
2707
- has_writer10 = any(stage['role'] == 'writer10' for stage in stages if 'role' in stage.keys())
2708
- has_writer1 = any(stage['role'] == 'writer1' for stage in stages if 'role' in stage.keys())
2709
- has_writer3 = any(stage['role'] == 'writer3' for stage in stages if 'role' in stage.keys())
2710
 
2711
  if has_writer10 and has_writer1 and not has_writer3:
2712
  is_test_mode = True
@@ -2734,7 +2751,7 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2734
 
2735
  theme_para = doc.add_paragraph()
2736
  theme_label = '์ฃผ์ œ: ' if actual_language == 'Korean' else 'Theme: '
2737
- theme_run = theme_para.add_run(f'{theme_label}{session["user_query"]}')
2738
  theme_run.font.size = Pt(12)
2739
  theme_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
2740
 
@@ -2770,9 +2787,10 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2770
  if is_test_mode:
2771
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ: writer1 revision + writer10 ๋‚ด์šฉ ์ฒ˜๋ฆฌ
2772
  for stage in stages:
2773
- role = stage['role'] if 'role' in stage.keys() else None
2774
- stage_name = stage['stage_name'] if 'stage_name' in stage.keys() else ''
2775
- content = stage['content'] if 'content' in stage.keys() else ''
 
2776
 
2777
  # Writer 1 ์ˆ˜์ •๋ณธ
2778
  if role == 'writer1' and stage_name and ('Revision' in stage_name or '์ˆ˜์ •๋ณธ' in stage_name):
@@ -2781,13 +2799,13 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2781
  content = content.strip()
2782
 
2783
  if content:
2784
- word_count = stage['word_count'] if 'word_count' in stage.keys() else len(content.split())
2785
  total_words += word_count
2786
  writer_contents.append({
2787
  'writer_num': 1,
2788
  'content': content,
2789
  'word_count': word_count,
2790
- 'quality_score': stage.get('quality_score', 0)
2791
  })
2792
  writer_count = 1
2793
  logger.info(f"Added writer 1 (Chapter 1): {word_count} words")
@@ -2811,7 +2829,7 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2811
  'writer_num': chapter_num,
2812
  'content': chapter_content,
2813
  'word_count': word_count,
2814
- 'quality_score': stage.get('quality_score', 0)
2815
  })
2816
  writer_count = max(writer_count, chapter_num)
2817
  logger.info(f"Added Chapter {chapter_num}: {word_count} words")
@@ -2820,9 +2838,10 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2820
  logger.info("[NORMAL MODE] Processing all writer revisions...")
2821
 
2822
  for stage in stages:
2823
- role = stage['role'] if 'role' in stage.keys() else None
2824
- stage_name = stage['stage_name'] if 'stage_name' in stage.keys() else ''
2825
- content = stage['content'] if 'content' in stage.keys() else ''
 
2826
 
2827
  # ์ž‘๊ฐ€ ์ˆ˜์ •๋ณธ ์ฐพ๊ธฐ
2828
  is_writer = role and role.startswith('writer')
@@ -2841,13 +2860,13 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2841
  content = content.strip()
2842
 
2843
  if content:
2844
- word_count = stage['word_count'] if 'word_count' in stage.keys() else len(content.split())
2845
  total_words += word_count
2846
  writer_contents.append({
2847
  'writer_num': writer_num,
2848
  'content': content,
2849
  'word_count': word_count,
2850
- 'quality_score': stage.get('quality_score', 0)
2851
  })
2852
  writer_count += 1
2853
  logger.info(f"Added writer {writer_num}: {word_count} words")
@@ -2967,7 +2986,7 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2967
 
2968
  # Save
2969
  temp_dir = tempfile.gettempdir()
2970
- safe_filename = re.sub(r'[^\w\s-]', '', session['user_query'][:30]).strip()
2971
  mode_suffix = "_TestMode" if is_test_mode else "_Complete"
2972
  filename = f"Novel{mode_suffix}_{safe_filename}_{timestamp}.docx"
2973
  filepath = os.path.join(temp_dir, filename)
@@ -2978,7 +2997,7 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2978
  else:
2979
  # TXT format
2980
  temp_dir = tempfile.gettempdir()
2981
- safe_filename = re.sub(r'[^\w\s-]', '', session['user_query'][:30]).strip()
2982
  mode_suffix = "_TestMode" if is_test_mode else "_Complete"
2983
  filename = f"Novel{mode_suffix}_{safe_filename}_{timestamp}.txt"
2984
  filepath = os.path.join(temp_dir, filename)
@@ -2992,14 +3011,14 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
2992
  f.write("="*60 + "\n")
2993
 
2994
  if actual_language == 'Korean':
2995
- f.write(f"์ฃผ์ œ: {session['user_query']}\n")
2996
  f.write(f"์–ธ์–ด: ํ•œ๊ตญ์–ด\n")
2997
  f.write(f"์ƒ์„ฑ์ผ: {datetime.now()}\n")
2998
  f.write(f"๋ชจ๋“œ: {'ํ…Œ์ŠคํŠธ ๋ชจ๋“œ (์‹ค์ œ ์ž‘์„ฑ๋œ ์ฑ•ํ„ฐ)' if is_test_mode else '์ „์ฒด ๋ชจ๋“œ (10๊ฐœ ์ฑ•ํ„ฐ)'}\n")
2999
  if quality_scores:
3000
  f.write(f"ํ’ˆ์งˆ ์ ์ˆ˜: {quality_scores.get('overall', 0):.1f}/10\n")
3001
  else:
3002
- f.write(f"Theme: {session['user_query']}\n")
3003
  f.write(f"Language: English\n")
3004
  f.write(f"Created: {datetime.now()}\n")
3005
  f.write(f"Mode: {'Test Mode (Actual chapters)' if is_test_mode else 'Full Mode (10 chapters)'}\n")
@@ -3016,9 +3035,10 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
3016
  if is_test_mode:
3017
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ์ฒ˜๋ฆฌ (DOCX์™€ ๋™์ผ)
3018
  for stage in stages:
3019
- role = stage['role'] if 'role' in stage.keys() else None
3020
- stage_name = stage['stage_name'] if 'stage_name' in stage.keys() else ''
3021
- content = stage['content'] if 'content' in stage.keys() else ''
 
3022
 
3023
  if role == 'writer1' and stage_name and ('Revision' in stage_name or '์ˆ˜์ •๋ณธ' in stage_name):
3024
  content = re.sub(r'\[(?:ํŽ˜์ด์ง€|Page|page)\s*\d+\]', '', content)
@@ -3026,7 +3046,7 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
3026
  content = content.strip()
3027
 
3028
  if content:
3029
- word_count = stage['word_count'] if 'word_count' in stage.keys() else len(content.split())
3030
  total_words += word_count
3031
  writer_count = 1
3032
 
@@ -3037,8 +3057,8 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
3037
  f.write(f"{chapter_title}\n")
3038
  word_count_label = f"๋‹จ์–ด ์ˆ˜: {word_count:,}" if actual_language == 'Korean' else f"Word Count: {word_count:,}"
3039
  f.write(f"{word_count_label}\n")
3040
- if stage.get('quality_score', 0) > 0:
3041
- quality_label = f"ํ’ˆ์งˆ: {stage['quality_score']:.1f}/10" if actual_language == 'Korean' else f"Quality: {stage['quality_score']:.1f}/10"
3042
  f.write(f"{quality_label}\n")
3043
  f.write(f"{'='*40}\n\n")
3044
  f.write(content)
@@ -3070,9 +3090,10 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
3070
  else:
3071
  # ์ผ๋ฐ˜ ๋ชจ๋“œ
3072
  for stage in stages:
3073
- role = stage['role'] if 'role' in stage.keys() else None
3074
- stage_name = stage['stage_name'] if 'stage_name' in stage.keys() else ''
3075
- content = stage['content'] if 'content' in stage.keys() else ''
 
3076
 
3077
  is_writer = role and role.startswith('writer')
3078
  is_revision = stage_name and ('Revision' in stage_name or '์ˆ˜์ •๋ณธ' in stage_name)
@@ -3088,7 +3109,7 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
3088
  content = content.strip()
3089
 
3090
  if content:
3091
- word_count = stage['word_count'] if 'word_count' in stage.keys() else len(content.split())
3092
  total_words += word_count
3093
  writer_count += 1
3094
 
@@ -3099,8 +3120,8 @@ def download_novel(novel_text: str, format: str, language: str, session_id: str
3099
  f.write(f"{chapter_title}\n")
3100
  word_count_label = f"๋‹จ์–ด ์ˆ˜: {word_count:,}" if actual_language == 'Korean' else f"Word Count: {word_count:,}"
3101
  f.write(f"{word_count_label}\n")
3102
- if stage.get('quality_score', 0) > 0:
3103
- quality_label = f"ํ’ˆ์งˆ: {stage['quality_score']:.1f}/10" if actual_language == 'Korean' else f"Quality: {stage['quality_score']:.1f}/10"
3104
  f.write(f"{quality_label}\n")
3105
  f.write(f"{'='*40}\n\n")
3106
  f.write(content)
 
732
 
733
  char_latest_states = {}
734
  for row in cursor.fetchall():
735
+ row_dict = dict(row) # Convert Row to dict
736
+ char_name = row_dict['character_name']
737
+ if char_name not in char_latest_states or row_dict['chapter'] > char_latest_states[char_name].last_seen_chapter:
738
  char_state = CharacterState(
739
  name=char_name,
740
+ alive=bool(row_dict['is_alive']),
741
+ location=row_dict['location'] or "",
742
+ injuries=json.loads(row_dict['injuries']) if row_dict['injuries'] else [],
743
+ emotional_state=row_dict['emotional_state'] or "",
744
+ relationships=json.loads(row_dict['relationships']) if row_dict['relationships'] else {},
745
+ last_seen_chapter=row_dict['chapter']
746
  )
747
  char_latest_states[char_name] = char_state
748
 
 
756
  ''', (session_id,))
757
 
758
  for row in cursor.fetchall():
759
+ row_dict = dict(row) # Convert Row to dict
760
  plot_point = PlotPoint(
761
+ chapter=row_dict['chapter'],
762
+ event_type=row_dict['event_type'],
763
+ description=row_dict['description'],
764
+ characters_involved=json.loads(row_dict['characters_involved']) if row_dict['characters_involved'] else [],
765
+ impact_level=row_dict['impact_level']
766
  )
767
  tracker.plot_points.append(plot_point)
768
 
 
982
  return datetime.fromisoformat(datetime_str)
983
  except:
984
  return datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
985
+
986
+ @staticmethod
987
+ def row_to_dict(row):
988
+ """Convert sqlite3.Row to dictionary"""
989
+ if row is None:
990
+ return None
991
+ return dict(row) if hasattr(row, 'keys') else row
992
 
993
 
994
  class NovelWritingSystem:
 
2196
  self.current_session_id = session_id
2197
  session = NovelDatabase.get_session(session_id)
2198
  if session:
2199
+ session_dict = dict(session) # Convert Row to dict
2200
+ query = session_dict['user_query']
2201
+ language = session_dict['language']
2202
+ resume_from_stage = session_dict['current_stage'] + 1
2203
  # Load existing state tracker
2204
  self.state_tracker = NovelDatabase.load_session_state_tracker(session_id)
2205
  logger.info(f"Resuming session {session_id} from stage {resume_from_stage}")
 
2223
  if resume_from_stage > 0:
2224
  existing_stages = NovelDatabase.get_stages(self.current_session_id)
2225
  for stage_data in existing_stages:
2226
+ # Convert sqlite3.Row to dict
2227
+ stage_dict = dict(stage_data)
2228
  stages.append({
2229
+ "name": stage_dict['stage_name'],
2230
+ "status": stage_dict['status'],
2231
+ "content": stage_dict['content'] or "",
2232
+ "quality_score": stage_dict.get('quality_score', 0.0)
2233
  })
2234
 
2235
  # Define all stages for 10 writers
 
2613
 
2614
  choices = []
2615
  for session in sessions:
2616
+ session_dict = dict(session) # Convert Row to dict
2617
+ created = NovelDatabase.parse_datetime(session_dict['created_at'])
2618
  date_str = created.strftime("%Y-%m-%d %H:%M")
2619
+ query_preview = session_dict['user_query'][:50] + "..." if len(session_dict['user_query']) > 50 else session_dict['user_query']
2620
+ label = f"[{date_str}] {query_preview} (Stage {session_dict['current_stage']})"
2621
+ choices.append((label, session_dict['session_id']))
2622
 
2623
  return choices
2624
  except Exception as e:
 
2644
  try:
2645
  latest_session = NovelDatabase.get_latest_active_session()
2646
  if latest_session:
2647
+ session_dict = dict(latest_session) # Convert Row to dict
2648
  if language == "Korean":
2649
+ message = f"โœ… ์ž๋™ ๋ณต๊ตฌ: '{session_dict['user_query'][:30]}...' (Stage {session_dict['current_stage']})"
2650
  else:
2651
+ message = f"โœ… Auto recovered: '{session_dict['user_query'][:30]}...' (Stage {session_dict['current_stage']})"
2652
+ return session_dict['session_id'], message
2653
  else:
2654
  return None, ""
2655
  except Exception as e:
 
2705
  logger.error(f"Session not found: {session_id}")
2706
  return None
2707
 
2708
+ # Convert sqlite3.Row to dict
2709
+ session_dict = dict(session) if session else {}
2710
+
2711
  # ์„ธ์…˜์˜ ์‹ค์ œ ์–ธ์–ด ์‚ฌ์šฉ
2712
+ actual_language = session_dict['language']
2713
  logger.info(f"Using session language: {actual_language}")
2714
 
2715
  # DB์—์„œ ๋ชจ๋“  ์Šคํ…Œ์ด์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
 
2717
  logger.info(f"Found {len(stages)} stages in database")
2718
 
2719
  # ํ’ˆ์งˆ ์ ์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
2720
+ quality_scores = json.loads(session_dict.get('quality_scores', '{}')) if session_dict.get('quality_scores') else {}
2721
 
2722
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ๊ฐ์ง€
2723
  is_test_mode = False
2724
+ has_writer10 = any(dict(stage).get('role') == 'writer10' for stage in stages)
2725
+ has_writer1 = any(dict(stage).get('role') == 'writer1' for stage in stages)
2726
+ has_writer3 = any(dict(stage).get('role') == 'writer3' for stage in stages)
2727
 
2728
  if has_writer10 and has_writer1 and not has_writer3:
2729
  is_test_mode = True
 
2751
 
2752
  theme_para = doc.add_paragraph()
2753
  theme_label = '์ฃผ์ œ: ' if actual_language == 'Korean' else 'Theme: '
2754
+ theme_run = theme_para.add_run(f'{theme_label}{session_dict["user_query"]}')
2755
  theme_run.font.size = Pt(12)
2756
  theme_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
2757
 
 
2787
  if is_test_mode:
2788
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ: writer1 revision + writer10 ๋‚ด์šฉ ์ฒ˜๋ฆฌ
2789
  for stage in stages:
2790
+ stage_dict = dict(stage) # Convert Row to dict
2791
+ role = stage_dict.get('role')
2792
+ stage_name = stage_dict.get('stage_name', '')
2793
+ content = stage_dict.get('content', '')
2794
 
2795
  # Writer 1 ์ˆ˜์ •๋ณธ
2796
  if role == 'writer1' and stage_name and ('Revision' in stage_name or '์ˆ˜์ •๋ณธ' in stage_name):
 
2799
  content = content.strip()
2800
 
2801
  if content:
2802
+ word_count = stage_dict.get('word_count', len(content.split()))
2803
  total_words += word_count
2804
  writer_contents.append({
2805
  'writer_num': 1,
2806
  'content': content,
2807
  'word_count': word_count,
2808
+ 'quality_score': stage_dict.get('quality_score', 0)
2809
  })
2810
  writer_count = 1
2811
  logger.info(f"Added writer 1 (Chapter 1): {word_count} words")
 
2829
  'writer_num': chapter_num,
2830
  'content': chapter_content,
2831
  'word_count': word_count,
2832
+ 'quality_score': stage_dict.get('quality_score', 0)
2833
  })
2834
  writer_count = max(writer_count, chapter_num)
2835
  logger.info(f"Added Chapter {chapter_num}: {word_count} words")
 
2838
  logger.info("[NORMAL MODE] Processing all writer revisions...")
2839
 
2840
  for stage in stages:
2841
+ stage_dict = dict(stage) # Convert Row to dict
2842
+ role = stage_dict.get('role')
2843
+ stage_name = stage_dict.get('stage_name', '')
2844
+ content = stage_dict.get('content', '')
2845
 
2846
  # ์ž‘๊ฐ€ ์ˆ˜์ •๋ณธ ์ฐพ๊ธฐ
2847
  is_writer = role and role.startswith('writer')
 
2860
  content = content.strip()
2861
 
2862
  if content:
2863
+ word_count = stage_dict.get('word_count', len(content.split()))
2864
  total_words += word_count
2865
  writer_contents.append({
2866
  'writer_num': writer_num,
2867
  'content': content,
2868
  'word_count': word_count,
2869
+ 'quality_score': stage_dict.get('quality_score', 0)
2870
  })
2871
  writer_count += 1
2872
  logger.info(f"Added writer {writer_num}: {word_count} words")
 
2986
 
2987
  # Save
2988
  temp_dir = tempfile.gettempdir()
2989
+ safe_filename = re.sub(r'[^\w\s-]', '', session_dict['user_query'][:30]).strip()
2990
  mode_suffix = "_TestMode" if is_test_mode else "_Complete"
2991
  filename = f"Novel{mode_suffix}_{safe_filename}_{timestamp}.docx"
2992
  filepath = os.path.join(temp_dir, filename)
 
2997
  else:
2998
  # TXT format
2999
  temp_dir = tempfile.gettempdir()
3000
+ safe_filename = re.sub(r'[^\w\s-]', '', session_dict['user_query'][:30]).strip()
3001
  mode_suffix = "_TestMode" if is_test_mode else "_Complete"
3002
  filename = f"Novel{mode_suffix}_{safe_filename}_{timestamp}.txt"
3003
  filepath = os.path.join(temp_dir, filename)
 
3011
  f.write("="*60 + "\n")
3012
 
3013
  if actual_language == 'Korean':
3014
+ f.write(f"์ฃผ์ œ: {session_dict['user_query']}\n")
3015
  f.write(f"์–ธ์–ด: ํ•œ๊ตญ์–ด\n")
3016
  f.write(f"์ƒ์„ฑ์ผ: {datetime.now()}\n")
3017
  f.write(f"๋ชจ๋“œ: {'ํ…Œ์ŠคํŠธ ๋ชจ๋“œ (์‹ค์ œ ์ž‘์„ฑ๋œ ์ฑ•ํ„ฐ)' if is_test_mode else '์ „์ฒด ๋ชจ๋“œ (10๊ฐœ ์ฑ•ํ„ฐ)'}\n")
3018
  if quality_scores:
3019
  f.write(f"ํ’ˆ์งˆ ์ ์ˆ˜: {quality_scores.get('overall', 0):.1f}/10\n")
3020
  else:
3021
+ f.write(f"Theme: {session_dict['user_query']}\n")
3022
  f.write(f"Language: English\n")
3023
  f.write(f"Created: {datetime.now()}\n")
3024
  f.write(f"Mode: {'Test Mode (Actual chapters)' if is_test_mode else 'Full Mode (10 chapters)'}\n")
 
3035
  if is_test_mode:
3036
  # ํ…Œ์ŠคํŠธ ๋ชจ๋“œ ์ฒ˜๋ฆฌ (DOCX์™€ ๋™์ผ)
3037
  for stage in stages:
3038
+ stage_dict = dict(stage) # Convert Row to dict
3039
+ role = stage_dict.get('role')
3040
+ stage_name = stage_dict.get('stage_name', '')
3041
+ content = stage_dict.get('content', '')
3042
 
3043
  if role == 'writer1' and stage_name and ('Revision' in stage_name or '์ˆ˜์ •๋ณธ' in stage_name):
3044
  content = re.sub(r'\[(?:ํŽ˜์ด์ง€|Page|page)\s*\d+\]', '', content)
 
3046
  content = content.strip()
3047
 
3048
  if content:
3049
+ word_count = stage_dict.get('word_count', len(content.split()))
3050
  total_words += word_count
3051
  writer_count = 1
3052
 
 
3057
  f.write(f"{chapter_title}\n")
3058
  word_count_label = f"๋‹จ์–ด ์ˆ˜: {word_count:,}" if actual_language == 'Korean' else f"Word Count: {word_count:,}"
3059
  f.write(f"{word_count_label}\n")
3060
+ if stage_dict.get('quality_score', 0) > 0:
3061
+ quality_label = f"ํ’ˆ์งˆ: {stage_dict['quality_score']:.1f}/10" if actual_language == 'Korean' else f"Quality: {stage_dict['quality_score']:.1f}/10"
3062
  f.write(f"{quality_label}\n")
3063
  f.write(f"{'='*40}\n\n")
3064
  f.write(content)
 
3090
  else:
3091
  # ์ผ๋ฐ˜ ๋ชจ๋“œ
3092
  for stage in stages:
3093
+ stage_dict = dict(stage) # Convert Row to dict
3094
+ role = stage_dict.get('role')
3095
+ stage_name = stage_dict.get('stage_name', '')
3096
+ content = stage_dict.get('content', '')
3097
 
3098
  is_writer = role and role.startswith('writer')
3099
  is_revision = stage_name and ('Revision' in stage_name or '์ˆ˜์ •๋ณธ' in stage_name)
 
3109
  content = content.strip()
3110
 
3111
  if content:
3112
+ word_count = stage_dict.get('word_count', len(content.split()))
3113
  total_words += word_count
3114
  writer_count += 1
3115
 
 
3120
  f.write(f"{chapter_title}\n")
3121
  word_count_label = f"๋‹จ์–ด ์ˆ˜: {word_count:,}" if actual_language == 'Korean' else f"Word Count: {word_count:,}"
3122
  f.write(f"{word_count_label}\n")
3123
+ if stage_dict.get('quality_score', 0) > 0:
3124
+ quality_label = f"ํ’ˆ์งˆ: {stage_dict['quality_score']:.1f}/10" if actual_language == 'Korean' else f"Quality: {stage_dict['quality_score']:.1f}/10"
3125
  f.write(f"{quality_label}\n")
3126
  f.write(f"{'='*40}\n\n")
3127
  f.write(content)