fix goaltime issue on server side
This commit is contained in:
@ -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)
|
||||||
|
|||||||
@ -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");
|
||||||
@ -812,12 +814,63 @@ function deleteRow(rowIndex) {
|
|||||||
const img = document.createElement('img');
|
const img = document.createElement('img');
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user