ll7098ll commited on
Commit
569cd00
·
verified ·
1 Parent(s): a35c585

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +23 -18
app.py CHANGED
@@ -60,6 +60,11 @@ ALL_TEXTS = {
60
  "game_title": "🎙️ 진실을 찾아서: 우리 역사 이야기",
61
  "scenario_select_title": "📰 어떤 시대로 가볼까요?",
62
  "scenario_select_button": "이 시대로 출발!",
 
 
 
 
 
63
  "dashboard_title": "📊 기자님 현재 상황",
64
  "dashboard_term": "{year}년 {turn}번째 날",
65
  "term_press_freedom": "기사 쓰는 자유",
@@ -107,7 +112,7 @@ ALL_TEXTS = {
107
  "error_openai_api": "AI 편집장님 연결에 문제가 생겼어요: {error}",
108
 
109
  # --- 4.19 혁명 취재 시나리오 ---
110
- "scenario_419_revolution_name": "4.19 혁명 이야기 (1960년)",
111
  # Turn 1 (3.15 부정선거)
112
  "event_419_t1_assignment": "오늘은 3월 15일, 대통령과 부통령을 뽑는 날이에요. 투표하는 곳에서 나쁜 사람들이 규칙을 어기고 자기편만 뽑으려고 할지도 몰라요. 그런 이상한 점을 찾아내고, 투표를 지켜보는 사람, 투표하는 사람, 선거를 관리하는 사람들에게 이야기를 들어보는 것이 오늘 할 일이에요. 특히 반대편 사람들이 잘 감시하는지, 힘 있는 쪽 사람들이 몰래 나쁜 일을 꾸미는지 잘 살펴보세요.",
113
  "event_419_t1_source": "[알아두면 좋아요] 지금 자유당이라는 당이 오랫동안 나라를 다스려서 사람들이 불만이 많아요. 이번 선거에서 이승만 대통령이 또 대통령이 되고, 이기붕 아저씨가 부통령이 되려고 자유당이 나쁜 방법을 쓸 거라는 소문이 많아요. 미리 투표한 표를 몰래 바꾸거나, 없는 사람 표를 넣거나, 여럿이 같이 보면서 투표하게 만들거나, 반대편 감시하는 사람들을 쫓아낸다는 이야기가 돌고 있어요.",
@@ -182,7 +187,7 @@ ALL_TEXTS = {
182
  "info_419_t8_opt3_got": "새로운 사실: 정치학자 P교수님이 말했어요. '허정 아저씨가 잠깐 나라를 맡고, 곧 국회의원 선거를 다시 할 것 같아요. 민주당이 중심이 돼서 나라를 다스릴 가능성이 높아요. 하지만 좀 시끄럽고 사람들이 원하는 게 많아질 수도 있어요.' (힌트: 허정 아저씨가 임시로 맡음, 다시 선거, 민주당 중심 정치, 좀 시끄러울 수도 있음)",
183
 
184
  # --- 5.18 광주 취재 시나리오 ---
185
- "scenario_518_gwangju_name": "5.18 광주 이야기 (1980년)",
186
  # Turn 1 (5.17 비상계엄 전국 확대)
187
  "event_518_t1_assignment": "5월 17일 밤부터 '비상계엄'이 전국으로 커졌어요. 광주 분위기는 어떤지, 중요한 사람들(학생 대표, 숨어있는 어른들)은 어떻게 움직이는지, 군인들은 뭘 하는지 알아보고 알려주세요. 신문이나 방송을 못 믿게 하니까 몰래 조심해서 취재해야 해요.",
188
  "event_518_t1_source": "[알아두면 좋아요] 10.26 사건 뒤에 '서울의 봄'이라고 해서 좋은 세상이 올 것 같았지만, 새로운 군인 아저씨들이 12.12 군사반란으로 힘을 잡으면서 다시 무서워졌어요. 새 군인들은 나라가 시끄럽다면서 비상계엄을 전국으로 넓히고, 김대중, 김영삼 같은 유명한 정치인들을 잡아가거나 못 나오게 했어요. 광주 대학교에서는 학생들이 시위하려는 움직임이 보여요.",
@@ -255,7 +260,7 @@ ALL_TEXTS = {
255
  "info_518_t8_opt2_got": "새로운 사실: 군인들이 시민들을 몰래 묻었다는 소문이 있어요. 정부는 사망자 수를 줄여서 발표하고, 진실을 숨기려고 해요. 사람들은 언젠가 모든 것이 밝혀지길 바라고 있어요. (힌트: 숨겨진 진실, 정부의 은폐, 진실 규명 바람)",
256
 
257
  # --- 6월 항쟁 취재 시나리오 ---
258
- "scenario_june_struggle_name": "6월 항쟁 이야기 (1987년)",
259
  # Turn 1 (박종철 고문치사 사건)
260
  "event_june_t1_assignment": "서울대학교 학생 박종철 군이 경찰 조사를 받다가 죽었어요. 경찰은 '책상을 탁 치니 억 하고 죽었다'고 이상하게 말했지만, 고문으로 죽었다는 의심이 커지고 있어요. 이 사건의 진짜 이유와 사람들의 반응을 알아보세요.",
261
  "event_june_t1_source": "[알아두면 좋아요] 1987년 1월 14일, 박종철 학생은 경찰서(남영동 대공분실)에서 조사를 받다가 죽었어요. 경찰이 이상하게 설명해서 사람들이 많이 화가 났고, 진짜 이유를 밝��라는 목소리가 커지고 있어요. 전두환 대통령 정부에게 큰 문제가 될 것 같아요.",
@@ -350,14 +355,14 @@ def get_text(key, level="초등"):
350
  text_content = ALL_TEXTS.get(key)
351
  if text_content:
352
  return text_content
353
- print(f"경고: 텍스트 키 '{key}'에 대한 내용을 ALL_TEXTS에서 찾을 수 없습니다.")
354
  return key
355
 
356
 
357
  # --- 게임 상태 초기화 (기자 컨셉) ---
358
  def initialize_reporter_scenario_state(scenario_key):
359
  scenario_settings = SCENARIOS[scenario_key]
360
- get_text_func = lambda k: get_text(k)
361
 
362
  return {
363
  'scenario_key': scenario_key,
@@ -631,7 +636,6 @@ def generate_article_interface(game_state):
631
  }
632
  return None
633
 
634
- # --- 기사 평가 및 데스크 피드백 (OpenAI 사용) ---
635
  # --- 기사 평가 및 데스크 피드백 (OpenAI 사용) ---
636
  def evaluate_article_and_get_feedback_openai(article, game_state, assignment_data):
637
  status = game_state['status']
@@ -697,7 +701,7 @@ def evaluate_article_and_get_feedback_openai(article, game_state, assignment_dat
697
  try:
698
  with st.spinner("AI 편집장님이 기사를 꼼꼼히 검토하고 있습니다..."): # 스피너 메시지 변경
699
  response = client.chat.completions.create(
700
- model="gpt-4.1-mini", # 정교한 평가를 위해 gpt-4o 권장
701
  messages=[{"role": "user", "content": prompt}],
702
  response_format={"type": "json_object"},
703
  temperature=0.1, # 매우 객관적이고 일관된 평가를 위해 낮춤
@@ -778,7 +782,8 @@ def display_historical_glossary_for_reporter(scenario_key):
778
  glossary_keys_for_scenario = [k for k in ALL_TEXTS if k.startswith(f'glossary_{scenario_key}_')]
779
 
780
  if not glossary_keys_for_scenario:
781
- st.sidebar.caption(f"{get_text(SCENARIOS[scenario_key]['display_name'])}에 대한 어려운 말/사람 이야기가 아직 없어요.")
 
782
 
783
  for key in glossary_keys_for_scenario:
784
  term_definition = get_text_for_ui(key)
@@ -810,7 +815,7 @@ def reporter_simulation_main():
810
  }
811
  for key, scenario_info in SCENARIOS.items():
812
  with st.container(border=True):
813
- st.subheader(get_text(scenario_info["display_name"]))
814
  st.caption(f"언제: {scenario_info['start_year']}년, 나는 누구?: {scenario_info['player_role']}")
815
  st.markdown(scenario_descriptions.get(key, "이 시대에는 어떤 일이 있었을까요?"))
816
  if st.button(get_text_main("scenario_select_button"), key=f"select_{key}"):
@@ -828,12 +833,12 @@ def reporter_simulation_main():
828
  if st.session_state.game_mode != 'scenario_select' and st.session_state.game_state:
829
  game_state = st.session_state.game_state
830
  scenario_key = st.session_state.current_scenario_key
831
- get_text_main = lambda key: get_text(key) # 로컬 get_text_main 재정의
832
 
833
  # --- 사이드바 구성 ---
834
  with st.sidebar:
835
  st.header(get_text_main("dashboard_title"))
836
- st.caption(f"지금 시대: {get_text(SCENARIOS[scenario_key]['display_name'])}")
837
  st.caption(f"내 역할: {game_state['player_role']}")
838
  st.markdown(f"**{get_text_main('dashboard_term').format(year=game_state['game_year'], turn=game_state['current_turn'])}**")
839
  display_reporter_dashboard(game_state)
@@ -847,8 +852,8 @@ def reporter_simulation_main():
847
  if assignment_data and len(assignment_data.get("options", [])) > 0: # 취재할 것이 남아있다면
848
  if st.session_state.actions_taken_this_turn < ACTIONS_PER_TURN_LIMIT: # 아직 행동 횟수가 남았다면
849
  can_proceed_to_next_day = False
850
- # 행동 횟수를 다 썼더라도, 기사 작성 단계가 있고 아직 기사를 안 썼다면 넘어가지 못하게 할 수 있음
851
- # (현재 로직은 기사 작성은 선택사항으로 두고 넘어갈 수 있게 함)
852
  # 취재할 것이 없는 턴 (예: 마지막 턴)은 바로 넘어갈 수 있어야 함
853
 
854
  if st.button(get_text_main("button_next_day"), use_container_width=True, key="next_day_button_sidebar",
@@ -860,7 +865,7 @@ def reporter_simulation_main():
860
  (st.session_state.current_assignment_data and st.session_state.current_assignment_data.get("is_final_turn_event", False) and st.session_state.desk_feedback_message is not None): # 마지막 턴 이벤트 후 피드백까지 봤다면
861
  st.session_state.game_mode = 'assignment_over'
862
  if game_state.get('event_log') is not None:
863
- game_state['event_log'].append(get_text_main("log_assignment_over").format(scenario_name=get_text(SCENARIOS[scenario_key]['display_name'])))
864
  else:
865
  game_state['current_turn'] += 1
866
  if game_state.get('event_log') is not None:
@@ -878,7 +883,7 @@ def reporter_simulation_main():
878
 
879
  st.subheader(get_text_main("sidebar_current_source_title"))
880
  current_assignment_data_sidebar = st.session_state.current_assignment_data
881
- if current_assignment_data_sidebar and current_assignment_data_sidebar.get('source_text') and current_assignment_data_sidebar.get('source_text') != "[알아두면 좋아요]":
882
  st.sidebar.info(current_assignment_data_sidebar.get('source_text'))
883
  else:
884
  st.sidebar.caption(get_text_main("sidebar_no_source"))
@@ -936,14 +941,14 @@ def reporter_simulation_main():
936
  st.session_state.game_mode = 'reporter_action'
937
  # 마지막 턴이면서 취재 옵션이 없는 경우 바로 기사쓰기로 가도록 설정
938
  if assignment.get("is_final_turn_event", False) and not assignment.get("options"):
939
- st.session_state.active_main_tab = "🖋️ 기사 쓰기" if assignment.get("article_writing_phase") else "🎤 무엇을 할까요?"
940
  else:
941
- st.session_state.active_main_tab = "🎤 무엇을 할까요?"
942
  st.rerun()
943
  else: # 더 이상 과제가 없으면 게임 종료
944
  st.session_state.game_mode = 'assignment_over'
945
  if game_state.get('event_log') is not None:
946
- game_state['event_log'].append(get_text_main("log_assignment_over").format(scenario_name=get_text(SCENARIOS[scenario_key]['display_name'])))
947
  st.rerun()
948
 
949
  current_assignment_data_main = st.session_state.current_assignment_data
 
60
  "game_title": "🎙️ 진실을 찾아서: 우리 역사 이야기",
61
  "scenario_select_title": "📰 어떤 시대로 가볼까요?",
62
  "scenario_select_button": "이 시대로 출발!",
63
+ # --- 추가된 display_name 키 ---
64
+ "4.19 혁명 취재 (1960)": "4.19 혁명 취재 (1960)",
65
+ "5.18 광주 현장 취재 (1980)": "5.18 광주 현장 취재 (1980)",
66
+ "6월 항쟁 함께 취재 (1987)": "6월 항쟁 함께 취재 (1987)",
67
+ # --- 나머지 공통 UI ---
68
  "dashboard_title": "📊 기자님 현재 상황",
69
  "dashboard_term": "{year}년 {turn}번째 날",
70
  "term_press_freedom": "기사 쓰는 자유",
 
112
  "error_openai_api": "AI 편집장님 연결에 문제가 생겼어요: {error}",
113
 
114
  # --- 4.19 혁명 취재 시나리오 ---
115
+ "scenario_4.19_revolution_name": "4.19 혁명 이야기 (1960년)", # 키 수정 (scenario_419_revolution_name -> scenario_4.19_revolution_name)
116
  # Turn 1 (3.15 부정선거)
117
  "event_419_t1_assignment": "오늘은 3월 15일, 대통령과 부통령을 뽑는 날이에요. 투표하는 곳에서 나쁜 사람들이 규칙을 어기고 자기편만 뽑으려고 할지도 몰라요. 그런 이상한 점을 찾아내고, 투표를 지켜보는 사람, 투표하는 사람, 선거를 관리하는 사람들에게 이야기를 들어보는 것이 오늘 할 일이에요. 특히 반대편 사람들이 잘 감시하는지, 힘 있는 쪽 사람들이 몰래 나쁜 일을 꾸미는지 잘 살펴보세요.",
118
  "event_419_t1_source": "[알아두면 좋아요] 지금 자유당이라는 당이 오랫동안 나라를 다스려서 사람들이 불만이 많아요. 이번 선거에서 이승만 대통령이 또 대통령이 되고, 이기붕 아저씨가 부통령이 되려고 자유당이 나쁜 방법을 쓸 거라는 소문이 많아요. 미리 투표한 표를 몰래 바꾸거나, 없는 사람 표를 넣거나, 여럿이 같이 보면서 투표하게 만들거나, 반대편 감시하는 사람들을 쫓아낸다는 이야기가 돌고 있어요.",
 
187
  "info_419_t8_opt3_got": "새로운 사실: 정치학자 P교수님이 말했어요. '허정 아저씨가 잠깐 나라를 맡고, 곧 국회의원 선거를 다시 할 것 같아요. 민주당이 중심이 돼서 나라를 다스릴 가능성이 높아요. 하지만 좀 시끄럽고 사람들이 원하는 게 많아질 수도 있어요.' (힌트: 허정 아저씨가 임시로 맡음, 다시 선거, 민주당 중심 정치, 좀 시끄러울 수도 있음)",
188
 
189
  # --- 5.18 광주 취재 시나리오 ---
190
+ "scenario_5.18_gwangju_name": "5.18 광주 이야기 (1980년)", # 키 수정 (scenario_518_gwangju_name -> scenario_5.18_gwangju_name)
191
  # Turn 1 (5.17 비상계엄 전국 확대)
192
  "event_518_t1_assignment": "5월 17일 밤부터 '비상계엄'이 전국으로 커졌어요. 광주 분위기는 어떤지, 중요한 사람들(학생 대표, 숨어있는 어른들)은 어떻게 움직이는지, 군인들은 뭘 하는지 알아보고 알려주세요. 신문이나 방송을 못 믿게 하니까 몰래 조심해서 취재해야 해요.",
193
  "event_518_t1_source": "[알아두면 좋아요] 10.26 사건 뒤에 '서울의 봄'이라고 해서 좋은 세상이 올 것 같았지만, 새로운 군인 아저씨들이 12.12 군사반란으로 힘을 잡으면서 다시 무서워졌어요. 새 군인들은 나라가 시끄럽다면서 비상계엄을 전국으로 넓히고, 김대중, 김영삼 같은 유명한 정치인들을 잡아가거나 못 나오게 했어요. 광주 대학교에서는 학생들이 시위하려는 움직임이 보여요.",
 
260
  "info_518_t8_opt2_got": "새로운 사실: 군인들이 시민들을 몰래 묻었다는 소문이 있어요. 정부는 사망자 수를 줄여서 발표하고, 진실을 숨기려고 해요. 사람들은 언젠가 모든 것이 밝혀지길 바라고 있어요. (힌트: 숨겨진 진실, 정부의 은폐, 진실 규명 바람)",
261
 
262
  # --- 6월 항쟁 취재 시나리오 ---
263
+ "scenario_june_struggle_name": "6월 항쟁 이야기 (1987년)", # 이 키는 이미 올바른 형식임
264
  # Turn 1 (박종철 고문치사 사건)
265
  "event_june_t1_assignment": "서울대학교 학생 박종철 군이 경찰 조사를 받다가 죽었어요. 경찰은 '책상을 탁 치니 억 하고 죽었다'고 이상하게 말했지만, 고문으로 죽었다는 의심이 커지고 있어요. 이 사건의 진짜 이유와 사람들의 반응을 알아보세요.",
266
  "event_june_t1_source": "[알아두면 좋아요] 1987년 1월 14일, 박종철 학생은 경찰서(남영동 대공분실)에서 조사를 받다가 죽었어요. 경찰이 이상하게 설명해서 사람들이 많이 화가 났고, 진짜 이유를 밝��라는 목소리가 커지고 있어요. 전두환 대통령 정부에게 큰 문제가 될 것 같아요.",
 
355
  text_content = ALL_TEXTS.get(key)
356
  if text_content:
357
  return text_content
358
+ # print(f"경고: 텍스트 키 '{key}'에 대한 내용을 ALL_TEXTS에서 찾을 수 없습니다.") # 운영 시 주석 처리 가능
359
  return key
360
 
361
 
362
  # --- 게임 상태 초기화 (기자 컨셉) ---
363
  def initialize_reporter_scenario_state(scenario_key):
364
  scenario_settings = SCENARIOS[scenario_key]
365
+ get_text_func = lambda k: get_text(k) # get_text 함수를 사용하도록 수정
366
 
367
  return {
368
  'scenario_key': scenario_key,
 
636
  }
637
  return None
638
 
 
639
  # --- 기사 평가 및 데스크 피드백 (OpenAI 사용) ---
640
  def evaluate_article_and_get_feedback_openai(article, game_state, assignment_data):
641
  status = game_state['status']
 
701
  try:
702
  with st.spinner("AI 편집장님이 기사를 꼼꼼히 검토하고 있습니다..."): # 스피너 메시지 변경
703
  response = client.chat.completions.create(
704
+ model="gpt-4o-mini", # 모델명 변경 (gpt-4.1-mini -> gpt-4o-mini)
705
  messages=[{"role": "user", "content": prompt}],
706
  response_format={"type": "json_object"},
707
  temperature=0.1, # 매우 객관적이고 일관된 평가를 위해 낮춤
 
782
  glossary_keys_for_scenario = [k for k in ALL_TEXTS if k.startswith(f'glossary_{scenario_key}_')]
783
 
784
  if not glossary_keys_for_scenario:
785
+ # SCENARIOS[scenario_key]['display_name'] get_text로 감싸서 처리
786
+ st.sidebar.caption(f"{get_text_for_ui(SCENARIOS[scenario_key]['display_name'])}에 대한 어려운 말/사람 이야기가 아직 없어요.")
787
 
788
  for key in glossary_keys_for_scenario:
789
  term_definition = get_text_for_ui(key)
 
815
  }
816
  for key, scenario_info in SCENARIOS.items():
817
  with st.container(border=True):
818
+ st.subheader(get_text_main(scenario_info["display_name"])) # get_text_main으로 감쌈
819
  st.caption(f"언제: {scenario_info['start_year']}년, 나는 누구?: {scenario_info['player_role']}")
820
  st.markdown(scenario_descriptions.get(key, "이 시대에는 어떤 일이 있었을까요?"))
821
  if st.button(get_text_main("scenario_select_button"), key=f"select_{key}"):
 
833
  if st.session_state.game_mode != 'scenario_select' and st.session_state.game_state:
834
  game_state = st.session_state.game_state
835
  scenario_key = st.session_state.current_scenario_key
836
+ # get_text_main = lambda key: get_text(key) # 로컬 get_text_main 재정의 (이미 위에서 정의됨)
837
 
838
  # --- 사이드바 구성 ---
839
  with st.sidebar:
840
  st.header(get_text_main("dashboard_title"))
841
+ st.caption(f"지금 시대: {get_text_main(SCENARIOS[scenario_key]['display_name'])}") # get_text_main으로 감쌈
842
  st.caption(f"내 역할: {game_state['player_role']}")
843
  st.markdown(f"**{get_text_main('dashboard_term').format(year=game_state['game_year'], turn=game_state['current_turn'])}**")
844
  display_reporter_dashboard(game_state)
 
852
  if assignment_data and len(assignment_data.get("options", [])) > 0: # 취재할 것이 남아있다면
853
  if st.session_state.actions_taken_this_turn < ACTIONS_PER_TURN_LIMIT: # 아직 행동 횟수가 남았다면
854
  can_proceed_to_next_day = False
855
+ # 행동 횟수를 다 썼더라도, 기사 작성 단계가 있고 아직 기사를 안 썼다면 넘어가지 못하게 할 수 있음
856
+ # (현재 로직은 기사 작성은 선택사항으로 두고 넘어갈 수 있게 함)
857
  # 취재할 것이 없는 턴 (예: 마지막 턴)은 바로 넘어갈 수 있어야 함
858
 
859
  if st.button(get_text_main("button_next_day"), use_container_width=True, key="next_day_button_sidebar",
 
865
  (st.session_state.current_assignment_data and st.session_state.current_assignment_data.get("is_final_turn_event", False) and st.session_state.desk_feedback_message is not None): # 마지막 턴 이벤트 후 피드백까지 봤다면
866
  st.session_state.game_mode = 'assignment_over'
867
  if game_state.get('event_log') is not None:
868
+ game_state['event_log'].append(get_text_main("log_assignment_over").format(scenario_name=get_text_main(SCENARIOS[scenario_key]['display_name']))) # get_text_main으로 감쌈
869
  else:
870
  game_state['current_turn'] += 1
871
  if game_state.get('event_log') is not None:
 
883
 
884
  st.subheader(get_text_main("sidebar_current_source_title"))
885
  current_assignment_data_sidebar = st.session_state.current_assignment_data
886
+ if current_assignment_data_sidebar and current_assignment_data_sidebar.get('source_text') and current_assignment_data_sidebar.get('source_text') != "[알아두면 좋아요]": # 빈 문자열도 체크
887
  st.sidebar.info(current_assignment_data_sidebar.get('source_text'))
888
  else:
889
  st.sidebar.caption(get_text_main("sidebar_no_source"))
 
941
  st.session_state.game_mode = 'reporter_action'
942
  # 마지막 턴이면서 취재 옵션이 없는 경우 바로 기사쓰기로 가도록 설정
943
  if assignment.get("is_final_turn_event", False) and not assignment.get("options"):
944
+ st.session_state.active_main_tab = get_text_main("article_writing_title") if assignment.get("article_writing_phase") else get_text_main("reporter_actions_title") # get_text_main으로 감쌈
945
  else:
946
+ st.session_state.active_main_tab = get_text_main("reporter_actions_title") # get_text_main으로 감쌈
947
  st.rerun()
948
  else: # 더 이상 과제가 없으면 게임 종료
949
  st.session_state.game_mode = 'assignment_over'
950
  if game_state.get('event_log') is not None:
951
+ game_state['event_log'].append(get_text_main("log_assignment_over").format(scenario_name=get_text_main(SCENARIOS[scenario_key]['display_name']))) # get_text_main으로 감쌈
952
  st.rerun()
953
 
954
  current_assignment_data_main = st.session_state.current_assignment_data