- docs/business-plans.md: Tasteby/Lyricsy/Parents Story 3개 앱 PSST 사업계획서 초안 - scripts/match.js: 앱별 주제 키워드 매칭 조회 - scripts/eligible.js: 예비창업자 자격 + 현재 열린 공고 목록 - scripts/export_eligible_csv.js: 신청 추적용 CSV(exports/) 생성 - exports/ gitignore Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69 lines
2.3 KiB
JavaScript
69 lines
2.3 KiB
JavaScript
// 예비창업자 자격 + 현재 열린 공고 전체를 CSV 로 내보낸다(신청 추적용).
|
||
// 실행: LD_LIBRARY_PATH=$ORACLE_IC_LIB_DIR node scripts/export_eligible_csv.js
|
||
import { writeFile, mkdir } from 'node:fs/promises';
|
||
import { withConnection, closePool, oracledb } from '../src/db.js';
|
||
|
||
const SQL = `
|
||
SELECT * FROM (
|
||
SELECT title, category, agency, source_code, detail_url, apply_start, apply_end,
|
||
CASE WHEN apply_end IS NULL THEN 9999 ELSE (apply_end - TRUNC(SYSDATE)) END AS dleft,
|
||
ROW_NUMBER() OVER (PARTITION BY title ORDER BY CASE source_code WHEN 'kstartup' THEN 1 WHEN 'bizinfo' THEN 2 ELSE 3 END) rn
|
||
FROM gov_opportunity
|
||
WHERE (apply_end IS NULL OR apply_end >= TRUNC(SYSDATE))
|
||
AND (
|
||
target LIKE '%' || :k1 || '%' OR target LIKE '%' || :k2 || '%'
|
||
OR DBMS_LOB.INSTR(body_text, :k1) > 0 OR DBMS_LOB.INSTR(body_text, :k2) > 0
|
||
)
|
||
)
|
||
WHERE rn = 1
|
||
ORDER BY dleft ASC, source_code`;
|
||
|
||
function csvCell(v) {
|
||
if (v == null) return '';
|
||
const s = String(v).replace(/"/g, '""').replace(/\r?\n/g, ' ');
|
||
return `"${s}"`;
|
||
}
|
||
function ymd(d) {
|
||
return d ? new Date(d).toISOString().slice(0, 10) : '';
|
||
}
|
||
function region(title) {
|
||
const m = /^\[([^\]]{1,8})\]/.exec(title);
|
||
return m ? m[1] : '';
|
||
}
|
||
|
||
const rows = await withConnection(async (conn) => {
|
||
const r = await conn.execute(
|
||
SQL,
|
||
{ k1: '예비창업', k2: '예비 창업' },
|
||
{ outFormat: oracledb.OUT_FORMAT_OBJECT }
|
||
);
|
||
return r.rows;
|
||
});
|
||
await closePool();
|
||
|
||
const header = ['신청완료', 'D-day', '마감일', '지역', '분야', '주관기관', '소스', '제목', '링크'];
|
||
const lines = [header.map(csvCell).join(',')];
|
||
for (const x of rows) {
|
||
lines.push(
|
||
[
|
||
'',
|
||
x.DLEFT === 9999 ? '상시' : `D-${x.DLEFT}`,
|
||
x.DLEFT === 9999 ? '상시/예산소진' : ymd(x.APPLY_END),
|
||
region(x.TITLE),
|
||
x.CATEGORY || '',
|
||
x.AGENCY || '',
|
||
x.SOURCE_CODE,
|
||
x.TITLE,
|
||
x.DETAIL_URL || '',
|
||
]
|
||
.map(csvCell)
|
||
.join(',')
|
||
);
|
||
}
|
||
|
||
await mkdir(new URL('../exports/', import.meta.url), { recursive: true });
|
||
const out = new URL('../exports/eligible_opportunities.csv', import.meta.url);
|
||
// 엑셀 한글 깨짐 방지 BOM
|
||
await writeFile(out, '' + lines.join('\n'), 'utf8');
|
||
console.log(`내보냄: ${rows.length}건 → government/exports/eligible_opportunities.csv`);
|