Keita Inoue commited on
Commit
51cf00a
2 Parent(s): 983c863 3802651

Merge pull request #6 from EQUES-Inc/feature/history

Browse files
main.py CHANGED
@@ -1,18 +1,24 @@
1
  import streamlit as st
2
 
 
3
  from src.forms.deviation_investigation_report import deviation_investigation_report_form
4
- from src.forms.deviation_notification_report import deviation_notification_report_form
5
-
6
- pages = {
7
- "逸脱連絡書": deviation_notification_report_form,
8
- "逸脱調査報告書": deviation_investigation_report_form,
9
- }
10
 
11
  selected_page = st.sidebar.selectbox(
12
- "作成する文書を選択してください。", list(pages.keys())
13
  )
 
14
  match selected_page:
15
- case "逸脱連絡書":
16
- pages["逸脱連絡書"]()
17
- case "逸脱調査報告書":
18
- pages["逸脱調査報告書"]()
 
 
 
 
 
 
1
  import streamlit as st
2
 
3
+ from src.entity.document import DocumentNames
4
  from src.forms.deviation_investigation_report import deviation_investigation_report_form
5
+ from src.forms.deviation_notification_report import (
6
+ deviation_notification_report_form,
7
+ deviation_notification_report_history_file_path,
8
+ )
9
+ from src.forms.history_common import display_download_history
 
10
 
11
  selected_page = st.sidebar.selectbox(
12
+ "作成する文書を選択してください。", [name.value for name in DocumentNames]
13
  )
14
+
15
  match selected_page:
16
+ case DocumentNames.DEVIATION_NOTIFICATION_REPORT.value:
17
+ deviation_notification_report_form()
18
+ case DocumentNames.DEVIATION_INVESTIGATION_REPORT.value:
19
+ deviation_investigation_report_form()
20
+ case DocumentNames.DEVIATION_NOTIFICATION_REPORT_HISTORY.value:
21
+ display_download_history(
22
+ DocumentNames.DEVIATION_NOTIFICATION_REPORT.value,
23
+ deviation_notification_report_history_file_path,
24
+ )
src/aws/s3.py CHANGED
@@ -10,9 +10,10 @@ from src.utils import setup_logger
10
  logger = setup_logger(__name__)
11
 
12
  trial_bucket_name = "pharma-doc-trials"
13
- base_change_request_history_file_path_in_trial_bucket = (
14
- "bankyo/change_request_history.csv"
15
  )
 
16
  csv_extension = ".csv"
17
  err_msg_file_is_not_csv = "指定したファイルはcsvではありません。"
18
 
 
10
  logger = setup_logger(__name__)
11
 
12
  trial_bucket_name = "pharma-doc-trials"
13
+ base_deviation_notification_report_history_file_path_in_trial_bucket = (
14
+ "taiyo/deviation_notification_report_history.csv"
15
  )
16
+
17
  csv_extension = ".csv"
18
  err_msg_file_is_not_csv = "指定したファイルはcsvではありません。"
19
 
src/entity/document.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from enum import Enum
2
+
3
+
4
+ class DocumentNames(Enum):
5
+ DEVIATION_NOTIFICATION_REPORT = "逸脱連絡書"
6
+ DEVIATION_INVESTIGATION_REPORT = "逸脱調査報告書"
7
+ DEVIATION_NOTIFICATION_REPORT_HISTORY = "逸脱連絡履歴"
src/forms/deviation_notification_report.py CHANGED
@@ -1,11 +1,25 @@
1
  import json
2
  import os
3
  from datetime import date, datetime
 
 
4
 
 
5
  import streamlit as st
6
  from dotenv import load_dotenv
7
  from openai import OpenAI
