Update src/streamlit_app.py

#1
by fitz87 - opened
Files changed (1) hide show
  1. src/streamlit_app.py +189 -69
src/streamlit_app.py CHANGED
@@ -318,44 +318,53 @@ def generate_wordcloud(keywords_dict):
318
  st.warning("워드클라우드 설치안되어 있습니다.")
319
  return None
320
  try:
321
- wc= WordCloud(
 
322
  width=800,
323
  height=400,
324
- background_color = 'white',
325
- colormap = 'viridis',
326
  max_font_size=150,
327
  random_state=42
328
- ).generate_from_frequencies(keywords_dict)
329
 
330
  try:
331
  import os
332
- script_dir = os.path.dirname(os.path.abspath(__file__))
333
- possible_font_paths = ["NanumGothic.ttf", "이름"]
 
 
334
 
335
  font_path = None
336
- for path in possible_font_paths:
337
- candidate = os.path.join(script_dir, fname)
338
  if os.path.exists(candidate):
339
  font_path = candidate
340
  break
341
-
 
342
  if font_path:
343
- wc= WordCloud(
344
  font_path=font_path,
345
  width=800,
346
  height=400,
347
- background_color = 'white',
348
- colormap = 'viridis',
349
  max_font_size=150,
350
  random_state=42
351
  ).generate_from_frequencies(keywords_dict)
352
- except Exception as e:
353
- print(f"오류발생 {str(e)}")
354
 
355
- return wc
 
 
 
 
 
356
 
357
  except Exception as e:
358
- st.error(f"오류발생 {str(e)}")
359
  return None
360
 
361
  # 뉴스 분석 함수
@@ -389,7 +398,7 @@ def generate_article(original_content, prompt_text):
389
  return "OpenAI API 키가 설정되지 않았습니다."
390
 
