fix goaltime issue on server side

This commit is contained in:
hayano
2024-11-03 05:16:05 +00:00
parent c6969d7afa
commit e992e834da
2 changed files with 84 additions and 31 deletions

View File

@ -2406,11 +2406,11 @@ def get_team_info(request, zekken_number):
# start_datetime = -1(ロゲ開始)のcreate_at # start_datetime = -1(ロゲ開始)のcreate_at
logger.debug(f"self.rogaining={entry.event.self_rogaining} => start_datetime = -1(ロゲ開始)のcreate_at") logger.debug(f"self.rogaining={entry.event.self_rogaining} => start_datetime = -1(ロゲ開始)のcreate_at")
# チームのゴール時間を取得 # チームの最初のゴール時間を取得
goal_record = GoalImages.objects.filter( goal_record = GoalImages.objects.filter(
team_name=entry.team.team_name, team_name=entry.team.team_name,
event_code=entry.event.event_name event_code=entry.event.event_name
).order_by('-goaltime').first() ).order_by('goaltime').first()
# Nullチェックを追加してからログ出力 # Nullチェックを追加してからログ出力
goalimage_url = None goalimage_url = None
@ -2771,7 +2771,7 @@ def update_goal_time(request):
# 既存の記録を更新 # 既存の記録を更新
goal_record.goaltime = goal_time goal_record.goaltime = goal_time
goal_record.save() 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: else:
# 新しい記録を作成 # 新しい記録を作成
entry = Entry.objects.get(zekken_number=zekken_number, event__event_name=event_code) entry = Entry.objects.get(zekken_number=zekken_number, event__event_name=event_code)

View File

