Merge pull request #281 feature-NEWDWH2021-1236 into develop

This commit is contained in:
朝倉 明日香 2023-10-11 11:06:04 +09:00
commit fc23443e0a
12 changed files with 859 additions and 747 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,9 @@
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Request
from starlette import status
from src.depends.services import get_service
from src.logging.get_logger import get_logger
from src.model.internal.session import UserSession
from src.model.request.bio import BioModel
from src.model.view.bio_view_model import BioViewModel
from src.router.session_router import AuthenticatedRoute
from src.services.batch_status_service import BatchStatusService
from src.services.bio_view_service import BioViewService
@ -48,34 +44,3 @@ def bio_view(
headers={'session_key': session_key}
)
return templates_response
@router.post('/BioSearchList')
def search_bio(
request: Request,
bio_form: Optional[BioModel] = Depends(BioModel.as_form),
bio_service: BioViewService = Depends(get_service(BioViewService)),
batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService))
):
session: UserSession = request.session
# バッチ処理中の場合、機能を利用させない
if batch_status_service.is_batch_processing():
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=constants.LOGOUT_REASON_BATCH_PROCESSING)
# 生物由来データを検索
bio_sales_view_data = bio_service.search_bio_data(bio_form)
# 検索項目などのデータを取得
bio: BioViewModel = bio_service.prepare_bio_view(session)
bio.bio_data = bio_sales_view_data
bio.form_data = bio_form
# レスポンス
session_key = session.session_key
templates_response = templates.TemplateResponse(
'bioSearchList.html', {
'request': request,
'bio': bio
},
headers={'session_key': session_key}
)
return templates_response

View File