391
  response = st.session_state.openai_client.chat.completions.create(
392
- model="gpt-4", # 또는 사용 가능한 적절한 모델
393
  messages=[
394
  {"role": "system", "content": "당신은 전문적인 뉴스 기자입니다. 주어진 내용을 바탕으로 새로운 기사를 작성해주세요."},
395
  {"role": "user", "content": f"다음 내용을 바탕으로 {prompt_text}\n\n{original_content[:1000]}"}
@@ -400,6 +409,26 @@ def generate_article(original_content, prompt_text):
400
  except Exception as e:
401
  return f"기사 생성 오류: {str(e)}"
402
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
403
  # OpenAI API를 이용한 이미지 생성 (새로운 버전 방식)
404
  def generate_image(prompt):
405
  try:
@@ -922,19 +951,23 @@ elif menu == "새 기사 생성하기":
922
  if not articles:
923
  st.warning("저장된 기사가 없습니다. 먼저 '뉴스 기사 크롤링' 메뉴에서 기사를 수집해주세요.")
924
  else:
925
- # 기사 선택
926
- titles = [article['title'] for article in articles]
927
- selected_title = st.selectbox("원본 기사 선택", titles)
928
 
929
- selected_article = next((a for a in articles if a['title'] == selected_title), None)
930
-
931
- if selected_article:
932
- st.write(f"**원본 제목:** {selected_article['title']}")
933
 
934
- with st.expander("원본 기사 내용"):
935
- st.write(selected_article['content'])
936
 
937
- prompt_text ="""다음 기사 양식을 따라서 다시 작성해줘.
 
 
 
 
 
 
938
  역할: 당신은 신문사의 기자입니다.
939
  작업: 최근 일어난 사건에 대한 보도자료를 작성해야 합니다. 자료는 사실을 기반으로 하며, 객관적이고 정확해야 합니다.
940
  지침:
@@ -943,52 +976,139 @@ elif menu == "새 기사 생성하기":
943
  기사 내용은 정확하고 간결하며 설득력 있는 문장으로 구성합니다.
944
  관련자의 인터뷰를 인용 형태로 넣어주세요.
945
  위의 정보와 지침을 참고하여 신문 보도자료 형식의 기사를 작성해 주세요"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
946
 
947
- # 이미지 생성 여부 선택 옵션 추가
948
- generate_image_too = st.checkbox("기사 생성 후 이미지도 함께 생성하기", value=True)
 
949
 
950
- if st.button("새 기사 생성하기"):
951
- if st.session_state.openai_client:
952
- with st.spinner("기사를 생성 중입니다..."):
953
- new_article = generate_article(selected_article['content'], prompt_text)
954
-
955
- st.write("**생성된 기사:**")
956
- st.write(new_article)
957
-
958
- # 이미지 생성하기 (옵션이 선택된 경우)
959
- if generate_image_too:
960
- with st.spinner("기사 관련 이미지를 생성 중입니다..."):
961
- image_prompt = f"""신문기사 제목 "{selected_article['title']}" 을 보고 이미지를 만들어줘
962
- 이미지에는 다음 요소가 포함되어야 합니다:
963
- - 기사를 이해할 수 있는 도식
964
- - 기사 내용과 관련된 텍스트
965
- - 심플하게 처리
966
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
967
 
968
- # 이미지 생성
969
- image = generate_image(image_prompt)
970
 
971
- if isinstance(image, BytesIO):
972
- st.subheader("생성된 이미지:")
973
- st.image(image, use_column_width=True)
974
- else:
975
- st.error(image)
976
-
977
- # 생성된 기사 저장 옵션
978
- if st.button("생성된 기사 ���장"):
979
- new_article_data = {
980
- 'title': f"[생성됨] {selected_article['title']}",
981
- 'source': f"AI 생성 (원본: {selected_article['source']})",
982
- 'date': datetime.now().strftime("%Y-%m-%d %H:%M"),
983
- 'description': new_article[:100] + "...",
984
- 'link': "",
985
- 'content': new_article
986
- }
987
- articles.append(new_article_data)
988
- save_articles(articles)
989
- st.success("생성된 기사가 저장되었습니다!")
990
- else:
991
- st.warning("OpenAI API 키를 사이드바에서 설정해주세요.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
992
 
993
  elif menu == "뉴스 기사 예약하기":
994
  st.header("뉴스 기사 예약하기")
 
318
  st.warning("워드클라우드 설치안되어 있습니다.")
319
  return None
320
  try:
321
+ # 기본 WordCloud 객체 (폰트 경로 없이)
322
+ wc = WordCloud(
323
  width=800,
324
  height=400,
325
+ background_color='white',
326
+ colormap='viridis',
327
  max_font_size=150,
328
  random_state=42
329
+ )
330
 
331
  try:
332
  import os
333
+ script_dir = os.path.dirname(os.path.abspath(__file__))
334
+ # 사용자가 루트에 넣은 폰트 파일 이름을 지정합니다.
335
+ # 만약 다른 이름의 폰트를 사용했다면 이 부분을 수정해주세요. (예: "YourFontName.ttf")
336
+ possible_font_paths = ["NanumGothic.ttf"]
337
 
338
  font_path = None
339
+ for path_segment in possible_font_paths:
340
+ candidate = os.path.join(script_dir, path_segment)
341
  if os.path.exists(candidate):
342
  font_path = candidate
343
  break
344
+
345
+ # font_path가 성공적으로 찾아진 경우에만 폰트 경로를 포함하여 WordCloud 재생성
346
  if font_path:
347
+ wc = WordCloud(
348
  font_path=font_path,
349
  width=800,
350
  height=400,
351
+ background_color='white',
352
+ colormap='viridis',
353
  max_font_size=150,
354
  random_state=42
355
  ).generate_from_frequencies(keywords_dict)
356
+ else:
357
+ st.warning(f"지정된 한국어 글꼴 파일({', '.join(possible_font_paths)})을 스크립트 디렉터리에서 찾을 수 없습니다. 워드클라우드가 깨질 수 있습니다.")
358
 
359
+ except Exception as e:
360
+ print(f"글꼴 로딩 중 오류 발생: {str(e)}")
361
+ st.warning(f"글꼴 로딩 중 예상치 못한 오류가 발생했습니다: {str(e)}") # 사용자에게도 경고 표시
362
+
363
+ # 최종적으로 wc 객체 반환 (폰트가 적용되었거나, 기본 객체이거나)
364
+ return wc.generate_from_frequencies(keywords_dict) if isinstance(wc, WordCloud) else None
365
 
366
  except Exception as e:
367
+ st.error(f"워드클라우드 생성 중 오류발생: {str(e)}")
368
  return None
369
 
370
  # 뉴스 분석 함수
 
398
  return "OpenAI API 키가 설정되지 않았습니다."
399
 
400
  response = st.session_state.openai_client.chat.completions.create(
401
+ model="gpt-4.1-nano", # 또는 사용 가능한 적절한 모델
402
  messages=[
403
  {"role": "system", "content": "당신은 전문적인 뉴스 기자입니다. 주어진 내용을 바탕으로 새로운 기사를 작성해주세요."},
404
  {"role": "user", "content": f"다음 내용을 바탕으로 {prompt_text}\n\n{original_content[:1000]}"}
 
409
  except Exception as e:
410
  return f"기사 생성 오류: {str(e)}"
411
 
412
+ # 여러 제목으로부터 기사 생성하는 함수 추가
413
+ def generate_article_from_titles(titles, prompt_text):
414
+ try:
415
+ if not st.session_state.openai_client:
416
+ return "OpenAI API 키가 설정되지 않았습니다."
417
+
418
+ titles_text = "\n".join([f"- {title}" for title in titles])
419
+
420
+ response = st.session_state.openai_client.chat.completions.create(
421
+ model="gpt-4.1-nano", # 또는 사용 가능한 적절한 모델
422
+ messages=[
423
+ {"role": "system", "content": "당신은 전문적인 뉴스 기자입니다. 주어진 여러 뉴스 제목을 바탕으로 새로운 통합 기사를 작성해주세요."},
424
+ {"role": "user", "content": f"다음 뉴스 제목들을 바탕으로 {prompt_text}\n\n{titles_text}"}
425
+ ],
426
+ max_tokens=2000
427
+ )
428
+ return response.choices[0].message.content
429
+ except Exception as e:
430
+ return f"기사 생성 오류: {str(e)}"
431
+
432
  # OpenAI API를 이용한 이미지 생성 (새로운 버전 방식)
433
  def generate_image(prompt):
434
  try:
 
951
  if not articles:
952
  st.warning("저장된 기사가 없습니다. 먼저 '뉴스 기사 크롤링' 메뉴에서 기사를 수집해주세요.")
953
  else:
954
+ # 탭 추가: 단일 기사로 생성 vs 다중 제목으로 생성
955
+ tab1, tab2 = st.tabs(["단일 기사로 생성", "여러 제목으로 생성"])
 
956
 
957
+ with tab1:
958
+ # 기존 코드: 단일 기사 선택
959
+ titles = [article['title'] for article in articles]
960
+ selected_title = st.selectbox("원본 기사 선택", titles, key="single_article")
961
 
962
+ selected_article = next((a for a in articles if a['title'] == selected_title), None)
 
963
 
964
+ if selected_article:
965
+ st.write(f"**원본 제목:** {selected_article['title']}")
966
+
967
+ with st.expander("원본 기사 내용"):
968
+ st.write(selected_article['content'])
969
+
970
+ prompt_text ="""다음 기사 양식을 따라서 다시 작성해줘.
971
  역할: 당신은 신문사의 기자입니다.
972
  작업: 최근 일어난 사건에 대한 보도자료를 작성해야 합니다. 자료는 사실을 기반으로 하며, 객관적이고 정확해야 합니다.
973
  지침:
 
976
  기사 내용은 정확하고 간결하며 설득력 있는 문장으로 구성합니다.
977
  관련자의 인터뷰를 인용 형태로 넣어주세요.
978
  위의 정보와 지침을 참고하여 신문 보도자료 형식의 기사를 작성해 주세요"""
979
+
980
+ # 이미지 생성 여부 선택 옵션 추가
981
+ generate_image_too = st.checkbox("기사 생성 후 이미지도 함께 생성하기", value=True, key="single_image")
982
+
983
+ if st.button("새 기사 생성하기", key="generate_single"):
984
+ if st.session_state.openai_client:
985
+ with st.spinner("기사를 생성 중입니다..."):
986
+ new_article = generate_article(selected_article['content'], prompt_text)
987
+
988
+ st.write("**생성된 기사:**")
989
+ st.write(new_article)
990
+
991
+ # 이미지 생성하기 (옵션이 선택된 경우)
992
+ if generate_image_too:
993
+ with st.spinner("기사 관련 이미지를 생성 중입니다..."):
994
+ image_prompt = f"""신문기사 제목 "{selected_article['title']}" 을 보고 이미지를 만들어줘
995
+ 이미지에는 다음 요소가 포함되어야 합니다:
996
+ - 기사를 이해할 수 있는 도식
997
+ - 기사 내용과 관련된 텍스트
998
+ - 심플하게 처리
999
+ """
1000
+
1001
+ # 이미지 생성
1002
+ image = generate_image(image_prompt)
1003
+
1004
+ if isinstance(image, BytesIO):
1005
+ st.subheader("생성된 이미지:")
1006
+ st.image(image, use_column_width=True)
1007
+ else:
1008
+ st.error(image)
1009
+
1010
+ # 생성된 기사 저장 옵션
1011
+ if st.button("생성된 기사 저장", key="save_single"):
1012
+ new_article_data = {
1013
+ 'title': f"[생성됨] {selected_article['title']}",
1014
+ 'source': f"AI 생성 (원본: {selected_article['source']})",
1015
+ 'date': datetime.now().strftime("%Y-%m-%d %H:%M"),
1016
+ 'description': new_article[:100] + "...",
1017
+ 'link': "",
1018
+ 'content': new_article
1019
+ }
1020
+ articles.append(new_article_data)
1021
+ save_articles(articles)
1022
+ st.success("생성된 기사가 저장되었습니다!")
1023
+ else:
1024
+ st.warning("OpenAI API 키를 사이드바에서 설정해주세요.")
1025
+
1026
+ with tab2:
1027
+ # 새로운 기능: 여러 제목으로 기사 생성
1028
+ st.subheader("여러 제목을 기반으로 하나의 기사 생성하기")
1029
 
1030
+ # 다중 선택 위젯으로 여러 제목 선택 가능
1031
+ titles = [article['title'] for article in articles]
1032
+ selected_titles = st.multiselect("여러 기사 제목 선택 (2개 이상 권장)", titles)
1033
 
1034
+ if selected_titles:
1035
+ st.write(f"**선택된 제목 수:** {len(selected_titles)}개")
1036
+
1037
+ with st.expander("선택된 제목 목록"):
1038
+ for i, title in enumerate(selected_titles):
1039
+ st.write(f"{i+1}. {title}")
1040
+
1041
+ multi_prompt_text = """다음 뉴스 제목들을 종합하여 하나의 통합된 기사로 작성해줘.
1042
+ 역할: 당신은 신문사의 기자입니다.
1043
+ 작업: 여러 뉴스 제목에서 공통 주제를 파악하고, 이를 종합한 보도자료를 작성해야 합니다.
1044
+ 지침:
1045
+ - 제공된 여러 제목을 종합적으로 분석하여 하나의 일관된 기사를 작성하세요.
1046
+ - 기사 제목은 제공된 모든 제목의 핵심 주제를 담아야 합니다.
1047
+ - 기사 내용은 제목들이 다루는 모든 주요 주제를 포함해야 합니다.
1048
+ - 관련자의 가상 인터뷰를 인용 형태로 넣어주세요.
1049
+ - 제공된 제목들의 맥락을 유지하면서 일관성 있는 내러티브를 구성하세요."""
1050
+
1051
+ # 프롬프트 편집 옵션
1052
+ custom_prompt = st.checkbox("직접 프롬프트 작성하기")
1053
+ if custom_prompt:
1054
+ multi_prompt_text = st.text_area("프롬프트 직접 입력", multi_prompt_text, height=250)
1055
+
1056
+ # 이미지 생성 옵션
1057
+ generate_multi_image = st.checkbox("기사 생성 후 이미지도 함께 생성하기", value=True, key="multi_image")
1058
+
1059
+ if st.button("새 기사 생성하기", key="generate_multi"):
1060
+ if st.session_state.openai_client:
1061
+ if len(selected_titles) < 1:
1062
+ st.error("최소 1개 이상의 제목을 선택해주세요.")
1063
+ else:
1064
+ with st.spinner("여러 제목으로부터 기사를 생성 중입니다..."):
1065
+ # 선택된 제목들을 이용하여 새 기사 생성
1066
+ new_article = generate_article_from_titles(selected_titles, multi_prompt_text)
1067
 
1068
+ st.write("**생성된 기사:**")
1069
+ st.write(new_article)
1070
 
1071
+ # 이미지 생성 (옵션이 선택된 경우)
1072
+ if generate_multi_image:
1073
+ with st.spinner("기사 관련 이미지를 생성 중입니다..."):
1074
+ combined_titles = " / ".join(selected_titles[:3]) # 처음 3개 제목만 사용
1075
+ image_prompt = f"""여러 뉴스를 종합한 기사 "{combined_titles}" 관련 이미지를 만들어줘.
1076
+ 이미지에는 다음 요소가 포함되어야 합니다:
1077
+ - 여러 뉴스의 공통 주제를 시각화한 도식
1078
+ - 핵심 키워드나 개념
1079
+ - 심플하고 통합된 디자인
1080
+ """
1081
+
1082
+ # 이미지 생성
1083
+ image = generate_image(image_prompt)
1084
+
1085
+ if isinstance(image, BytesIO):
1086
+ st.subheader("생성된 이미지:")
1087
+ st.image(image, use_column_width=True)
1088
+ else:
1089
+ st.error(image)
1090
+
1091
+ # 생성된 기사 저장 옵션
1092
+ if st.button("생성된 기사 저장", key="save_multi"):
1093
+ # 통합 제목 생성 (첫 번째 제목 + 추가 제목 수)
1094
+ if len(selected_titles) > 1:
1095
+ combined_title = f"{selected_titles[0]} 외 {len(selected_titles)-1}건 관련 소식"
1096
+ else:
1097
+ combined_title = selected_titles[0]
1098
+
1099
+ new_article_data = {
1100
+ 'title': f"[여러 제목 통합] {combined_title}",
1101
+ 'source': "AI 생성 (여러 제목 통합)",
1102
+ 'date': datetime.now().strftime("%Y-%m-%d %H:%M"),
1103
+ 'description': new_article[:100] + "...",
1104
+ 'link': "",
1105
+ 'content': new_article
1106
+ }
1107
+ articles.append(new_article_data)
1108
+ save_articles(articles)
1109
+ st.success("생성된 기사가 저장되었습니다!")
1110
+ else:
1111
+ st.warning("OpenAI API 키를 사이드바에서 설정해주세요.")
1112
 
1113
  elif menu == "뉴스 기사 예약하기":
1114
  st.header("뉴스 기사 예약하기")