@ -4,9 +4,10 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>スーパーバイザーパネル</title> <title>スーパーバイザーパネル</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
</head> </head>
<body class="bg-gray-50"> <body class="bg-gray-50">
<div class="container mx-auto p-4"> <div class="container mx-auto p-4">
@ -170,7 +171,7 @@
// Excel出力ボタンの処理 // Excel出力ボタンの処理
document.getElementById('exportButton').addEventListener('click', exportExcel); document.getElementById('exportButton').addEventListener('click', exportExcel);
console.log('Page loaded, attempting to load events...'); //console.log('Page loaded, attempting to load events...');
// 初期データ読み込み // 初期データ読み込み
loadEventCodes(); loadEventCodes();
}); });
@ -194,7 +195,8 @@
if (display.textContent && display.textContent !== '-') { if (display.textContent && display.textContent !== '-') {
try { try {
const date = new Date(display.textContent); 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) { } catch (e) {
console.error('Error parsing date:', e); console.error('Error parsing date:', e);
} }
@ -302,13 +304,13 @@
const timeDiff = (newTime - startTime) / 1000; // 分単位の差 const timeDiff = (newTime - startTime) / 1000; // 分単位の差
const maxTime = team.duration + 15*60; // 制限時間+15分 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 ); updateValidation(timeDiff, maxTime );
// 1秒でも遅刻すると、1分につき-50点 // 1秒でも遅刻すると、1分につき-50点
const overtime = ((newTime - startTime) / 1000 - team.duration ); const overtime = ((newTime - startTime) / 1000 - team.duration );
if( overtime>0 ){ if( overtime>0 ){
console.info('overtime=',overtime); //console.info('overtime=',overtime);
late_point = Math.ceil(overtime/60)*(-50); late_point = Math.ceil(overtime/60)*(-50);
lateElement = document.getElementById('latePoints'); lateElement = document.getElementById('latePoints');
lateElement.textContent = late_point; lateElement.textContent = late_point;
@ -333,7 +335,7 @@
// 判定の更新を行う補助関数 // 判定の更新を行う補助関数
function updateValidation(timeDiff, maxTime) { function updateValidation(timeDiff, maxTime) {
console.log('updateValidation',timeDiff,' > ',maxTime) //console.log('updateValidation',timeDiff,' > ',maxTime)
const validateElement = document.getElementById('validate'); const validateElement = document.getElementById('validate');
if (validateElement) { if (validateElement) {
if (timeDiff > maxTime) { if (timeDiff > maxTime) {
@ -458,23 +460,23 @@
goalTimeDisplay.onclick = () => editGoalTime(goalTimeDisplay); goalTimeDisplay.onclick = () => editGoalTime(goalTimeDisplay);
} }
console.info("step 0"); //console.info("step 0");
// ゴール時計の表示を更新 // ゴール時計の表示を更新
const goalTimeElement = document.getElementById('goalTime'); const goalTimeElement = document.getElementById('goalTime');
if (teamData.goal_photo) { if (teamData.goal_photo) {
// 画像要素を作成 // 画像要素を作成
console.info("step 1"); //console.info("step 1");
const img = document.createElement('img'); const img = document.createElement('img');
img.src = teamData.goal_photo; img.src = teamData.goal_photo;
img.classList.add('h-32', 'w-auto', 'object-contain', 'cursor-pointer'); img.classList.add('h-32', 'w-auto', 'object-contain', 'cursor-pointer');
img.onclick = () => showLargeImage(teamData.goal_photo); img.onclick = () => showLargeImage(teamData.goal_photo);
console.info("step 2"); //console.info("step 2");
// 既存の内容をクリアして画像を追加 // 既存の内容をクリアして画像を追加
goalTimeElement.innerHTML = ''; goalTimeElement.innerHTML = '';
goalTimeElement.appendChild(img); goalTimeElement.appendChild(img);
console.info("Goal photo displayed: ",teamData.goal_photo); //console.info("Goal photo displayed: ",teamData.goal_photo);
} else { } else {
goalTimeElement.textContent = '画像なし'; goalTimeElement.textContent = '画像なし';
console.info("No goal photo available"); console.info("No goal photo available");
@ -813,11 +815,62 @@ function deleteRow(rowIndex) {
img.src = src; img.src = src;
img.classList.add('max-w-3xl', 'max-h-[90vh]', 'object-contain'); img.classList.add('max-w-3xl', 'max-h-[90vh]', 'object-contain');
// 画像の向きを補正
applyImageOrientation(img);
modal.appendChild(img); modal.appendChild(img);
modal.onclick = () => modal.remove(); modal.onclick = () => modal.remove();
document.body.appendChild(modal); 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追加のための関数 // 新規CP追加のための関数
function showAddCPDialog() { function showAddCPDialog() {
const cpInput = prompt('追加するCPをカンマ区切りで入力してくださいCP1,CP2,CP3'); const cpInput = prompt('追加するCPをカンマ区切りで入力してくださいCP1,CP2,CP3');
@ -833,12 +886,12 @@ function deleteRow(rowIndex) {
const existingCheckins = getCurrentCheckins(); // 現在の表示データを取得する関数 const existingCheckins = getCurrentCheckins(); // 現在の表示データを取得する関数
const newCheckins = []; const newCheckins = [];
console.info('existingCheckins.length =',existingCheckins.length); //console.info('existingCheckins.length =',existingCheckins.length);
cpList.forEach((cp,index) => { cpList.forEach((cp,index) => {
cploc = findLocationByCP(cp); cploc = findLocationByCP(cp);
console.info('location=',cploc); //console.info('location=',cploc);
console.info('index = ',index); //console.info('index = ',index);
newCheckins.push({ newCheckins.push({
id: cploc.id, id: cploc.id,
order: existingCheckins.length + index + 1, order: existingCheckins.length + index + 1,
@ -855,7 +908,7 @@ function deleteRow(rowIndex) {
}); });
}); });
console.info('newCheckins=',newCheckins); //console.info('newCheckins=',newCheckins);
// 新しいCPを表に追加 // 新しいCPを表に追加
addCheckinsToTable(newCheckins); addCheckinsToTable(newCheckins);
@ -930,7 +983,7 @@ function deleteRow(rowIndex) {
function updatePathOrders() { function updatePathOrders() {
const rows = Array.from(document.getElementById('checkinList').children); const rows = Array.from(document.getElementById('checkinList').children);
rows.forEach((row, index) => { rows.forEach((row, index) => {
console.info('row=',row); //console.info('row=',row);
row.children[1].textContent = index + 1; row.children[1].textContent = index + 1;
row.dataset.path_order = index + 1; row.dataset.path_order = index + 1;
}); });
@ -938,7 +991,7 @@ function deleteRow(rowIndex) {
// 総合ポイントの計算 // 総合ポイントの計算
function calculatePoints() { function calculatePoints() {
console.info('calculatePoints'); //console.info('calculatePoints');
const rows = Array.from(document.getElementById('checkinList').children); const rows = Array.from(document.getElementById('checkinList').children);
let totalPoints = 0; // チェックインポイントの合計をクリア let totalPoints = 0; // チェックインポイントの合計をクリア
let cpPoints = 0; // チェックインポイントの合計をクリア let cpPoints = 0; // チェックインポイントの合計をクリア
@ -965,7 +1018,7 @@ function deleteRow(rowIndex) {
const finalPoints = totalPoints + latePoints; const finalPoints = totalPoints + latePoints;
// 判定を更新。順位を表示、ゴール時刻を15分経過したら失格 // 判定を更新。順位を表示、ゴール時刻を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('totalPoints').textContent = cpPoints;
document.getElementById('buyPoints').textContent = buyPoints; document.getElementById('buyPoints').textContent = buyPoints;
@ -1046,7 +1099,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
.replace(/\//g, '-') // スラッシュをハイフンに変換 .replace(/\//g, '-') // スラッシュをハイフンに変換
.replace(' ', 'T'); // スペースをTに変換 .replace(' ', 'T'); // スペースをTに変換
console.log(formattedDateTime); // "2024-10-26T12:59:13" //console.log(formattedDateTime); // "2024-10-26T12:59:13"
console.info('goaltime=',formattedDateTime); console.info('goaltime=',formattedDateTime);
@ -1151,7 +1204,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
checkins.forEach(checkin => { checkins.forEach(checkin => {
const row = document.createElement('tr'); const row = document.createElement('tr');
console.info('checkin=',checkin); //console.info('checkin=',checkin);
row.dataset.id = 0; row.dataset.id = 0;
row.dataset.local_id = checkin.order; // Unique row.dataset.local_id = checkin.order; // Unique
@ -1214,7 +1267,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
// チェックポイントデータをロードする関数 // チェックポイントデータをロードする関数
async function loadLocations(eventCode) { async function loadLocations(eventCode) {
try { try {
console.info('loadLocations-1:',eventCode); //console.info('loadLocations-1:',eventCode);
if (!eventCode) { if (!eventCode) {
console.error('Event code is required'); console.error('Event code is required');
return; return;
@ -1226,7 +1279,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
return loadedLocations; return loadedLocations;
} }
console.info('loadLocations-2:',eventCode); //console.info('loadLocations-2:',eventCode);
// group__containsフィルターを使用してクエリパラメータを構築 // group__containsフィルターを使用してクエリパラメータを構築
const params = new URLSearchParams({ const params = new URLSearchParams({
@ -1244,7 +1297,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
// レスポンスをJSONとして解決 // レスポンスをJSONとして解決
const data = await response.json(); const data = await response.json();
console.info('loadLocations-3:', data); //console.info('loadLocations-3:', data);
if (!response.ok) { if (!response.ok) {
console.info('loadLocations-3: Bad Response :',response); 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}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
console.info('loadLocations-4:',eventCode); //console.info('loadLocations-4:',eventCode);
// 取得したデータを処理して保存 // 取得したデータを処理して保存
loadedLocations = data.features.map(feature => ({ loadedLocations = data.features.map(feature => ({
cp: feature.properties.cp, cp: feature.properties.cp,
@ -1268,9 +1321,9 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
})).filter(location => location.group && location.group.includes(eventCode)); })).filter(location => location.group && location.group.includes(eventCode));
currentEventCode = 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; return loadedLocations;
@ -1282,7 +1335,7 @@ async function saveGoalTime(goalTimeStr, zekkenNumber, eventCode) {
// イベント選択時のハンドラー // イベント選択時のハンドラー
async function handleEventSelect(eventCode) { async function handleEventSelect(eventCode) {
console.info('handleEventSelect : ',eventCode); //console.info('handleEventSelect : ',eventCode);
try { try {
//document.getElementById('loading').style.display = 'block'; //document.getElementById('loading').style.display = 'block';
await loadLocations(eventCode); await loadLocations(eventCode);
@ -1315,7 +1368,7 @@ function findLocationByCP(cpNumber) {
return null; return null;
} }
console.info(`Found location with CP ${cpNumber}:`, found); //console.info(`Found location with CP ${cpNumber}:`, found);
return found; return found;
} }