diff --git a/Dockerfile.gdal b/Dockerfile.gdal index c30d958..0b61846 100644 --- a/Dockerfile.gdal +++ b/Dockerfile.gdal @@ -39,6 +39,7 @@ RUN apt-get update && apt-get install -y \ python3-pip RUN pip install --upgrade pip +RUN pip install -e ./SumasenLibs/excel_lib RUN apt-get update diff --git a/rog/postgres_views.sql b/rog/postgres_views.sql index 40c3075..089e538 100644 --- a/rog/postgres_views.sql +++ b/rog/postgres_views.sql @@ -42,11 +42,9 @@ WHERE -- マテリアライズドビューの作成 -- マテリアライズドビューの再作成 -DROP MATERIALIZED VIEW IF EXISTS mv_entry_details; - CREATE MATERIALIZED VIEW mv_entry_details AS SELECT - -- エントリー基本情報 + -- 既存のフィールド e.id, CAST(e.zekken_number AS TEXT) as zekken_number, e.is_active, @@ -86,6 +84,19 @@ SELECT COALESCE(cs.total_checkins, 0) as checkin_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.total_participants, @@ -123,6 +134,8 @@ FROM AND CAST(e.zekken_number AS TEXT) = cs.zekken_number 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_goalimages gi ON ev.event_name = gi.event_code + AND CAST(e.zekken_number AS TEXT) = gi.zekken_number GROUP BY 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_checkins, cs.purchase_count, cs.last_checkin, 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 ON mv_entry_details(event_name, zekken_number); -- ビューの更新 -REFRESH MATERIALIZED VIEW mv_entry_details; \ No newline at end of file +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); +*/ \ No newline at end of file diff --git a/rog/views.py b/rog/views.py index 164921b..ee287cc 100644 --- a/rog/views.py +++ b/rog/views.py @@ -91,6 +91,8 @@ from django.urls import get_resolver import os import json +from sumaexcel import SumasenExcel + logger = logging.getLogger(__name__) @api_view(['PATCH']) @@ -2605,41 +2607,32 @@ def update_checkins(request): @api_view(['GET']) 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() - workbook = xlsxwriter.Workbook(output) - worksheet = workbook.add_worksheet('通過証明書') + # 初期化 + variables = { + "zekken_number":sekken_number, + "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({ - 'bold': True, - 'bg_color': '#CCCCCC', - 'border': 1 - }) + # シート初期化 + ret = excel.make_report(variables=variables) + if ret["status"]==True: + filepath=ret["filepath"] + 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) @@ -2647,7 +2640,7 @@ def export_excel(request, zekken_number): output.read(), 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 # ----- for Supervisor ----- diff --git a/templates/.DS_Store b/templates/.DS_Store index 9a5fa71..6309652 100644 Binary files a/templates/.DS_Store and b/templates/.DS_Store differ