페이지 수정사항 반영
This commit is contained in:
95
backend/check-data.js
Normal file
95
backend/check-data.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
async function main() {
|
||||
const conn = new Client({
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'turbo123',
|
||||
database: 'genome_db'
|
||||
});
|
||||
await conn.connect();
|
||||
|
||||
// 1. 해당 개체의 형질 데이터 확인
|
||||
const cowTraitsResult = await conn.query(
|
||||
"SELECT trait_name, trait_ebv FROM tb_genome_trait_detail WHERE cow_id = 'KOR002191643715' AND del_dt IS NULL ORDER BY trait_name"
|
||||
);
|
||||
const cowTraits = cowTraitsResult.rows;
|
||||
console.log('=== 개체 KOR002191643715 형질 데이터 ===');
|
||||
console.log('형질수:', cowTraits.length);
|
||||
let totalEbv = 0;
|
||||
cowTraits.forEach(t => {
|
||||
console.log(t.trait_name + ': ' + t.trait_ebv);
|
||||
totalEbv += Number(t.trait_ebv || 0);
|
||||
});
|
||||
console.log('\n*** 내 개체 EBV 합계(선발지수):', totalEbv.toFixed(2));
|
||||
|
||||
// 2. 해당 개체의 농가 확인
|
||||
const cowInfoResult = await conn.query(
|
||||
"SELECT gr.fk_farm_no, f.farmer_name FROM tb_genome_request gr JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no LEFT JOIN tb_farm f ON gr.fk_farm_no = f.pk_farm_no WHERE c.cow_id = 'KOR002191643715' AND gr.del_dt IS NULL LIMIT 1"
|
||||
);
|
||||
const cowInfo = cowInfoResult.rows;
|
||||
console.log('\n=== 농가 정보 ===');
|
||||
console.log('농가번호:', cowInfo[0]?.fk_farm_no, '농장주:', cowInfo[0]?.farmer_name);
|
||||
|
||||
const farmNo = cowInfo[0]?.fk_farm_no;
|
||||
|
||||
// 3. 같은 농가의 모든 개체 EBV 합계
|
||||
if (farmNo) {
|
||||
const farmCowsResult = await conn.query(
|
||||
`SELECT c.cow_id, SUM(gtd.trait_ebv) as total_ebv, COUNT(*) as trait_count
|
||||
FROM tb_genome_request gr
|
||||
JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no
|
||||
JOIN tb_genome_trait_detail gtd ON gtd.cow_id = c.cow_id AND gtd.del_dt IS NULL
|
||||
WHERE gr.fk_farm_no = $1 AND gr.del_dt IS NULL AND c.del_dt IS NULL
|
||||
AND gr.chip_sire_name = '일치'
|
||||
GROUP BY c.cow_id
|
||||
HAVING COUNT(*) = 35
|
||||
ORDER BY total_ebv DESC`,
|
||||
[farmNo]
|
||||
);
|
||||
const farmCows = farmCowsResult.rows;
|
||||
console.log('\n=== 같은 농가 개체들 EBV 합계 (35형질 전체) ===');
|
||||
console.log('개체수:', farmCows.length);
|
||||
let farmSum = 0;
|
||||
farmCows.forEach(c => {
|
||||
console.log(c.cow_id + ': ' + Number(c.total_ebv).toFixed(2));
|
||||
farmSum += Number(c.total_ebv);
|
||||
});
|
||||
if (farmCows.length > 0) {
|
||||
console.log('\n*** 농가 평균:', (farmSum / farmCows.length).toFixed(2));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 전체 보은군 평균
|
||||
const allCowsResult = await conn.query(
|
||||
`SELECT c.cow_id, SUM(gtd.trait_ebv) as total_ebv
|
||||
FROM tb_genome_request gr
|
||||
JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no
|
||||
JOIN tb_genome_trait_detail gtd ON gtd.cow_id = c.cow_id AND gtd.del_dt IS NULL
|
||||
WHERE gr.del_dt IS NULL AND c.del_dt IS NULL
|
||||
AND gr.chip_sire_name = '일치'
|
||||
GROUP BY c.cow_id
|
||||
HAVING COUNT(*) = 35`
|
||||
);
|
||||
const allCows = allCowsResult.rows;
|
||||
console.log('\n=== 보은군 전체 통계 ===');
|
||||
console.log('개체수:', allCows.length);
|
||||
let regionSum = 0;
|
||||
allCows.forEach(c => regionSum += Number(c.total_ebv));
|
||||
if (allCows.length > 0) {
|
||||
console.log('*** 보은군 평균:', (regionSum / allCows.length).toFixed(2));
|
||||
}
|
||||
|
||||
// 5. 최대/최소 확인
|
||||
if (allCows.length > 0) {
|
||||
const maxCow = allCows.reduce((max, c) => Number(c.total_ebv) > Number(max.total_ebv) ? c : max, allCows[0]);
|
||||
const minCow = allCows.reduce((min, c) => Number(c.total_ebv) < Number(min.total_ebv) ? c : min, allCows[0]);
|
||||
console.log('\n최대:', maxCow?.cow_id, Number(maxCow?.total_ebv).toFixed(2));
|
||||
console.log('최소:', minCow?.cow_id, Number(minCow?.total_ebv).toFixed(2));
|
||||
}
|
||||
|
||||
await conn.end();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
97
backend/check-data2.js
Normal file
97
backend/check-data2.js
Normal file
@@ -0,0 +1,97 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
async function main() {
|
||||
const conn = new Client({
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'turbo123',
|
||||
database: 'genome_db'
|
||||
});
|
||||
await conn.connect();
|
||||
|
||||
// getComparisonAverages가 계산하는 방식 확인
|
||||
// 농가 1번의 카테고리별 평균 EBV 계산
|
||||
const farmNo = 1;
|
||||
|
||||
// 카테고리 매핑 (백엔드와 동일)
|
||||
const TRAIT_CATEGORY_MAP = {
|
||||
'12개월령체중': '성장',
|
||||
'도체중': '생산', '등심단면적': '생산', '등지방두께': '생산', '근내지방도': '생산',
|
||||
'체고': '체형', '십자': '체형', '체장': '체형', '흉심': '체형', '흉폭': '체형',
|
||||
'고장': '체형', '요각폭': '체형', '곤폭': '체형', '좌골폭': '체형', '흉위': '체형',
|
||||
'안심weight': '무게', '등심weight': '무게', '채끝weight': '무게', '목심weight': '무게',
|
||||
'앞다리weight': '무게', '우둔weight': '무게', '설도weight': '무게', '사태weight': '무게',
|
||||
'양지weight': '무게', '갈비weight': '무게',
|
||||
'안심rate': '비율', '등심rate': '비율', '채끝rate': '비율', '목심rate': '비율',
|
||||
'앞다리rate': '비율', '우둔rate': '비율', '설도rate': '비율', '사태rate': '비율',
|
||||
'양지rate': '비율', '갈비rate': '비율',
|
||||
};
|
||||
|
||||
// 농가 1번의 모든 형질 데이터 조회
|
||||
const result = await conn.query(`
|
||||
SELECT gtd.trait_name, gtd.trait_ebv
|
||||
FROM tb_genome_trait_detail gtd
|
||||
JOIN tb_genome_request gr ON gtd.fk_request_no = gr.pk_request_no
|
||||
WHERE gr.fk_farm_no = $1
|
||||
AND gtd.del_dt IS NULL
|
||||
AND gtd.trait_ebv IS NOT NULL
|
||||
`, [farmNo]);
|
||||
|
||||
const details = result.rows;
|
||||
console.log('농가 1번 전체 형질 데이터 수:', details.length);
|
||||
|
||||
// 카테고리별로 합계 계산
|
||||
const categoryMap = {};
|
||||
for (const d of details) {
|
||||
const category = TRAIT_CATEGORY_MAP[d.trait_name] || '기타';
|
||||
if (!categoryMap[category]) {
|
||||
categoryMap[category] = { sum: 0, count: 0 };
|
||||
}
|
||||
categoryMap[category].sum += Number(d.trait_ebv);
|
||||
categoryMap[category].count += 1;
|
||||
}
|
||||
|
||||
console.log('\n=== getComparisonAverages 방식 (카테고리별 평균) ===');
|
||||
const categories = ['성장', '생산', '체형', '무게', '비율'];
|
||||
let totalAvgEbv = 0;
|
||||
for (const cat of categories) {
|
||||
const data = categoryMap[cat];
|
||||
const avgEbv = data ? data.sum / data.count : 0;
|
||||
console.log(`${cat}: 합계=${data?.sum?.toFixed(2)} / 개수=${data?.count} = 평균 ${avgEbv.toFixed(2)}`);
|
||||
totalAvgEbv += avgEbv;
|
||||
}
|
||||
|
||||
console.log('\n*** farmAvgZ (카테고리 평균의 합/5):', (totalAvgEbv / categories.length).toFixed(2));
|
||||
|
||||
// getSelectionIndex 방식 비교
|
||||
console.log('\n=== getSelectionIndex 방식 (개체별 합계의 평균) ===');
|
||||
const farmCowsResult = await conn.query(`
|
||||
SELECT c.cow_id, SUM(gtd.trait_ebv) as total_ebv, COUNT(*) as trait_count
|
||||
FROM tb_genome_request gr
|
||||
JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no
|
||||
JOIN tb_genome_trait_detail gtd ON gtd.cow_id = c.cow_id AND gtd.del_dt IS NULL
|
||||
WHERE gr.fk_farm_no = $1 AND gr.del_dt IS NULL AND c.del_dt IS NULL
|
||||
AND gr.chip_sire_name = '일치'
|
||||
GROUP BY c.cow_id
|
||||
HAVING COUNT(*) = 35
|
||||
ORDER BY total_ebv DESC
|
||||
`, [farmNo]);
|
||||
const farmCows = farmCowsResult.rows;
|
||||
|
||||
let farmSum = 0;
|
||||
farmCows.forEach(c => farmSum += Number(c.total_ebv));
|
||||
const farmAvgScore = farmCows.length > 0 ? farmSum / farmCows.length : 0;
|
||||
|
||||
console.log(`개체수: ${farmCows.length}`);
|
||||
console.log(`*** farmAvgScore (개체별 합계의 평균): ${farmAvgScore.toFixed(2)}`);
|
||||
|
||||
console.log('\n=================================================');
|
||||
console.log('farmAvgZ (카테고리 방식):', (totalAvgEbv / categories.length).toFixed(2));
|
||||
console.log('farmAvgScore (선발지수 방식):', farmAvgScore.toFixed(2));
|
||||
console.log('=================================================');
|
||||
|
||||
await conn.end();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
114
backend/check-data3.js
Normal file
114
backend/check-data3.js
Normal file
@@ -0,0 +1,114 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
async function main() {
|
||||
const conn = new Client({
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'turbo123',
|
||||
database: 'genome_db'
|
||||
});
|
||||
await conn.connect();
|
||||
|
||||
const farmNo = 1;
|
||||
const cowId = 'KOR002191643715';
|
||||
|
||||
console.log('=======================================================');
|
||||
console.log('대시보드 vs 개체상세 차트 비교');
|
||||
console.log('=======================================================\n');
|
||||
|
||||
// =====================================================
|
||||
// 1. 대시보드: getFarmRegionRanking API
|
||||
// - 농가 평균 = 농가 내 개체들의 선발지수 평균
|
||||
// - 보은군 평균 = 전체 개체들의 선발지수 평균
|
||||
// =====================================================
|
||||
console.log('=== 1. 대시보드 (getFarmRegionRanking) ===');
|
||||
console.log('보은군 내 농가 위치 차트\n');
|
||||
|
||||
// 모든 개체별 선발지수 (35개 형질 EBV 합계)
|
||||
const allCowsResult = await conn.query(`
|
||||
SELECT c.cow_id, gr.fk_farm_no, SUM(gtd.trait_ebv) as total_ebv
|
||||
FROM tb_genome_request gr
|
||||
JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no
|
||||
JOIN tb_genome_trait_detail gtd ON gtd.cow_id = c.cow_id AND gtd.del_dt IS NULL
|
||||
WHERE gr.del_dt IS NULL AND c.del_dt IS NULL
|
||||
AND gr.chip_sire_name = '일치'
|
||||
GROUP BY c.cow_id, gr.fk_farm_no
|
||||
HAVING COUNT(*) = 35
|
||||
`);
|
||||
const allCows = allCowsResult.rows;
|
||||
|
||||
// 농가별 평균 계산
|
||||
const farmScoresMap = new Map();
|
||||
for (const cow of allCows) {
|
||||
const fNo = cow.fk_farm_no;
|
||||
if (!farmScoresMap.has(fNo)) {
|
||||
farmScoresMap.set(fNo, []);
|
||||
}
|
||||
farmScoresMap.set(fNo, [...farmScoresMap.get(fNo), Number(cow.total_ebv)]);
|
||||
}
|
||||
|
||||
// 농가별 평균
|
||||
const farmAverages = [];
|
||||
for (const [fNo, scores] of farmScoresMap.entries()) {
|
||||
const avg = scores.reduce((a, b) => a + b, 0) / scores.length;
|
||||
farmAverages.push({ farmNo: fNo, avgScore: avg, cowCount: scores.length });
|
||||
}
|
||||
farmAverages.sort((a, b) => b.avgScore - a.avgScore);
|
||||
|
||||
// 보은군 전체 평균 (개체별 합계의 평균)
|
||||
const regionAvgScore_dashboard = allCows.reduce((sum, c) => sum + Number(c.total_ebv), 0) / allCows.length;
|
||||
|
||||
// 농가 1번 평균
|
||||
const myFarm = farmAverages.find(f => f.farmNo === farmNo);
|
||||
const farmAvgScore_dashboard = myFarm?.avgScore || 0;
|
||||
|
||||
console.log('농가 평균 (개체 선발지수 평균):', farmAvgScore_dashboard.toFixed(2));
|
||||
console.log('보은군 평균 (개체 선발지수 평균):', regionAvgScore_dashboard.toFixed(2));
|
||||
console.log('차이 (농가 - 보은군):', (farmAvgScore_dashboard - regionAvgScore_dashboard).toFixed(2));
|
||||
|
||||
// =====================================================
|
||||
// 2. 개체 상세: getSelectionIndex API
|
||||
// - 내 개체 = 개체의 선발지수 (35개 형질 EBV 합계)
|
||||
// - 농가 평균 = 같은 농가 개체들의 선발지수 평균
|
||||
// - 보은군 평균 = 전체 개체들의 선발지수 평균
|
||||
// =====================================================
|
||||
console.log('\n=== 2. 개체 상세 (getSelectionIndex) ===');
|
||||
console.log('농가 및 보은군 내 개체 위치 차트\n');
|
||||
|
||||
// 내 개체 선발지수
|
||||
const myCow = allCows.find(c => c.cow_id === cowId);
|
||||
const myScore = myCow ? Number(myCow.total_ebv) : 0;
|
||||
|
||||
// 같은 농가 개체들의 평균
|
||||
const farmCows = allCows.filter(c => c.fk_farm_no === farmNo);
|
||||
const farmAvgScore_detail = farmCows.reduce((sum, c) => sum + Number(c.total_ebv), 0) / farmCows.length;
|
||||
|
||||
// 보은군 전체 평균
|
||||
const regionAvgScore_detail = regionAvgScore_dashboard; // 동일
|
||||
|
||||
console.log('내 개체 선발지수:', myScore.toFixed(2));
|
||||
console.log('농가 평균:', farmAvgScore_detail.toFixed(2));
|
||||
console.log('보은군 평균:', regionAvgScore_detail.toFixed(2));
|
||||
console.log('');
|
||||
console.log('내 개체 vs 농가평균:', (myScore - farmAvgScore_detail).toFixed(2));
|
||||
console.log('내 개체 vs 보은군평균:', (myScore - regionAvgScore_detail).toFixed(2));
|
||||
|
||||
// =====================================================
|
||||
// 3. 비교 요약
|
||||
// =====================================================
|
||||
console.log('\n=======================================================');
|
||||
console.log('비교 요약');
|
||||
console.log('=======================================================');
|
||||
console.log('');
|
||||
console.log('[대시보드] 농가 vs 보은군 차이:', (farmAvgScore_dashboard - regionAvgScore_dashboard).toFixed(2));
|
||||
console.log('[개체상세] 개체 vs 농가 차이:', (myScore - farmAvgScore_detail).toFixed(2));
|
||||
console.log('[개체상세] 개체 vs 보은군 차이:', (myScore - regionAvgScore_detail).toFixed(2));
|
||||
console.log('');
|
||||
console.log('=> 대시보드는 농가평균 vs 보은군평균 비교 (차이 작음)');
|
||||
console.log('=> 개체상세는 개별개체 vs 평균 비교 (개체가 우수하면 차이 큼)');
|
||||
|
||||
await conn.end();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
126
backend/check-data4.js
Normal file
126
backend/check-data4.js
Normal file
@@ -0,0 +1,126 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
async function main() {
|
||||
const conn = new Client({
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'turbo123',
|
||||
database: 'genome_db'
|
||||
});
|
||||
await conn.connect();
|
||||
|
||||
const cowId = 'KOR002191643715';
|
||||
const farmNo = 1;
|
||||
|
||||
console.log('=======================================================');
|
||||
console.log('선발지수 계산 방식 비교 분석');
|
||||
console.log('=======================================================\n');
|
||||
|
||||
// 1. 해당 개체의 35개 형질 EBV 확인
|
||||
const traitsResult = await conn.query(`
|
||||
SELECT trait_name, trait_ebv
|
||||
FROM tb_genome_trait_detail
|
||||
WHERE cow_id = $1 AND del_dt IS NULL
|
||||
ORDER BY trait_name
|
||||
`, [cowId]);
|
||||
const traits = traitsResult.rows;
|
||||
|
||||
console.log('=== 개체 형질 데이터 ===');
|
||||
console.log('형질 수:', traits.length);
|
||||
|
||||
// EBV 합계
|
||||
const ebvSum = traits.reduce((sum, t) => sum + Number(t.trait_ebv || 0), 0);
|
||||
console.log('EBV 합계:', ebvSum.toFixed(2));
|
||||
|
||||
// EBV 평균
|
||||
const ebvAvg = ebvSum / traits.length;
|
||||
console.log('EBV 평균:', ebvAvg.toFixed(2));
|
||||
|
||||
console.log('\n=== 선발지수 계산 방식 비교 ===\n');
|
||||
|
||||
// 방식 1: EBV 합계 (getSelectionIndex 방식)
|
||||
console.log('방식1 - EBV 합계 (weightedSum):', ebvSum.toFixed(2));
|
||||
|
||||
// 방식 2: EBV 평균
|
||||
console.log('방식2 - EBV 평균 (sum/count):', ebvAvg.toFixed(2));
|
||||
|
||||
// 농가/보은군 평균도 각 방식으로 계산
|
||||
console.log('\n=== 농가 평균 계산 방식 비교 ===\n');
|
||||
|
||||
// 농가 내 모든 개체
|
||||
const farmCowsResult = await conn.query(`
|
||||
SELECT c.cow_id, SUM(gtd.trait_ebv) as sum_ebv, AVG(gtd.trait_ebv) as avg_ebv, COUNT(*) as cnt
|
||||
FROM tb_genome_request gr
|
||||
JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no
|
||||
JOIN tb_genome_trait_detail gtd ON gtd.cow_id = c.cow_id AND gtd.del_dt IS NULL
|
||||
WHERE gr.fk_farm_no = $1 AND gr.del_dt IS NULL AND c.del_dt IS NULL
|
||||
AND gr.chip_sire_name = '일치'
|
||||
GROUP BY c.cow_id
|
||||
HAVING COUNT(*) = 35
|
||||
`, [farmNo]);
|
||||
const farmCows = farmCowsResult.rows;
|
||||
|
||||
// 방식 1: 개체별 합계의 평균
|
||||
const farmSumAvg = farmCows.reduce((sum, c) => sum + Number(c.sum_ebv), 0) / farmCows.length;
|
||||
console.log('방식1 - 개체별 합계의 평균:', farmSumAvg.toFixed(2));
|
||||
|
||||
// 방식 2: 개체별 평균의 평균
|
||||
const farmAvgAvg = farmCows.reduce((sum, c) => sum + Number(c.avg_ebv), 0) / farmCows.length;
|
||||
console.log('방식2 - 개체별 평균의 평균:', farmAvgAvg.toFixed(2));
|
||||
|
||||
console.log('\n=== 보은군 평균 계산 방식 비교 ===\n');
|
||||
|
||||
// 보은군 전체 개체
|
||||
const allCowsResult = await conn.query(`
|
||||
SELECT c.cow_id, SUM(gtd.trait_ebv) as sum_ebv, AVG(gtd.trait_ebv) as avg_ebv, COUNT(*) as cnt
|
||||
FROM tb_genome_request gr
|
||||
JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no
|
||||
JOIN tb_genome_trait_detail gtd ON gtd.cow_id = c.cow_id AND gtd.del_dt IS NULL
|
||||
WHERE gr.del_dt IS NULL AND c.del_dt IS NULL
|
||||
AND gr.chip_sire_name = '일치'
|
||||
GROUP BY c.cow_id
|
||||
HAVING COUNT(*) = 35
|
||||
`);
|
||||
const allCows = allCowsResult.rows;
|
||||
|
||||
// 방식 1: 개체별 합계의 평균
|
||||
const regionSumAvg = allCows.reduce((sum, c) => sum + Number(c.sum_ebv), 0) / allCows.length;
|
||||
console.log('방식1 - 개체별 합계의 평균:', regionSumAvg.toFixed(2));
|
||||
|
||||
// 방식 2: 개체별 평균의 평균
|
||||
const regionAvgAvg = allCows.reduce((sum, c) => sum + Number(c.avg_ebv), 0) / allCows.length;
|
||||
console.log('방식2 - 개체별 평균의 평균:', regionAvgAvg.toFixed(2));
|
||||
|
||||
console.log('\n=======================================================');
|
||||
console.log('결과 비교');
|
||||
console.log('=======================================================\n');
|
||||
|
||||
console.log('만약 "합계" 방식 사용 시:');
|
||||
console.log(' 내 개체:', ebvSum.toFixed(2));
|
||||
console.log(' 농가 평균:', farmSumAvg.toFixed(2));
|
||||
console.log(' 보은군 평균:', regionSumAvg.toFixed(2));
|
||||
console.log(' → 내 개체 vs 농가: +', (ebvSum - farmSumAvg).toFixed(2));
|
||||
console.log(' → 내 개체 vs 보은군: +', (ebvSum - regionSumAvg).toFixed(2));
|
||||
|
||||
console.log('\n만약 "평균" 방식 사용 시:');
|
||||
console.log(' 내 개체:', ebvAvg.toFixed(2));
|
||||
console.log(' 농가 평균:', farmAvgAvg.toFixed(2));
|
||||
console.log(' 보은군 평균:', regionAvgAvg.toFixed(2));
|
||||
console.log(' → 내 개체 vs 농가: +', (ebvAvg - farmAvgAvg).toFixed(2));
|
||||
console.log(' → 내 개체 vs 보은군: +', (ebvAvg - regionAvgAvg).toFixed(2));
|
||||
|
||||
console.log('\n=======================================================');
|
||||
console.log('리스트 선발지수 확인 (page.tsx의 GENOMIC_TRAITS)');
|
||||
console.log('=======================================================\n');
|
||||
|
||||
// 리스트에서 보여주는 선발지수는 어떻게 계산되나?
|
||||
// page.tsx:350-356 확인 필요
|
||||
console.log('리스트의 overallScore 계산식 확인 필요:');
|
||||
console.log(' - selectionIndex.score 사용 시: 합계 방식');
|
||||
console.log(' - GENOMIC_TRAITS.reduce / length 사용 시: 평균 방식');
|
||||
|
||||
await conn.end();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
47
backend/check-data5.js
Normal file
47
backend/check-data5.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
async function main() {
|
||||
const conn = new Client({
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'turbo123',
|
||||
database: 'genome_db'
|
||||
});
|
||||
await conn.connect();
|
||||
|
||||
const farmNo = 1;
|
||||
|
||||
// 농가 내 모든 개체의 선발지수(가중 합계) 조회
|
||||
const farmCowsResult = await conn.query(`
|
||||
SELECT c.cow_id, SUM(gtd.trait_ebv) as sum_ebv
|
||||
FROM tb_genome_request gr
|
||||
JOIN tb_cow c ON gr.fk_cow_no = c.pk_cow_no
|
||||
JOIN tb_genome_trait_detail gtd ON gtd.cow_id = c.cow_id AND gtd.del_dt IS NULL
|
||||
WHERE gr.fk_farm_no = $1 AND gr.del_dt IS NULL AND c.del_dt IS NULL
|
||||
AND gr.chip_sire_name = '일치'
|
||||
GROUP BY c.cow_id
|
||||
HAVING COUNT(*) = 35
|
||||
ORDER BY sum_ebv DESC
|
||||
`, [farmNo]);
|
||||
|
||||
const farmCows = farmCowsResult.rows;
|
||||
|
||||
console.log('=== 농가 1번 개체별 선발지수 (가중 합계) ===\n');
|
||||
|
||||
let total = 0;
|
||||
farmCows.forEach((c, i) => {
|
||||
const score = Number(c.sum_ebv);
|
||||
total += score;
|
||||
console.log(`${i+1}. ${c.cow_id}: ${score.toFixed(2)}`);
|
||||
});
|
||||
|
||||
console.log('\n=== 계산 ===');
|
||||
console.log('개체 수:', farmCows.length);
|
||||
console.log('선발지수 총합:', total.toFixed(2));
|
||||
console.log('농가 평균 = 총합 / 개체수 =', total.toFixed(2), '/', farmCows.length, '=', (total / farmCows.length).toFixed(2));
|
||||
|
||||
await conn.end();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
66
backend/check-data6.js
Normal file
66
backend/check-data6.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
async function main() {
|
||||
const conn = new Client({
|
||||
host: 'localhost',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: 'turbo123',
|
||||
database: 'genome_db'
|
||||
});
|
||||
await conn.connect();
|
||||
|
||||
const cowId = 'KOR002191643715';
|
||||
|
||||
console.log('=======================================================');
|
||||
console.log('리스트 vs 개체상세 선발지수 비교');
|
||||
console.log('=======================================================\n');
|
||||
|
||||
// 해당 개체의 35개 형질 EBV 조회
|
||||
const traitsResult = await conn.query(`
|
||||
SELECT trait_name, trait_ebv
|
||||
FROM tb_genome_trait_detail
|
||||
WHERE cow_id = $1 AND del_dt IS NULL
|
||||
ORDER BY trait_name
|
||||
`, [cowId]);
|
||||
const traits = traitsResult.rows;
|
||||
|
||||
console.log('형질 수:', traits.length);
|
||||
|
||||
// 1. 가중 합계 (weight = 1)
|
||||
let weightedSum = 0;
|
||||
let totalWeight = 0;
|
||||
traits.forEach(t => {
|
||||
const ebv = Number(t.trait_ebv);
|
||||
const weight = 1;
|
||||
weightedSum += ebv * weight;
|
||||
totalWeight += weight;
|
||||
});
|
||||
|
||||
console.log('\n=== 계산 비교 ===');
|
||||
console.log('가중 합계 (weightedSum):', weightedSum.toFixed(2));
|
||||
console.log('총 가중치 (totalWeight):', totalWeight);
|
||||
console.log('');
|
||||
console.log('리스트 (cow.service.ts) - 가중 합계:', weightedSum.toFixed(2));
|
||||
console.log('개체상세 (genome.service.ts) - 가중 합계:', weightedSum.toFixed(2));
|
||||
|
||||
console.log('\n=== 프론트엔드 가중치 확인 ===');
|
||||
console.log('프론트엔드에서 weight / 100 정규화 확인 필요');
|
||||
console.log('예: weight 100 → 1, weight 50 → 0.5');
|
||||
|
||||
// 만약 프론트에서 weight/100을 적용한다면?
|
||||
console.log('\n=== 만약 weight가 0.01로 적용된다면? ===');
|
||||
let weightedSum2 = 0;
|
||||
let totalWeight2 = 0;
|
||||
traits.forEach(t => {
|
||||
const ebv = Number(t.trait_ebv);
|
||||
const weight = 0.01; // 1/100
|
||||
weightedSum2 += ebv * weight;
|
||||
totalWeight2 += weight;
|
||||
});
|
||||
console.log('가중 합계:', weightedSum2.toFixed(2));
|
||||
|
||||
await conn.end();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
@@ -223,8 +223,24 @@ export class CowService {
|
||||
*/
|
||||
private async applyGenomeRanking(
|
||||
cows: CowModel[],
|
||||
traitConditions: TraitRankingCondition[],
|
||||
inputTraitConditions: TraitRankingCondition[],
|
||||
): Promise<any> {
|
||||
// 35개 전체 형질 (기본값)
|
||||
const ALL_TRAITS = [
|
||||
'12개월령체중',
|
||||
'도체중', '등심단면적', '등지방두께', '근내지방도',
|
||||
'체고', '십자', '체장', '흉심', '흉폭', '고장', '요각폭', '좌골폭', '곤폭', '흉위',
|
||||
'안심weight', '등심weight', '채끝weight', '목심weight', '앞다리weight',
|
||||
'우둔weight', '설도weight', '사태weight', '양지weight', '갈비weight',
|
||||
'안심rate', '등심rate', '채끝rate', '목심rate', '앞다리rate',
|
||||
'우둔rate', '설도rate', '사태rate', '양지rate', '갈비rate',
|
||||
];
|
||||
|
||||
// traitConditions가 비어있으면 35개 전체 형질 사용 (개체상세, 대시보드와 동일)
|
||||
const traitConditions = inputTraitConditions && inputTraitConditions.length > 0
|
||||
? inputTraitConditions
|
||||
: ALL_TRAITS.map(traitNm => ({ traitNm, weight: 1 }));
|
||||
|
||||
// 각 개체별로 점수 계산
|
||||
const cowsWithScore = await Promise.all(
|
||||
cows.map(async (cow) => {
|
||||
@@ -260,7 +276,7 @@ export class CowService {
|
||||
return { entity: { ...cow, unavailableReason: '형질정보없음' }, sortValue: null, details: [] };
|
||||
}
|
||||
|
||||
// Step 4: 가중 평균 계산
|
||||
// Step 4: 가중 합계 계산
|
||||
let weightedSum = 0; // 가중치 적용된 EBV 합계
|
||||
let totalWeight = 0; // 총 가중치
|
||||
let hasAllTraits = true; // 모든 선택 형질 존재 여부
|
||||
@@ -290,10 +306,10 @@ export class CowService {
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: 최종 점수 계산 (가중 평균)
|
||||
// Step 6: 최종 점수 계산 (가중 합계)
|
||||
// 모든 선택 형질이 있어야만 점수 계산
|
||||
const sortValue = (hasAllTraits && totalWeight > 0)
|
||||
? weightedSum / totalWeight // 가중 평균 = 가중합 / 총가중치
|
||||
? weightedSum // 가중 합계 (개체상세, 대시보드와 동일한 방식)
|
||||
: null;
|
||||
|
||||
// Step 7: 응답 데이터 구성
|
||||
|
||||
Reference in New Issue
Block a user