diff --git a/rog/views.py b/rog/views.py index c686b88..164921b 100644 --- a/rog/views.py +++ b/rog/views.py @@ -2406,11 +2406,11 @@ def get_team_info(request, zekken_number): # start_datetime = -1(ロゲ開始)のcreate_at logger.debug(f"self.rogaining={entry.event.self_rogaining} => start_datetime = -1(ロゲ開始)のcreate_at") - # チームのゴール時間を取得 + # チームの最初のゴール時間を取得 goal_record = GoalImages.objects.filter( team_name=entry.team.team_name, event_code=entry.event.event_name - ).order_by('-goaltime').first() + ).order_by('goaltime').first() # Nullチェックを追加してからログ出力 goalimage_url = None @@ -2771,7 +2771,7 @@ def update_goal_time(request): # 既存の記録を更新 goal_record.goaltime = goal_time goal_record.save() - logger.info(f"Updated goal time for team {team_name} in event {event_code}") + logger.info(f"Updated goal time as {goal_time} for team {team_name} in event {event_code}") else: # 新しい記録を作成 entry = Entry.objects.get(zekken_number=zekken_number, event__event_name=event_code) diff --git a/supervisor/html/index.html b/supervisor/html/index.html index 6b795d6..e86d25d 100755 --- a/supervisor/html/index.html +++ b/supervisor/html/index.html @@ -4,9 +4,10 @@ スーパーバイザーパネル + - +
@@ -170,7 +171,7 @@ // Excel出力ボタンの処理 document.getElementById('exportButton').addEventListener('click', exportExcel); - console.log('Page loaded, attempting to load events...'); + //console.log('Page loaded, attempting to load events...'); // 初期データ読み込み loadEventCodes(); }); @@ -194,7 +195,8 @@ if (display.textContent && display.textContent !== '-') { try { const date = new Date(display.textContent); - input.value = date.toISOString().slice(0, 19); + const jstDate = new Date(date.getTime() + (9 * 60 * 60 * 1000)); + input.value = jstDate.toISOString().slice(0, 19); } catch (e) { console.error('Error parsing date:', e); } @@ -302,13 +304,13 @@ const timeDiff = (newTime - startTime) / 1000; // 分単位の差 const maxTime = team.duration + 15*60; // 制限時間+15分 - console.info('startTime=',startTime,',goalTime=',newTime,',timeDiff=',timeDiff,'duration=',team.duration,',maxTime=',maxTime); + //console.info('startTime=',startTime,',goalTime=',newTime,',timeDiff=',timeDiff,'duration=',team.duration,',maxTime=',maxTime); updateValidation(timeDiff, maxTime ); // 1秒でも遅刻すると、1分につき-50点 const overtime = ((newTime - startTime) / 1000 - team.duration ); if( overtime>0 ){ - console.info('overtime=',overtime); + //console.info('overtime=',overtime); late_point = Math.ceil(overtime/60)*(-50); lateElement = document.getElementById('latePoints'); lateElement.textContent = late_point; @@ -333,7 +335,7 @@ // 判定の更新を行う補助関数 function updateValidation(timeDiff, maxTime) { - console.log('updateValidation',timeDiff,' > ',maxTime) + //console.log('updateValidation',timeDiff,' > ',maxTime) const validateElement = document.getElementById('validate'); if (validateElement) { if (timeDiff > maxTime) { @@ -458,23 +460,23 @@ goalTimeDisplay.onclick = () => editGoalTime(goalTimeDisplay); } - console.info("step 0"); + //console.info("step 0"); // ゴール時計の表示を更新 const goalTimeElement = document.getElementById('goalTime'); if (teamData.goal_photo) { // 画像要素を作成 - console.info("step 1"); + //console.info("step 1"); const img = document.createElement('img'); img.src = teamData.goal_photo; img.classList.add('h-32', 'w-auto', 'object-contain', 'cursor-pointer'); img.onclick = () => showLargeImage(teamData.goal_photo); - console.info("step 2"); + //console.info("step 2"); // 既存の内容をクリアして画像を追加 goalTimeElement.innerHTML = ''; goalTimeElement.appendChild(img); - console.info("Goal photo displayed: ",teamData.goal_photo); + //console.info("Goal photo displayed: ",teamData.goal_photo); } else { goalTimeElement.textContent = '画像なし'; console.info("No goal photo available"); @@ -812,12 +814,63 @@ function deleteRow(rowIndex) { const img = document.createElement('img'); img.src = src; img.classList.add('max-w-3xl', 'max-h-[90vh]', 'object-contain'); - + + // 画像の向きを補正 + applyImageOrientation(img); + modal.appendChild(img); modal.onclick = () => modal.remove(); document.body.appendChild(modal); } +// 画像の向きを取得して適用する関数 +function applyImageOrientation(imgElement) { + return new Promise((resolve) => { + const img = new Image(); + img.onload = function() { + // 仮想キャンバスを作成 + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + // EXIF情報を取得 + EXIF.getData(img, function() { + const orientation = EXIF.getTag(this, 'Orientation') || 1; + let width = img.width; + let height = img.height; + + // 向きに応じてキャンバスのサイズを調整 + if (orientation > 4 && orientation < 9) { + canvas.width = height; + canvas.height = width; + } else { + canvas.width = width; + canvas.height = height; + } + + // 向きに応じて回転を適用 + switch (orientation) { + case 2: ctx.transform(-1, 0, 0, 1, width, 0); break; + case 3: ctx.transform(-1, 0, 0, -1, width, height); break; + case 4: ctx.transform(1, 0, 0, -1, 0, height); break; + case 5: ctx.transform(0, 1, 1, 0, 0, 0); break; + case 6: ctx.transform(0, 1, -1, 0, height, 0); break; + case 7: ctx.transform(0, -1, -1, 0, height, width); break; + case 8: ctx.transform(0, -1, 1, 0, 0, width); break; + default: break; + } + + // 画像を描画 + ctx.drawImage(img, 0, 0); + + // 回転済みの画像をソースとして設定 + imgElement.src = canvas.toDataURL(); + resolve(); + }); + }; + img.src = imgElement.src; + }); +} + // 新規CP追加のための関数 function showAddCPDialog() { const cpInput = prompt('追加するCPをカンマ区切りで入力してください(例:CP1,CP2,CP3)'); @@ -833,12 +886,12 @@ function deleteRow(rowIndex) { const existingCheckins = getCurrentCheckins(); // 現在の表示データを取得する関数 const newCheckins = []; - console.info('existingCheckins.length =',existingCheckins.length); + //console.info('existingCheckins.length =',existingCheckins.length); cpList.forEach((cp,index) => { cploc = findLocationByCP(cp); - console.info('location=',cploc); - console.info('index = ',index); + //console.info('location=',cploc); + //console.info('index = ',index); newCheckins.push({ id: cploc.id, order: existingCheckins.length + index + 1, @@ -855,7 +908,7 @@ function deleteRow(rowIndex) { }); }); - console.info('newCheckins=',newCheckins); + //console.info('newCheckins=',newCheckins); // 新しいCPを表に追加 addCheckinsToTable(newCheckins); @@ -930,7 +983,7 @@ function deleteRow(rowIndex) { function updatePathOrders() { const rows = Array.from(document.getElementById('checkinList').children); rows.forEach((row, index) => { - console.info('row=',row); + //console.info('row=',row); row.children[1].textContent = index + 1; row.dataset.path_order = index + 1; }); @@ -938,7 +991,7 @@ function deleteRow(rowIndex) { // 総合ポイントの計算 function calculatePoints() { - console.info('calculatePoints'); + //console.info('calculatePoints'); const rows = Array.from(document.getElementById('checkinList').children); let totalPoints = 0; // チェックインポイントの合計をクリア let cpPoints = 0; // チェックインポイントの合計をクリア @@ -965,7 +1018,7 @@ function deleteRow(rowIndex) { const finalPoints = totalPoints + latePoints; // 判定を更新。順位を表示、ゴール時刻を15分経過したら失格 - console.info('calculatePoints:totalPoints=',cpPoints,',buyPoints=',buyPoints,',finalPoints=',finalPoints); + //console.info('calculatePoints:totalPoints=',cpPoints,',buyPoints=',buyPoints,',finalPoints=',finalPoints); document.getElementById('totalPoints').textContent = cpPoints; document.getElementById('buyPoints').textContent = buyPoints; @@ -1046,7 +1099,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { .replace(/\//g, '-') // スラッシュをハイフンに変換 .replace(' ', 'T'); // スペースをTに変換 - console.log(formattedDateTime); // "2024-10-26T12:59:13" + //console.log(formattedDateTime); // "2024-10-26T12:59:13" console.info('goaltime=',formattedDateTime); @@ -1151,7 +1204,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { checkins.forEach(checkin => { const row = document.createElement('tr'); - console.info('checkin=',checkin); + //console.info('checkin=',checkin); row.dataset.id = 0; row.dataset.local_id = checkin.order; // Unique @@ -1214,7 +1267,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { // チェックポイントデータをロードする関数 async function loadLocations(eventCode) { try { - console.info('loadLocations-1:',eventCode); + //console.info('loadLocations-1:',eventCode); if (!eventCode) { console.error('Event code is required'); return; @@ -1226,7 +1279,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { return loadedLocations; } - console.info('loadLocations-2:',eventCode); + //console.info('loadLocations-2:',eventCode); // group__containsフィルターを使用してクエリパラメータを構築 const params = new URLSearchParams({ @@ -1244,7 +1297,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { // レスポンスをJSONとして解決 const data = await response.json(); - console.info('loadLocations-3:', data); + //console.info('loadLocations-3:', data); if (!response.ok) { console.info('loadLocations-3: Bad Response :',response); @@ -1252,7 +1305,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { throw new Error(`HTTP error! status: ${response.status}`); } - console.info('loadLocations-4:',eventCode); + //console.info('loadLocations-4:',eventCode); // 取得したデータを処理して保存 loadedLocations = data.features.map(feature => ({ cp: feature.properties.cp, @@ -1268,9 +1321,9 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { })).filter(location => location.group && location.group.includes(eventCode)); currentEventCode = eventCode; - console.info(`Loaded ${loadedLocations.length} locations for event ${eventCode}`); + //console.info(`Loaded ${loadedLocations.length} locations for event ${eventCode}`); - console.info('loadedLocation[0]=',loadedLocations[0]); + //console.info('loadedLocation[0]=',loadedLocations[0]); return loadedLocations; @@ -1282,7 +1335,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) { // イベント選択時のハンドラー async function handleEventSelect(eventCode) { - console.info('handleEventSelect : ',eventCode); + //console.info('handleEventSelect : ',eventCode); try { //document.getElementById('loading').style.display = 'block'; await loadLocations(eventCode); @@ -1315,7 +1368,7 @@ function findLocationByCP(cpNumber) { return null; } - console.info(`Found location with CP ${cpNumber}:`, found); + //console.info(`Found location with CP ${cpNumber}:`, found); return found; }