8
 
 
 
 
 
 
 
 
 
 
 
 
9
  from src.prompts.deviation_notification_report import (
10
  deviation_notification_system_template_review,
11
  deviation_notification_systemprompt_output_format_instructions,
@@ -13,6 +27,11 @@ from src.prompts.deviation_notification_report import (
13
  )
14
  from src.utils import output_deviation_notification_report, setup_logger
15
 
 
 
 
 
 
16
  OCCURRENCE_CLASSIFICATIONS = [
17
  "",
18
  "工程内外観",
@@ -282,8 +301,8 @@ def deviation_notification_report_form() -> None:
282
  "課名": st.session_state["section_name"],
283
  "発生場所": st.session_state["occurrence_place"],
284
  "発見者": st.session_state["discoverer"],
285
- "発生日": formatted_occurrence_date,
286
  "発見日": formatted_discovery_date,
 
287
  "品質保証課への第一報日": formatted_first_report_date,
288
  "発生事象の分類": st.session_state["occurrence_classification"],
289
  "逸脱概要": str(response_data["逸脱概要"]),
@@ -305,6 +324,35 @@ def deviation_notification_report_form() -> None:
305
  )
306
 
307
  output_deviation_notification_report(st.session_state["contents"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  st.session_state["document_generated"] = True
309
 
310
  if st.session_state["document_generated"]:
@@ -321,6 +369,11 @@ def deviation_notification_report_form() -> None:
321
  for key, value in st.session_state["contents"].items():
322
  st.markdown(f"**{key}**: {value}")
323
 
 
 
 
 
 
324
  deviation_notification_user_template_review = f"""
325
  ## 今回の「逸脱概要」:
326
  {st.session_state["contents"]["逸脱概要"]}
@@ -348,3 +401,156 @@ def deviation_notification_report_form() -> None:
348
 
349
  logger.info(f"**レビュー結果** : {review}")
350
  st.markdown(f"**レビュー結果** : {review}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import json
2
  import os
3
  from datetime import date, datetime
4
+ from typing import Any, List
5
+ from zoneinfo import ZoneInfo
6
 
7
+ import pandas as pd
8
  import streamlit as st
9
  from dotenv import load_dotenv
10
  from openai import OpenAI
11
 
12
+ from src.aws.s3 import (
13
+ base_deviation_notification_report_history_file_path_in_trial_bucket,
14
+ )
15
+ from src.aws.s3 import get_client as get_s3_client
16
+ from src.aws.s3 import (
17
+ get_csv_as_pd_dataframe_from_s3,
18
+ save_pd_dataframe_as_csv_to_s3,
19
+ trial_bucket_name,
20
+ )
21
+ from src.entity.document import DocumentNames
22
+ from src.forms.history_common import display_history
23
  from src.prompts.deviation_notification_report import (
24
  deviation_notification_system_template_review,
25
  deviation_notification_systemprompt_output_format_instructions,
 
27
  )
28
  from src.utils import output_deviation_notification_report, setup_logger
29
 
30
+ deviation_notification_report_history_file_path = (
31
+ "data/deviation_notification_report_history.csv"
32
+ )
33
+
34
+
35
  OCCURRENCE_CLASSIFICATIONS = [
36
  "",
37
  "工程内外観",
 
301
  "課名": st.session_state["section_name"],
302
  "発生場所": st.session_state["occurrence_place"],
303
  "発見者": st.session_state["discoverer"],
 
304
  "発見日": formatted_discovery_date,
305
+ "発生日": formatted_occurrence_date,
306
  "品質保証課への第一報日": formatted_first_report_date,
307
  "発生事象の分類": st.session_state["occurrence_classification"],
308
  "逸脱概要": str(response_data["逸脱概要"]),
 
324
  )
325
 
326
  output_deviation_notification_report(st.session_state["contents"])
327
+
328
+ save_deviation_notification_report_history(
329
+ st.session_state["contents"]["件名"],
330
+ st.session_state["contents"]["管理番号"],
331
+ st.session_state["contents"]["品名"],
332
+ st.session_state["contents"]["品目コード"],
333
+ st.session_state["contents"]["工程名"],
334
+ st.session_state["contents"]["標準書番号"],
335
+ st.session_state["contents"]["ロット番号"],
336
+ st.session_state["contents"]["課名"],
337
+ st.session_state["contents"]["発生場所"],
338
+ st.session_state["contents"]["発見者"],
339
+ st.session_state["contents"]["発見日"],
340
+ st.session_state["contents"]["発生日"],
341
+ st.session_state["contents"]["品質保証課への第一報日"],
342
+ st.session_state["contents"]["発生事象の分類"],
343
+ st.session_state["contents"]["逸脱概要"],
344
+ st.session_state["contents"]["応急措置"],
345
+ st.session_state["contents"]["調査(根本原因・品質影響等)の提案"],
346
+ st.session_state["contents"]["調査の実施部署"],
347
+ st.session_state["contents"][
348
+ "措置(回復措置・製品等に対する措置)の提案"
349
+ ],
350
+ st.session_state["contents"]["措置の実施部署"],
351
+ st.session_state["contents"]["調査回答期限(30営業日)"],
352
+ st.session_state["contents"]["添付資料"],
353
+ answers,
354
+ )
355
+
356
  st.session_state["document_generated"] = True
357
 
358
  if st.session_state["document_generated"]:
 
369
  for key, value in st.session_state["contents"].items():
370
  st.markdown(f"**{key}**: {value}")
371
 
372
+ display_history(
373
+ DocumentNames.DEVIATION_NOTIFICATION_REPORT.value,
374
+ deviation_notification_report_history_file_path,
375
+ )
376
+
377
  deviation_notification_user_template_review = f"""
378
  ## 今回の「逸脱概要」:
379
  {st.session_state["contents"]["逸脱概要"]}
 
401
 
402
  logger.info(f"**レビュー結果** : {review}")
403
  st.markdown(f"**レビュー結果** : {review}")
404
+
405
+
406
+ def save_deviation_notification_report_history(
407
+ subject: str,
408
+ control_number: str,
409
+ product_name: str,
410
+ item_code: str,
411
+ process_name: str,
412
+ standard_number: str,
413
+ lot_number: str,
414
+ section_name: str,
415
+ occurrence_place: str,
416
+ discoverer: str,
417
+ discovery_date: str,
418
+ occurrence_date: str,
419
+ first_report_date: str,
420
+ occurrence_classification: str,
421
+ output_deviation_summary: str,
422
+ output_emergency_measures: str,
423
+ output_proposed_investigation: str,
424
+ investigation_department: str,
425
+ output_proposed_measures: str,
426
+ measures_department: str,
427
+ survey_response_deadline: str,
428
+ attached_files: str,
429
+ answers: List[str],
430
+ ) -> None:
431
+ s3_client = get_s3_client()
432
+ try:
433
+ env = os.environ["ENV"]
434
+ if env != "production":
435
+ file_name, file_extension = os.path.splitext(
436
+ base_deviation_notification_report_history_file_path_in_trial_bucket
437
+ )
438
+ deviation_notification_report_history_file_path_in_trial_bucket = (
439
+ f"{file_name}_{env}{file_extension}"
440
+ )
441
+ else:
442
+ deviation_notification_report_history_file_path_in_trial_bucket = (
443
+ base_deviation_notification_report_history_file_path_in_trial_bucket
444
+ )
445
+ df = get_csv_as_pd_dataframe_from_s3(
446
+ s3_client,
447
+ trial_bucket_name,
448
+ deviation_notification_report_history_file_path_in_trial_bucket,
449
+ )
450
+ except s3_client.exceptions.NoSuchKey:
451
+ df = pd.DataFrame()
452
+
453
+ new_row_data = create_new_row_data(
454
+ subject,
455
+ control_number,
456
+ product_name,
457
+ item_code,
458
+ process_name,
459
+ standard_number,
460
+ lot_number,
461
+ section_name,
462
+ occurrence_place,
463
+ discoverer,
464
+ discovery_date,
465
+ occurrence_date,
466
+ first_report_date,
467
+ occurrence_classification,
468
+ output_deviation_summary,
469
+ output_emergency_measures,
470
+ output_proposed_investigation,
471
+ investigation_department,
472
+ output_proposed_measures,
473
+ measures_department,
474
+ survey_response_deadline,
475
+ attached_files,
476
+ answers,
477
+ )
478
+
479
+ new_row = pd.DataFrame([new_row_data])
480
+ df = pd.concat([df, new_row], ignore_index=True)
481
+
482
+ # NOTE: トライアル版のため、排他制御は実施しない。バケットはバージョニングする。
483
+ save_pd_dataframe_as_csv_to_s3(
484
+ s3_client,
485
+ df,
486
+ trial_bucket_name,
487
+ deviation_notification_report_history_file_path_in_trial_bucket,
488
+ )
489
+
490
+
491
+ def create_new_row_data(
492
+ subject: str,
493
+ control_number: str,
494
+ product_name: str,
495
+ item_code: str,
496
+ process_name: str,
497
+ standard_number: str,
498
+ lot_number: str,
499
+ section_name: str,
500
+ occurrence_place: str,
501
+ discoverer: str,
502
+ discovery_date: str,
503
+ occurrence_date: str,
504
+ first_report_date: str,
505
+ occurrence_classification: str,
506
+ output_deviation_summary: str,
507
+ output_emergency_measures: str,
508
+ output_proposed_investigation: str,
509
+ investigation_department: str,
510
+ output_proposed_measures: str,
511
+ measures_department: str,
512
+ survey_response_deadline: str,
513
+ attached_files: str,
514
+ answers: List[str],
515
+ ) -> dict[str, Any]:
516
+ now = datetime.now(ZoneInfo("Asia/Tokyo"))
517
+ data = {
518
+ "件名": subject,
519
+ "作成日時": now,
520
+ "管理番号": control_number,
521
+ "品名": product_name,
522
+ "品目コード": item_code,
523
+ "工程名": process_name,
524
+ "標準書番号": standard_number,
525
+ "ロット番号": lot_number,
526
+ "課名": section_name,
527
+ "発生場所": occurrence_place,
528
+ "発見者": discoverer,
529
+ "発見日": discovery_date,
530
+ "発生日": occurrence_date,
531
+ "品質保証課への第一報日": first_report_date,
532
+ "発生事象の分類": occurrence_classification,
533
+ "逸脱概要": output_deviation_summary,
534
+ "応急処置": output_emergency_measures,
535
+ "調査(根本原因・品質影響等)の提案": output_proposed_investigation,
536
+ "調査の実施部署": investigation_department,
537
+ "措置(回復措置・製品等に対する措置)の提案": output_proposed_measures,
538
+ "措置の実施部署": measures_department,
539
+ "調査回答期限(30営業日)": survey_response_deadline,
540
+ "添付資料": attached_files,
541
+ "逸脱内容(回答)": answers[0],
542
+ "発見の経緯(回答)": answers[1],
543
+ "応急処置とその理由(回答)": answers[2],
544
+ "当該ロットの品質評価(回答)": answers[3],
545
+ "安定性モニタリングの対象ロットか(回答)": answers[4],
546
+ "安定性モニタリングの検体採取表の更新(回答)": answers[5],
547
+ "在庫ロットの品質評価・状況確認(回答)": answers[6],
548
+ "メーカーへの調査依頼(回答)": answers[7],
549
+ "生産スケジュール・受注状況の変更(回答)": answers[8],
550
+ "出荷日の変更(回答)": answers[9],
551
+ "製造販売業者への連絡(回答)": answers[10],
552
+ "他製品・他ラインへの影響(回答)": answers[11],
553
+ "過去製造ロットの品質の再評価(回答)": answers[12],
554
+ "その他の調査・措置(回答)": answers[13],
555
+ }
556
+ return data
src/forms/history_common.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import streamlit as st
4
+
5
+ from src.aws.s3 import (
6
+ base_deviation_notification_report_history_file_path_in_trial_bucket,
7
+ get_client,
8
+ get_csv_as_pd_dataframe_from_s3,
9
+ trial_bucket_name,
10
+ )
11
+ from src.entity.document import DocumentNames
12
+
13
+
14
+ def display_history(document_name: str, history_file_path: str) -> None:
15
+ match document_name:
16
+ case DocumentNames.DEVIATION_NOTIFICATION_REPORT.value:
17
+ base_history_file_path_in_trial_bucket = (
18
+ base_deviation_notification_report_history_file_path_in_trial_bucket
19
+ )
20
+
21
+ s3_client = get_client()
22
+ env = os.environ["ENV"]
23
+ if env != "production":
24
+ file_name, file_extension = os.path.splitext(
25
+ base_history_file_path_in_trial_bucket
26
+ )
27
+ history_file_path_in_trial_bucket = f"{file_name}_{env}{file_extension}"
28
+ else:
29
+ history_file_path_in_trial_bucket = base_history_file_path_in_trial_bucket
30
+ data = get_csv_as_pd_dataframe_from_s3(
31
+ s3_client,
32
+ trial_bucket_name,
33
+ history_file_path_in_trial_bucket,
34
+ )
35
+ data.to_csv(history_file_path, index=False)
36
+ st.write(f"**{document_name}作成履歴**")
37
+ st.dataframe(data)
38
+ return
39
+
40
+
41
+ def display_download_history(document_name: str, history_file_path: str) -> None:
42
+ match document_name:
43
+ case DocumentNames.DEVIATION_NOTIFICATION_REPORT.value:
44
+ base_history_file_path_in_trial_bucket = (
45
+ base_deviation_notification_report_history_file_path_in_trial_bucket
46
+ )
47
+
48
+ s3_client = get_client()
49
+ try:
50
+ env = os.environ["ENV"]
51
+ if env != "production":
52
+ file_name, file_extension = os.path.splitext(
53
+ base_history_file_path_in_trial_bucket
54
+ )
55
+ history_file_path_in_trial_bucket = f"{file_name}_{env}{file_extension}"
56
+ else:
57
+ history_file_path_in_trial_bucket = base_history_file_path_in_trial_bucket
58
+ data = get_csv_as_pd_dataframe_from_s3(
59
+ s3_client,
60
+ trial_bucket_name,
61
+ history_file_path_in_trial_bucket,
62
+ )
63
+ except s3_client.exceptions.NoSuchKey:
64
+ st.write(f"{document_name}作成履歴がありません。")
65
+ return
66
+ except Exception:
67
+ st.error(
68
+ f"システムの内部エラーにより、{document_name}作成履歴の表示ができません。システム管理者に問い合わせて下さい。"
69
+ )
70
+ return
71
+ data.to_csv(history_file_path, index=False)
72
+ st.write(f"**{document_name}作成履歴**")
73
+ st.dataframe(data)
74
+ file_exists = os.path.isfile(history_file_path)
75
+ if file_exists:
76
+ with open(history_file_path, "rb") as file:
77
+ st.download_button(
78
+ label="ダウンロード",
79
+ data=file,
80
+ file_name="history.csv",
81
+ mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
82
+ )
83
+ else:
84
+ st.error(
85
+ f"システムの内部エラーにより、{document_name}作成履歴の表示ができません。システム管理者に問い合わせて下さい。"
86
+ )
87
+ return