feat: MySQL DB 정규화(Master/History) 및 시계열 데이터 수집 시스템 통합

1. 마스터/히스토리 테이블 분리 및 마이그레이션 완료\n2. 날짜별 데이터 축적 및 대시보드 필터링 기능 추가\n3. Playwright 수집 로직(날짜필터, 좌표클릭, 정밀합산) 완전 복구
This commit is contained in:
2026-03-10 16:24:13 +09:00
parent 743cce543b
commit 4a995c11f4
9 changed files with 268 additions and 89 deletions

View File

@@ -18,27 +18,60 @@ const continentOrder = {
async function init() {
const container = document.getElementById('projectAccordion');
const baseDateStrong = document.getElementById('baseDate');
if (!container) return;
// 서버에서 최신 sheet.csv 데이터 가져오기 (캐시 방지 위해 timestamp 추가)
// 1. 가용한 날짜 목록 가져오기 및 셀렉트 박스 생성
try {
const response = await fetch(`/project-data?t=${new Date().getTime()}`);
rawData = await response.json();
console.log("Loaded rawData:", rawData);
if (rawData.error) throw new Error(rawData.error);
const datesRes = await fetch('/available-dates');
const dates = await datesRes.json();
if (dates && dates.length > 0) {
let selectHtml = `<select id="dateSelector" onchange="loadDataByDate(this.value)" style="margin-left:10px; border:none; background:none; font-weight:700; cursor:pointer;">`;
dates.forEach(d => {
selectHtml += `<option value="${d}">${d}</option>`;
});
selectHtml += `</select>`;
// 기준날짜 텍스트 영역을 셀렉트 박스로 교체
const baseDateInfo = document.querySelector('.base-date-info');
if (baseDateInfo) {
baseDateInfo.innerHTML = `기준날짜: ${selectHtml}`;
}
}
} catch (e) {
console.error("날짜 목록 로드 실패:", e);
}
// 2. 기본 데이터 로드 (최신 날짜)
loadDataByDate();
}
async function loadDataByDate(selectedDate = "") {
const container = document.getElementById('projectAccordion');
try {
const url = selectedDate ? `/project-data?date=${selectedDate}` : `/project-data?t=${new Date().getTime()}`;
const response = await fetch(url);
const data = await response.json();
if (data.error) throw new Error(data.error);
rawData = data.projects || [];
renderDashboard(rawData);
} catch (e) {
console.error("데이터 로드 실패:", e);
alert("데이터를 가져오는 데 실패했습니다.");
return;
}
}
function renderDashboard(data) {
const container = document.getElementById('projectAccordion');
container.innerHTML = ''; // 초기화
const groupedData = {};
rawData.forEach((item, index) => {
const projectName = item[0];
// DB에서 넘어온 대륙과 국가 정보 사용 (item[5], item[6])
let continent = item[5] || "기타";
data.forEach((item, index) => {
let continent = item[5] || "기기타";
let country = item[6] || "미분류";
if (!groupedData[continent]) groupedData[continent] = {};