@ -1,9 +1,10 @@
"""生物由来ファイルダウンロード APIRoute"""
from datetime import datetime
from typing import Union
"""生物由来照会 APIRoute"""
import datetime
from typing import Optional, Union
import pandas as pd
from fastapi import APIRouter, Depends, HTTPException
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from starlette import status
@ -13,6 +14,7 @@ from src.logging.get_logger import get_logger
from src.model.internal.session import UserSession
from src.model.request.bio import BioModel
from src.model.request.bio_download import BioDownloadModel
from src.model.view.bio_disp_model import BioDisplayModel
from src.services.batch_status_service import BatchStatusService
from src.services.bio_view_service import BioViewService
from src.services.session_service import set_session
@ -27,6 +29,67 @@ router = APIRouter()
#########################
@router.post('/search')
def search_bio_data(
bio_form: Optional[BioModel] = Depends(BioModel.as_form),
bio_service: BioViewService = Depends(get_service(BioViewService)),
batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)),
session: Union[UserSession, None] = Depends(verify_session)
):
if session is None:
return JSONResponse(content={'status': 'session_expired'}, status_code=status.HTTP_401_UNAUTHORIZED)
# バッチ処理中の場合、機能を利用させない
if batch_status_service.is_batch_processing():
return JSONResponse(content={'status': 'batch_processing'}, status_code=status.HTTP_403_FORBIDDEN)
# 生物由来データと件数を取得
bio_sales_lot_data = bio_service.search_bio_data(bio_form)
bio_sales_lot_count = bio_service.count_bio_data(bio_form, session)
# レスポンスデータを加工
# 日付型のデータのエンコードエラーを解消するための措置
def custom_encode(obj):
encoded_obj = obj.model_dump()
for key, value in encoded_obj.items():
if type(value) == datetime.datetime:
encoded_obj[key] = value.strftime("%Y-%m-%d %H:%M:%S") if obj is not None else ''
if type(value) == datetime.date:
encoded_obj[key] = value.strftime("%Y-%m-%d") if obj is not None else ''
return encoded_obj
data = jsonable_encoder(
bio_sales_lot_data,
custom_encoder={
BioDisplayModel: custom_encode
}
)
# セッション書き換え
session.update(
actions=[
UserSession.last_access_time.set(UserSession.new_last_access_time()),
UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()),
# 検索結果をキャッシュする
UserSession.bio_search_condition.set(bio_form.model_dump()),
UserSession.bio_search_count.set(bio_sales_lot_count),
]
)
set_session(session)
json_response = JSONResponse(content={
'data': data,
'count': bio_sales_lot_count
})
# クッキーも書き換え
json_response.set_cookie(
key='session',
value=session.session_key,
max_age=environment.SESSION_EXPIRE_MINUTE * 60, # cookieの有効期限は秒数指定なので、60秒をかける
secure=True,
httponly=True
)
return json_response
@router.post('/download')
async def download_bio_data(
search_param: BioModel = Depends(BioModel.as_body),
@ -40,7 +103,7 @@ async def download_bio_data(
logger.info(f'ユーザーID: {download_param.user_id}')
logger.info(f'拡張子: {download_param.ext}')
# ファイル名に使用するタイムスタンプを初期化しておく
current_timestamp = datetime.now()
current_timestamp = datetime.datetime.now()
# 出力ファイル名
download_file_name = f'Result_{download_param.user_id}_{current_timestamp:%Y%m%d%H%M%S%f}.{download_param.ext}'
if session is None:
@ -50,7 +113,7 @@ async def download_bio_data(
return {'status': 'batch_processing'}
# 生物由来データを検索
# 検索に使用したクエリも取得
search_result_df, query = _search_bio_data(bio_service, search_param, download_param)
search_result_df, query = _search_download_bio_data(bio_service, search_param, download_param)
# アクセスログを記録
bio_service.write_access_log(query, search_param, download_param.user_id, current_timestamp, download_file_name)
@ -100,7 +163,7 @@ async def download_bio_data(
return json_response
def _search_bio_data(
def _search_download_bio_data(
bio_service: BioViewService,
search_param: BioModel,
download_param: BioDownloadModel

View File

@ -5,7 +5,7 @@ from fastapi.staticfiles import StaticFiles
from starlette import status
import src.static as static
from src.controller import (bio, bio_download, healthcheck, login, logout,
from src.controller import (bio, bio_api, healthcheck, login, logout,
master_mainte, menu, root, ultmarc)
from src.core import task
from src.error.exception_handler import http_exception_handler
@ -27,9 +27,9 @@ app.include_router(menu.router, prefix='/menu')
app.include_router(bio.router, prefix='/bio')
# アルトマークデータ照会のルーター
app.include_router(ultmarc.router, prefix='/ultmarc')
# 生物由来ダウンロード用APIルーター。
# 生物由来照会のAPIルーター。
# クライアントから非同期呼出しされるため、共通ルーターとは異なる扱いとする。
app.include_router(bio_download.router, prefix='/bio')
app.include_router(bio_api.router, prefix='/bio')
# マスタメンテ
app.include_router(master_mainte.router, prefix='/masterMainte')
# ヘルスチェック用のルーター

View File

@ -42,3 +42,7 @@ class BioSalesLotDBModel(BaseDBModel):
data_kind: Optional[str]
err_dtl_kind: Optional[str]
expr_dt: Optional[date]
class BioSalesLotCountDBModel(BaseDBModel):
count: Optional[int]

View File

@ -1,7 +1,8 @@
import datetime
import uuid
from pynamodb.attributes import NumberAttribute, UnicodeAttribute
from pynamodb.attributes import (JSONAttribute, NumberAttribute,
UnicodeAttribute)
from pynamodb.models import Model as DynamoDBTableModel
from src.system_var import environment
@ -23,6 +24,8 @@ class UserSession(DynamoDBTableModel):
csrf_token = UnicodeAttribute()
last_access_time = NumberAttribute()
record_expiration_time = NumberAttribute()
bio_search_condition = JSONAttribute(null=True, default=None)
bio_search_count = NumberAttribute(null=True, default=None)
@classmethod
def new_last_access_time(cls):

View File

@ -21,6 +21,8 @@ class BioModel(BaseModel):
rev_hsdnymd_srk_from: Optional[str]
rev_hsdnymd_srk_to: Optional[str]
iko_flg: Optional[str]
pageNumber: Optional[int]
pageSize: Optional[int]
@classmethod
def as_form(
@ -34,7 +36,9 @@ class BioModel(BaseModel):
ctrl_maker_cd: str = Form(None),
ctrl_rev_hsdnymd_srk_from: str = Form(None),
ctrl_rev_hsdnymd_srk_to: str = Form(None),
ikoFlg: str = Form(None)
ikoFlg: str = Form(None),
pageNumber: int = Form(None),
pageSize: int = Form(None)
):
return cls.__convert_request_param(
@ -48,7 +52,9 @@ class BioModel(BaseModel):
ctrl_maker_cd,
ctrl_rev_hsdnymd_srk_from,
ctrl_rev_hsdnymd_srk_to,
ikoFlg
ikoFlg,
pageNumber,
pageSize
)
@classmethod
@ -91,7 +97,9 @@ class BioModel(BaseModel):
ctrl_maker_cd: str,
ctrl_rev_hsdnymd_srk_from: str,
ctrl_rev_hsdnymd_srk_to: str,
ikoFlg: str
ikoFlg: str,
pageNumber: int = None,
pageSize: int = None
):
wholesaler_code = None
wholesaler_sub_code = None
@ -132,5 +140,7 @@ class BioModel(BaseModel):
mkr_cd=ctrl_maker_cd,
rev_hsdnymd_srk_from=rev_hsdnymd_srk_from,
rev_hsdnymd_srk_to=rev_hsdnymd_srk_to,
iko_flg=ikoFlg
iko_flg=ikoFlg,
pageNumber=pageNumber,
pageSize=pageSize
)

View File

@ -3,6 +3,6 @@ from src.util.sanitize import sanitize
@sanitize
class BisDisplayModel(BioSalesLotDBModel):
class BioDisplayModel(BioSalesLotDBModel):
def __init__(self, param: BioSalesLotDBModel) -> None:
super().__init__(**param.model_dump())

View File

@ -1,14 +1,9 @@
import json
from collections import OrderedDict
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
from src.model.db.pharmacy_product_master import PharmacyProductMasterModel
from src.model.db.wholesaler_master import WholesalerMasterModel
from src.model.request.bio import BioModel
from src.model.view.bio_disp_model import BisDisplayModel
from src.system_var import environment
@ -17,9 +12,8 @@ class BioViewModel(BaseModel):
user_id: str
whs_models: list[WholesalerMasterModel]
phm_models: list[PharmacyProductMasterModel]
bio_data: Optional[list[BisDisplayModel]] = None
form_data: BioModel = None
excel_max_lines: int = str(environment.BIO_EXCEL_RESULT_MAX_COUNT)
search_data_max_length: int = environment.BIO_SEARCH_RESULT_MAX_COUNT
excel_max_lines: str = str(environment.BIO_EXCEL_RESULT_MAX_COUNT)
def display_wholesaler_names(self):
display_names = [
@ -49,114 +43,3 @@ class BioViewModel(BaseModel):
'2': '除外'
}
)
def bio_data_json_str(self):
"""生物由来ロット分解データの検索結果を指定された件数ごとに分割しながら返す"""
def date_handler(obj):
"""json.dumpsの日付項目のフォーマットハンドラ"""
return obj.isoformat().replace('T', ' ') if hasattr(obj, 'isoformat') else obj
search_data_list = [model.model_dump() for model in self.bio_data]
search_data_len = len(search_data_list)
# 呼び出し一回あたりの分割数
part_size = 500
for i in range(0, search_data_len, part_size):
json_str = json.dumps(search_data_list[i:i + part_size], ensure_ascii=False, default=date_handler)
# JavaScriptに埋め込むため、バックスラッシュ、バッククォートをエスケープ
json_str = json_str.replace('\\', '\\\\')
json_str = json_str.replace("`", "\\`")
yield json_str
def make_whs_name(self):
if not self.is_form_submitted():
return ''
if self.form_data.rec_whs_cd is None:
return ''
form_wholesaler_full_name = \
f'{self.form_data.rec_whs_cd}-{self.form_data.rec_whs_sub_cd}:{self.form_data.whs_name}'
return form_wholesaler_full_name
def is_selected_whs_name(self, selected_wholesaler):
if not self.is_form_submitted():
return ''
form_wholesaler_full_name = \
f'{self.form_data.rec_whs_cd}-{self.form_data.rec_whs_sub_cd}:{self.form_data.whs_name}'
return self._selected_value(form_wholesaler_full_name, selected_wholesaler)
def is_selected_org_kbn(self, selected_org_kbn):
if not self.is_form_submitted():
return ''
return self._selected_value(self.form_data.slip_org_kbn, selected_org_kbn)
def is_input_rec_ymd_from(self):
if not self.is_form_submitted():
return ''
return self._format_date_string(self.form_data.rec_ymd_from)
def is_input_rec_ymd_to(self):
if not self.is_form_submitted():
return ''
return self._format_date_string(self.form_data.rec_ymd_to)
def is_input_lot_num(self):
if not self.is_form_submitted():
return ''
return self.form_data.rec_lot_num or ''
def is_selected_data_kbn(self, selected_data_kbn):
if not self.is_form_submitted():
return ''
return self._selected_value(self.form_data.data_kbn, selected_data_kbn)
def is_selected_maker_cd(self, selected_maker_cd):
if not self.is_form_submitted():
return ''
return self._selected_value(self.form_data.mkr_cd, selected_maker_cd)
def is_input_rev_hsdnymd_srk_from(self):
if not self.is_form_submitted():
return ''
return self._format_date_string(self.form_data.rev_hsdnymd_srk_from)
def is_input_rev_hsdnymd_srk_to(self):
if not self.is_form_submitted():
return ''
return self._format_date_string(self.form_data.rev_hsdnymd_srk_to)
def is_checked_iko_flg(self):
if not self.is_form_submitted():
return ''
return 'checked' if self.form_data.iko_flg else ''
def disabled_button(self):
return 'disabled' if self.is_data_empty() or self.is_data_overflow_max_length() else ''
def is_form_submitted(self):
return self.form_data is not None
def is_data_empty(self):
return self.bio_data is None or len(self.bio_data) == 0
def is_data_overflow_max_length(self):
return self.bio_data is None or len(self.bio_data) > environment.BIO_SEARCH_RESULT_MAX_COUNT
def _format_date_string(self, date_string):
if date_string is None:
return ''
date = datetime.strptime(date_string, '%Y%m%d')
return date.strftime('%Y/%m/%d')
def _selected_value(self, form_value: str, current_value: str):
return 'selected' if form_value == current_value else ''

View File

@ -1,7 +1,8 @@
from src.db import sql_condition as condition
from src.db.sql_condition import SQLCondition
from src.logging.get_logger import get_logger
from src.model.db.bio_sales_lot import BioSalesLotDBModel
from src.model.db.bio_sales_lot import (BioSalesLotCountDBModel,
BioSalesLotDBModel)
from src.model.request.bio import BioModel
from src.repositories.base_repository import BaseRepository
from src.system_var import environment
@ -68,12 +69,12 @@ class BioSalesLotRepository(BaseRepository):
try:
logger.debug('DB参照実行')
where_clause = self.__build_condition(parameter)
# システムとしての最大取得件数 +1 まで取る
query = self.FETCH_SQL.format(where_clause=where_clause, limit=environment.BIO_SEARCH_RESULT_MAX_COUNT + 1)
# ページングで取得するため、LIMIT,OFFSETを指定
limit_clause = self.__build_paging_limit_clauses(parameter)
query = self.FETCH_SQL.format(where_clause=where_clause, limit=limit_clause)
logger.debug(f'SQL: {query}')
result = self._database.execute_select(query, parameter.model_dump())
models = [BioSalesLotDBModel(**r) for r in result]
logger.debug(f'count= {len(models)}')
return models
except Exception as e:
logger.exception(f"DB Error : Exception={e.args}")
@ -93,6 +94,35 @@ class BioSalesLotRepository(BaseRepository):
logger.exception(f"DB Error : Exception={e.args}")
raise e
COUNT_SQL = """\
SELECT
COUNT(*) AS count
FROM
(
SELECT 1
FROM src05.bio_sales_lot
WHERE
{where_clause}
LIMIT {limit}
) AS t\
"""
def fetch_count(self, parameter: BioModel) -> list[BioSalesLotDBModel]:
try:
logger.debug('DB参照実行')
where_clause = self.__build_condition(parameter)
# システムとしての最大取得件数 + 1まで取る
query = self.COUNT_SQL.format(where_clause=where_clause, limit=environment.BIO_SEARCH_RESULT_MAX_COUNT + 1)
logger.debug(f'SQL: {query}')
result = self._database.execute_select(query, parameter.model_dump())
models = [BioSalesLotCountDBModel(**r) for r in result]
count = models[0].count if len(models) > 0 else 0
logger.debug(f'count= {count}')
return count
except Exception as e:
logger.exception(f"DB Error : Exception={e.args}")
raise e
def __build_condition(self, parameter: BioModel):
where_clauses: list[SQLCondition] = []
@ -142,3 +172,9 @@ class BioSalesLotRepository(BaseRepository):
logger.debug(f'条件設定終了:{where_clauses_str}')
return where_clauses_str
def __build_paging_limit_clauses(self, parameter: BioModel) -> str:
page_size = parameter.pageSize or 0
page_number = parameter.pageNumber or 0
return f'{page_size} OFFSET {(page_number - 1) * page_size}'

View File

@ -11,7 +11,7 @@ from src.aws.s3 import S3Client
from src.logging.get_logger import get_logger
from src.model.internal.session import UserSession
from src.model.request.bio import BioModel
from src.model.view.bio_disp_model import BisDisplayModel
from src.model.view.bio_disp_model import BioDisplayModel
from src.model.view.bio_view_model import BioViewModel
from src.repositories.base_repository import BaseRepository
from src.repositories.bio_sales_lot_repository import BioSalesLotRepository
@ -65,12 +65,23 @@ class BioViewService(BaseService):
def search_bio_data(self, search_params: BioModel):
# 生物由来データを検索
bio_sales_view_data = self.bio_sales_repository.fetch_many(parameter=search_params)
bio_sales_lot_data = self.bio_sales_repository.fetch_many(parameter=search_params)
# 画面表示用に加工
display_bio_data: list[BisDisplayModel] = [BisDisplayModel(data) for data in bio_sales_view_data]
display_bio_data: list[BioDisplayModel] = [BioDisplayModel(data) for data in bio_sales_lot_data]
return display_bio_data
def count_bio_data(self, search_params: BioModel, session: UserSession) -> int:
# 検索値が前回検索時と変更がない場合、キャッシュした件数を返す
previous_search_params = session.bio_search_condition
current_search_params = search_params.model_dump()
if previous_search_params == current_search_params:
return session.bio_search_count
# 生物由来データの件数をDBから取得
bio_sales_data_count = self.bio_sales_repository.fetch_count(parameter=search_params)
return bio_sales_data_count
def search_download_bio_data(
self,
search_params: BioModel,

View File

@ -28,18 +28,16 @@
</td>
</tr>
</table>
<form class="_form _border" id="bio_search" name="search" action="/bio/BioSearchList" method="POST" onsubmit="showLoading('_loading_for_other')">
<form class="_form _border" id="bio_search" name="search">
<table class="search_table">
<tbody>
<tr>
<td>卸:</td>
<td class="search_tb" id="oroshi_dd">
<select class="text search_dropdown" name="ctrl_wholesaler" value="" onChange="formBtDisabled();">
<select class="text search_dropdown" name="ctrl_wholesaler" value="" onChange="formBtDisabled();applySearchParam(this)">
<option value=""></option>
{% for whs_name in bio.display_wholesaler_names() %}
<option
value="{{whs_name}}"
{{bio.is_selected_whs_name(whs_name)}}>
<option value="{{whs_name}}">
{{whs_name}}
</option>
{% endfor %}
@ -47,22 +45,24 @@
</td>
<td>データ種別:</td>
<td class="search_tb">
<select class="text search_dropdown" name="ctrl_org_kbn" onChange="formBtDisabled();" value="">
<select class="text search_dropdown" name="ctrl_org_kbn" onChange="formBtDisabled();applySearchParam(this)" value="">
{% for org_kbn_code, org_kbn_value in bio.display_org_kbn().items() %}
<option value="{{org_kbn_code}}" {{bio.is_selected_org_kbn(org_kbn_code)}} >{{org_kbn_value}}</option>
<option value="{{org_kbn_code}}">
{{org_kbn_value}}
</option>
{% endfor %}
</select>
</td>
<td>処理日:</td>
<td colspan="2">
<input type="text" id="shoribi_start" class="date_picker" name="ctrl_rec_ymd_from" maxlength="10"
value="{{bio.is_input_rec_ymd_from()}}"
onchange="formBtDisabled()"
value=""
onchange="formBtDisabled();applySearchParam(this)"
>
<input type="text" id="shoribi_end" class="date_picker" name="ctrl_rec_ymd_to" maxlength="10"
value="{{bio.is_input_rec_ymd_to()}}"
onchange="formBtDisabled()"
value=""
onchange="formBtDisabled();applySearchParam(this)"
>
</td>
</tr>
@ -70,26 +70,27 @@
<td>ロット番号:</td>
<td class="search_tb">
<input class="text" type="text" id="lot_tb" name="ctrl_rec_lot_num" style="ime-mode:disabled" maxlength="10"
value="{{bio.is_input_lot_num()}}"
oninput="checkSpaceForm(this); checkAimaiSearhForm(this); formBtDisabled()">
value=""
oninput="checkSpaceForm(this); checkAimaiSearhForm(this); formBtDisabled();applySearchParam(this)">
</td>
<td>データ区分:</td>
<td class="search_tb">
<select class="text search_dropdown" name="ctrl_data_kbn" onchange="formBtDisabled()">
<select class="text search_dropdown" name="ctrl_data_kbn" onchange="formBtDisabled();applySearchParam(this)">
{% for data_kbn_code, data_kbn_value in bio.display_data_kbn().items() %}
<option value="{{data_kbn_value}}" {{bio.is_selected_data_kbn(data_kbn_value)}} >{{data_kbn_value}}</option>
<option option value="{{data_kbn_value}}">
{{data_kbn_value}}
</option>
{% endfor %}
</select>
</td>
<td>製品:</td>
<td class="search_tb" id="seihin_dd">
<select class="text search_dropdown" name="ctrl_maker_cd" value="" onChange="formBtDisabled();">
<select class="text search_dropdown" name="ctrl_maker_cd" value="" onChange="formBtDisabled();applySearchParam(this);">
<option value=""></option>
{% for phm in bio.phm_models %}
<option
value="{{phm['mkr_cd']}}" {{bio.is_selected_maker_cd(phm['mkr_cd'])}}>
{{phm['mkr_cd_name']}}
</option>
<option value="{{phm['mkr_cd']}}">
{{phm['mkr_cd_name']}}
</option>
{% endfor %}
</select>
</td>
@ -98,22 +99,22 @@
<td>発伝年月日:</td>
<td colspan="3">
<input type="text" id="hsdnymd_start" class="date_picker" name="ctrl_rev_hsdnymd_srk_from" maxlength="10"
value="{{bio.is_input_rev_hsdnymd_srk_from()}}"
onchange="formBtDisabled()"
value=""
onchange="formBtDisabled();applySearchParam(this)"
>
<input type="text" id="hsdnymd_end" class="date_picker" name="ctrl_rev_hsdnymd_srk_to" maxlength="10"
value="{{bio.is_input_rev_hsdnymd_srk_to()}}"
onchange="formBtDisabled()"
value=""
onchange="formBtDisabled();applySearchParam(this)"
>
</td>
<td colspan="2">
<input type="checkbox" id="ikoFlg" name="ikoFlg" value="true" {{bio.is_checked_iko_flg()}}>
<input type="checkbox" id="ikoFlg" name="ikoFlg" value="true" oninput="applySearchParam(this)">
<label for="ikoFlg">2017年11月以前のデータを含める</label>
</td>
<td>
<input class="buttonSize" id="clear" type="button" name="clear_bt" value="クリア" onclick="clr()">
<input class="buttonSize" id="search_bt" value="検索" type="submit">
<input class="buttonSize" id="clear" type="button" name="clear_bt" value="クリア" onclick="clr();clearHidden('bioSearchHidden')">
<input class="buttonSize" id="search_bt" value="検索" type="button" onclick="searchBioList()">
</td>
</tr>
</tbody>
@ -121,9 +122,9 @@
</form>
<!--検索結果-->
<form class="_form" id="searchResult" name="searchResult">
<input type="button" id="outExcel" name="outExcel" value="Excel出力" {{bio.disabled_button()}}
<input type="button" id="outExcel" name="outExcel" value="Excel出力" disabled
data-bs-toggle="modal" data-bs-target="#modal_xlsx" data-bs-message="生物由来卸販売データ一覧をExcel出力しますか"/>
<input type="button" id="outCSV" name="outCSV" value="CSV出力" {{bio.disabled_button()}}
<input type="button" id="outCSV" name="outCSV" value="CSV出力" disabled
data-bs-toggle="modal" data-bs-target="#modal_csv" data-bs-message="生物由来卸販売データ一覧をCSV出力しますか" />
<!--ページネーション-->
<div id="light-pagination" class="pagination"></div>
@ -173,50 +174,62 @@
</thead>
<tbody id="result_data" class="result_data"></tbody>
</table>
{% if bio.is_form_submitted() and bio.is_data_overflow_max_length() %}
<div class="resultAreaMsg">
検索結果が最大件数を超えました。検索条件を見直しして下さい。
</div>
{% endif %}
{% if bio.is_form_submitted() and bio.is_data_empty() %}
<div class="resultAreaMsg">
対象のデータが存在しません
</div>
{% endif %}
<div id="message_area" class="resultAreaMsg"></div>
</div>
</form>
<form id="bio_download">
<input type="hidden" name="ctrl_wholesaler" value="{{bio.make_whs_name()}}">
<input type="hidden" name="ctrl_org_kbn" value="{{bio.form_data.slip_org_kbn or ''}}">
<input type="hidden" name="ctrl_rec_ymd_from" value="{{bio.is_input_rec_ymd_from()}}">
<input type="hidden" name="ctrl_rec_ymd_to" value="{{bio.is_input_rec_ymd_to()}}">
<input type="hidden" name="ctrl_rec_lot_num" value="{{bio.is_input_lot_num()}}">
<input type="hidden" name="ctrl_data_kbn" value="{{bio.form_data.data_kbn or ''}}">
<input type="hidden" name="ctrl_maker_cd" value="{{bio.form_data.mkr_cd or ''}}">
<input type="hidden" name="ctrl_rev_hsdnymd_srk_from" value="{{bio.is_input_rev_hsdnymd_srk_from()}}">
<input type="hidden" name="ctrl_rev_hsdnymd_srk_to" value="{{bio.is_input_rev_hsdnymd_srk_to()}}">
<input type="checkbox" name="ikoFlg" value="true" {{bio.is_checked_iko_flg()}} style="display: none;">
<form id="search_param_hidden" name="bioSearchHidden">
<input type="hidden" name="ctrl_wholesaler" value="">
<input type="hidden" name="ctrl_org_kbn" value="">
<input type="hidden" name="ctrl_rec_ymd_from" value="">
<input type="hidden" name="ctrl_rec_ymd_to" value="">
<input type="hidden" name="ctrl_rec_lot_num" value="">
<input type="hidden" name="ctrl_data_kbn" value="">
<input type="hidden" name="ctrl_maker_cd" value="">
<input type="hidden" name="ctrl_rev_hsdnymd_srk_from" value="">
<input type="hidden" name="ctrl_rev_hsdnymd_srk_to" value="">
<input type="checkbox" name="ikoFlg" value="true" style="display: none;">
</form>
<form id="download_hidden" name="bioDownloadHidden">
<input type="hidden" name="ctrl_wholesaler" value="">
<input type="hidden" name="ctrl_org_kbn" value="">
<input type="hidden" name="ctrl_rec_ymd_from" value="">
<input type="hidden" name="ctrl_rec_ymd_to" value="">
<input type="hidden" name="ctrl_rec_lot_num" value="">
<input type="hidden" name="ctrl_data_kbn" value="">
<input type="hidden" name="ctrl_maker_cd" value="">
<input type="hidden" name="ctrl_rev_hsdnymd_srk_from" value="">
<input type="hidden" name="ctrl_rev_hsdnymd_srk_to" value="">
<input type="checkbox" name="ikoFlg" value="true" style="display: none;">
</form>
<!-- CSV/Excelダウンロード処理-->
<script type="text/javascript">
function clearHidden(hiddenName) {
// 非表示項目をクリア
const formElement = document[hiddenName]
const formInputElements = Array.from(formElement.elements)
for (const formInput of formInputElements) {
if (formInput.name.startsWith('ctrl_')) {
formInput.value = "";
}
if (formInput.type === 'checkbox') {
formInput.checked = false;
}
}
}
// CSV/Excelダウンロード処理
function download(ext) {
// ローディング開始
const loading = new Loading();
loading.start();
// 検索パラメータを取得
const formData = $('#bio_download').serializeArray()
// リクエスト用に加工
const searchParams = {}
for (let i = 0; i < formData.length; i++) {
searchParams[formData[i].name] = formData[i].value
}
// ダウンロード固有のパラメータを設定
const downloadRequestParams = {
user_id: '{{bio.user_id}}',
ext: ext,
}
// 検索パラメータを取得
const searchParams = createSearchParams('download_hidden')
$.extend(downloadRequestParams, searchParams)
$.ajax({
@ -229,7 +242,7 @@
success: function(data) {
try {
if (data.status === 'batch_processing') {
location.href='/logout/?reason=batchProcessing';
location.href='/logout/?reason=batch_processing';
return
}
@ -272,13 +285,41 @@
});
}
// <! --ページネーションの作成-- >
$(function() {
const searchResultData = generateSearchResult();
if (searchResultData.length == 0) return;
function createSearchParams(paramId) {
// 検索パラメータを取得
const formData = $(`#${paramId}`).serializeArray()
// リクエスト用に加工
const searchParams = {}
for (let i = 0; i < formData.length; i++) {
searchParams[formData[i].name] = formData[i].value
}
$(".pagination").pagination({
dataSource: searchResultData,
return searchParams
}
function applySearchParam(elem) {
const bioSearchHiddenForm = document['bioSearchHidden']
bioSearchHiddenForm[elem.name].value = elem.value
if (elem.type === 'checkbox') {
bioSearchHiddenForm[elem.name].checked = elem.checked
}
}
// 生物由来データの検索&ページネーションの作成
function searchBioList() {
const messageArea = $('#message_area')
messageArea.text('')
messageArea.hide()
const loading = new Loading('_loading_for_other')
const searchParams = createSearchParams('search_param_hidden')
$('.pagination').pagination({
dataSource: '/bio/search',
locator: 'data',
totalNumberLocator: function(response) {
// you can return totalNumber by analyzing response content
return response.count
},
pageNumber: 1, // 初期ページ番号
pageSize: 100, //表示するコンテンツ数
pageRange: 2, //選択されているページネーション番号の両隣に表示する個数
@ -287,8 +328,73 @@
nextText: 'Next', //「次へ」の文字。エスケープ文字
showNavigator: true,
formatNavigator: '件数: <%= totalNumber %>件 ページ数: <%= totalPage %>',
ajax: {
type: 'POST',
data: searchParams,
async: true,
beforeSend: function() {
loading.start()
}
},
formatAjaxError: function(jqXHR, textStatus, errorThrown) {
loading.stop()
const responseJson = jqXHR.responseJSON
if (!responseJson) {
$(`#ErrorModal_Unexpected`).modal('toggle')
return
}
const status = responseJson?.status
if (status === 'batch_processing') {
location.href='/logout/?reason=batch_processing';
return
}
if (status === 'session_expired') {
location.href='/logout/?reason=session_expired';
return
}
},
callback: function(data, pagination) {
// ダウンロード用の非表示項目に検索値を埋め込む
const bioSearchHiddenForm = document['bioSearchHidden']
const bioDownloadHiddenForm = document['bioDownloadHidden']
for (const key of Object.keys(searchParams)) {
const elem = bioDownloadHiddenForm[key]
elem.value = searchParams[key]
if (elem.type === 'checkbox') {
elem.checked = searchParams[key] === 'true' ? true : false
}
}
$('#result_data').html('')
if (pagination.totalNumber === 0) {
loading.stop()
messageArea.text('対象のデータが存在しません')
messageArea.show()
$('.pagination').pagination('hide')
$('#outExcel').attr('disabled', 'disabled')
$('#outCSV').attr('disabled', 'disabled')
return
}
if (pagination.totalNumber > bioDataOverflowMaxLength()) {
loading.stop()
messageArea.text('検索結果が最大件数を超えました。検索条件を見直しして下さい。')
messageArea.show()
$('.pagination').pagination('hide')
$('#outExcel').attr('disabled', 'disabled')
$('#outCSV').attr('disabled', 'disabled')
return
}
messageArea.hide()
$('.pagination').pagination('show')
$('#result_data').html(pagination_content(data))
loading.stop()
$('#outExcel').removeAttr('disabled')
$('#outCSV').removeAttr('disabled')
$('.paginationjs-pages > ul > li').not('.disabled,.active').each(function(index, val) {
// paginationにtabindexをつける
$(val).attr('tabindex', '0')
@ -303,11 +409,16 @@
})
})
// ページ送りしたときにヘッダがずれるのを修正
FixedMidashi.remove();
FixedMidashi.create();
FixedMidashi.remove()
FixedMidashi.create()
}
})
});
}
function bioDataOverflowMaxLength() {
const maxlength = '{{bio.search_data_max_length}}'
return Number(maxlength)
}
function pagination_content(datas) {
const display_keys = [
@ -364,20 +475,6 @@
})
}
function generateSearchResult(){
const searchResultData = []
// {% if bio.is_form_submitted() and not (bio.is_data_overflow_max_length() or bio.is_data_empty()) %}
// {% autoescape False%}
// ジェネレータですこしずつ取得してリストに詰める
// {% for bio_data_json_str in bio.bio_data_json_str() %}
// unicode制御文字をエスケープしてからJSON.parseする
searchResultData.push(...JSON.parse(`{{bio_data_json_str}}`.replace(/[\u0000-\u001F]+/g, " ")))
// {% endfor %}
// {% endautoescape%}
// {% endif %}
return searchResultData
}
</script>
<!-- Excel出力モーダル -->