Merge remote-tracking branch 'origin/extdb-3' into extdb-3
This commit is contained in:
@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
python3-pip
|
python3-pip
|
||||||
|
|
||||||
RUN pip install --upgrade pip
|
RUN pip install --upgrade pip
|
||||||
|
RUN pip install -e ./SumasenLibs/excel_lib
|
||||||
|
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
|
|
||||||
|
|||||||
@ -42,11 +42,9 @@ WHERE
|
|||||||
|
|
||||||
-- マテリアライズドビューの作成
|
-- マテリアライズドビューの作成
|
||||||
-- マテリアライズドビューの再作成
|
-- マテリアライズドビューの再作成
|
||||||
DROP MATERIALIZED VIEW IF EXISTS mv_entry_details;
|
|
||||||
|
|
||||||
CREATE MATERIALIZED VIEW mv_entry_details AS
|
CREATE MATERIALIZED VIEW mv_entry_details AS
|
||||||
SELECT
|
SELECT
|
||||||
-- エントリー基本情報
|
-- 既存のフィールド
|
||||||
e.id,
|
e.id,
|
||||||
CAST(e.zekken_number AS TEXT) as zekken_number,
|
CAST(e.zekken_number AS TEXT) as zekken_number,
|
||||||
e.is_active,
|
e.is_active,
|
||||||
@ -86,6 +84,19 @@ SELECT
|
|||||||
COALESCE(cs.total_checkins, 0) as checkin_count,
|
COALESCE(cs.total_checkins, 0) as checkin_count,
|
||||||
COALESCE(cs.purchase_count, 0) as purchase_count,
|
COALESCE(cs.purchase_count, 0) as purchase_count,
|
||||||
|
|
||||||
|
-- ゴール情報
|
||||||
|
gi.goalimage as goal_image,
|
||||||
|
gi.goaltime as goal_time,
|
||||||
|
|
||||||
|
-- 完走状態の判定を追加
|
||||||
|
CASE
|
||||||
|
WHEN gi.goaltime IS NULL THEN '棄権'
|
||||||
|
WHEN gi.goaltime <= ev.end_datetime THEN '完走'
|
||||||
|
WHEN gi.goaltime > ev.end_datetime AND
|
||||||
|
gi.goaltime <= ev.end_datetime + INTERVAL '15 minutes' THEN '完走(遅刻)'
|
||||||
|
ELSE '失格'
|
||||||
|
END as validation,
|
||||||
|
|
||||||
-- ランキング情報
|
-- ランキング情報
|
||||||
cr.ranking as category_rank,
|
cr.ranking as category_rank,
|
||||||
cr.total_participants,
|
cr.total_participants,
|
||||||
@ -123,6 +134,8 @@ FROM
|
|||||||
AND CAST(e.zekken_number AS TEXT) = cs.zekken_number
|
AND CAST(e.zekken_number AS TEXT) = cs.zekken_number
|
||||||
LEFT JOIN v_category_rankings cr ON e.id = cr.id
|
LEFT JOIN v_category_rankings cr ON e.id = cr.id
|
||||||
LEFT JOIN rog_member m ON t.id = m.team_id
|
LEFT JOIN rog_member m ON t.id = m.team_id
|
||||||
|
LEFT JOIN rog_goalimages gi ON ev.event_name = gi.event_code
|
||||||
|
AND CAST(e.zekken_number AS TEXT) = gi.zekken_number
|
||||||
|
|
||||||
GROUP BY
|
GROUP BY
|
||||||
e.id, e.zekken_number, e.is_active, e."hasParticipated", e."hasGoaled", e.date,
|
e.id, e.zekken_number, e.is_active, e."hasParticipated", e."hasGoaled", e.date,
|
||||||
@ -134,11 +147,43 @@ GROUP BY
|
|||||||
cs.total_points, cs.normal_points, cs.bonus_points, cs.penalty_points,
|
cs.total_points, cs.normal_points, cs.bonus_points, cs.penalty_points,
|
||||||
cs.total_checkins, cs.purchase_count, cs.last_checkin,
|
cs.total_checkins, cs.purchase_count, cs.last_checkin,
|
||||||
cr.ranking, cr.total_participants,
|
cr.ranking, cr.total_participants,
|
||||||
e.owner_id; -- オーナーIDをGROUP BYに追加
|
gi.goalimage, gi.goaltime,
|
||||||
|
e.owner_id;
|
||||||
|
|
||||||
-- インデックスの再作成
|
-- インデックスの再作成
|
||||||
CREATE UNIQUE INDEX idx_mv_entry_details_event_zekken
|
CREATE UNIQUE INDEX idx_mv_entry_details_event_zekken
|
||||||
ON mv_entry_details(event_name, zekken_number);
|
ON mv_entry_details(event_name, zekken_number);
|
||||||
|
|
||||||
-- ビューの更新
|
-- ビューの更新
|
||||||
REFRESH MATERIALIZED VIEW mv_entry_details;
|
REFRESH MATERIALIZED VIEW mv_entry_details;
|
||||||
|
|
||||||
|
|
||||||
|
-- チェックインと位置情報を結合したビューを作成
|
||||||
|
DROP VIEW IF EXISTS v_checkins_locations CASCADE;
|
||||||
|
CREATE OR REPLACE VIEW v_checkins_locations AS
|
||||||
|
SELECT
|
||||||
|
g.event_code,
|
||||||
|
g.zekken_number,
|
||||||
|
g.path_order,
|
||||||
|
g.cp_number,
|
||||||
|
l.sub_loc_id,
|
||||||
|
l.location_name,
|
||||||
|
l.photos,
|
||||||
|
g.image_address,
|
||||||
|
g.create_at,
|
||||||
|
g.buy_flag,
|
||||||
|
g.validate_location,
|
||||||
|
g.points
|
||||||
|
FROM
|
||||||
|
gps_checkins g
|
||||||
|
LEFT JOIN rog_location l ON g.cp_number = l.cp
|
||||||
|
ORDER BY
|
||||||
|
g.event_code,
|
||||||
|
g.zekken_number,
|
||||||
|
g.path_order;
|
||||||
|
|
||||||
|
-- インデックスのサジェスチョン(実際のテーブルに適用する必要があります)
|
||||||
|
/*
|
||||||
|
CREATE INDEX idx_gps_checkins_cp_number ON gps_checkins(cp_number);
|
||||||
|
CREATE INDEX idx_rog_location_cp ON rog_location(cp);
|
||||||
|
*/
|
||||||
57
rog/views.py
57
rog/views.py
@ -91,6 +91,8 @@ from django.urls import get_resolver
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from sumaexcel import SumasenExcel
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@api_view(['PATCH'])
|
@api_view(['PATCH'])
|
||||||
@ -2605,41 +2607,32 @@ def update_checkins(request):
|
|||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
def export_excel(request, zekken_number):
|
def export_excel(request, zekken_number):
|
||||||
# エントリー情報の取得
|
|
||||||
entry = Entry.objects.select_related('team','event').get(zekken_number=zekken_number)
|
|
||||||
checkins = GpsCheckin.objects.filter(zekken_number=zekken_number).order_by('path_order')
|
|
||||||
|
|
||||||
# Excelファイルの生成
|
# 初期化
|
||||||
output = BytesIO()
|
variables = {
|
||||||
workbook = xlsxwriter.Workbook(output)
|
"zekken_number":sekken_number,
|
||||||
worksheet = workbook.add_worksheet('通過証明書')
|
"event_code":request["FC岐阜"],
|
||||||
|
"db":"rogdb",
|
||||||
|
"username":"admin",
|
||||||
|
"password":"admin123456",
|
||||||
|
"host":"localhost",
|
||||||
|
"port":"5432"
|
||||||
|
}
|
||||||
|
excel = SumasenExcel(document="test", variables=variables, docbase="./docbase")
|
||||||
|
# ./docbase/certificate.ini の定義をベースに、
|
||||||
|
# ./docbase/certificate_template.xlsxを読み込み
|
||||||
|
# ./docbase/certificate_(zekken_number).xlsxを作成する
|
||||||
|
|
||||||
# スタイルの定義
|
# シート初期化
|
||||||
header_format = workbook.add_format({
|
ret = excel.make_report(variables=variables)
|
||||||
'bold': True,
|
if ret["status"]==True:
|
||||||
'bg_color': '#CCCCCC',
|
filepath=ret["filepath"]
|
||||||
'border': 1
|
logging.info(f"Excelファイル作成 : ret.filepath={filepath}")
|
||||||
})
|
else:
|
||||||
|
message = ret.get("message", "No message provided")
|
||||||
|
logging.error(f"Excelファイル作成失敗 : ret.message={message}")
|
||||||
|
|
||||||
# ヘッダー情報の書き込み
|
|
||||||
worksheet.write('A1', 'チーム名', header_format)
|
|
||||||
worksheet.write('B1', entry.team.team_name)
|
|
||||||
worksheet.write('A2', 'ゼッケン番号', header_format)
|
|
||||||
worksheet.write('B2', zekken_number)
|
|
||||||
|
|
||||||
# チェックインデータの書き込み
|
|
||||||
headers = ['順序', 'CP番号', 'チェックイン時刻', '検証', 'ポイント']
|
|
||||||
for col, header in enumerate(headers):
|
|
||||||
worksheet.write(3, col, header, header_format)
|
|
||||||
|
|
||||||
for row, checkin in enumerate(checkins, start=4):
|
|
||||||
worksheet.write(row, 0, checkin.path_order)
|
|
||||||
worksheet.write(row, 1, checkin.cp_number)
|
|
||||||
worksheet.write(row, 2, checkin.create_at.strftime('%Y-%m-%d %H:%M:%S'))
|
|
||||||
worksheet.write(row, 3, '✓' if checkin.validate_location else '')
|
|
||||||
worksheet.write(row, 4, checkin.points)
|
|
||||||
|
|
||||||
workbook.close()
|
|
||||||
|
|
||||||
# レスポンスの生成
|
# レスポンスの生成
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
@ -2647,7 +2640,7 @@ def export_excel(request, zekken_number):
|
|||||||
output.read(),
|
output.read(),
|
||||||
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
)
|
)
|
||||||
response['Content-Disposition'] = f'attachment; filename=通過証明書_{zekken_number}.xlsx'
|
response['Content-Disposition'] = f'attachment; filename=./docbase/certificate_{zekken_number}.xlsx'
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# ----- for Supervisor -----
|
# ----- for Supervisor -----
|
||||||
|
|||||||
BIN
templates/.DS_Store
vendored
BIN
templates/.DS_Store
vendored
Binary file not shown.
Reference in New Issue
Block a user