gov-scraper: 신청 체크리스트 연령(46세) 필터 추가
- generate_checklist.js: 본문 연령 상한 추출(만 N세 이하/범위) → 46세 미만이면 제외 - 제목 '청년/대학생' = 청년한정 제외, 단 '중장년/만40+이상/연령무관' 신호 있으면 유지 - apply-checklist.md: 지역(서울) + 연령(46세) 적용 → 252→122건 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import { withConnection, closePool, oracledb } from '../src/db.js';
|
||||
|
||||
const SQL = `
|
||||
SELECT * FROM (
|
||||
SELECT title, category, agency, source_code, detail_url, apply_end,
|
||||
SELECT title, category, agency, source_code, detail_url, apply_end, body_text,
|
||||
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
|
||||
@@ -46,6 +46,30 @@ const NON_SEOUL_BODY = [
|
||||
'여수', '순천', '포항', '창원', '김해', '춘천', '원주', '강릉',
|
||||
];
|
||||
|
||||
// 신청자 나이(MY_AGE) 기준 지원 가능 판정.
|
||||
// 본문에서 연령 상한을 추출해 최대 허용연령이 MY_AGE 미만이면 제외.
|
||||
// '만 40세 이상/중장년/시니어/연령무관' 신호가 있으면(중장년 트랙 포함) 유지.
|
||||
const MY_AGE = 46;
|
||||
|
||||
function ageAllows(x) {
|
||||
const text = `${x.TITLE}\n${x.BODY_TEXT || ''}`;
|
||||
// 46세 이상 허용 신호 → 유지 (만 40~59세 이상, 중장년/장년/시니어, 연령 무관/제한없음, 전 연령, 누구나)
|
||||
if (/만\s*[45]\d\s*세\s*이상|중장년|장년층|시니어|연령\s*무관|연령\s*제한\s*없|나이\s*제한\s*없|제한\s*없음|전\s*연령|누구나/.test(text)) {
|
||||
return true;
|
||||
}
|
||||
// 제목에 '청년/대학생' = 청년 한정 신호 → 제외 (46세 초과)
|
||||
if (/청년|대학생/.test(x.TITLE)) return false;
|
||||
// 연령 상한 추출
|
||||
const uppers = [];
|
||||
let m;
|
||||
const reUpper = /(\d{2})\s*세\s*(?:이하|미만|까지)/g;
|
||||
while ((m = reUpper.exec(text))) uppers.push(Number(m[1]));
|
||||
const reRange = /(\d{2})\s*세\s*[~∼\-–]\s*만?\s*(\d{2})\s*세/g;
|
||||
while ((m = reRange.exec(text))) uppers.push(Number(m[2]));
|
||||
if (uppers.length === 0) return true; // 연령 상한 언급 없음 → 유지
|
||||
return Math.max(...uppers) >= MY_AGE; // 최대 허용연령이 MY_AGE 이상이어야 지원 가능
|
||||
}
|
||||
|
||||
function applicableInSeoul(x) {
|
||||
const prefix = (/^\[([^\]]{1,8})\]/.exec(x.TITLE) || [])[1] || '';
|
||||
const strong = `${prefix} ${x.AGENCY || ''}`; // 접두 + 주관기관
|
||||
@@ -77,9 +101,14 @@ const allRows = await withConnection(async (conn) => {
|
||||
await closePool();
|
||||
|
||||
// 서울 거주 기준: 타 지역 한정 공고 제외
|
||||
const rows = allRows.filter(applicableInSeoul);
|
||||
const removed = allRows.length - rows.length;
|
||||
console.log(`지역 필터(서울 거주): 전체 ${allRows.length}건 → 유지 ${rows.length}건, 제거(타지역) ${removed}건`);
|
||||
const seoulRows = allRows.filter(applicableInSeoul);
|
||||
const removedRegion = allRows.length - seoulRows.length;
|
||||
// 연령(46세) 기준: 청년 한정 등 연령 초과 공고 제외
|
||||
const rows = seoulRows.filter(ageAllows);
|
||||
const removedAge = seoulRows.length - rows.length;
|
||||
console.log(
|
||||
`필터: 전체 ${allRows.length}건 → 지역(서울) -${removedRegion} → 연령(${MY_AGE}세) -${removedAge} → 최종 ${rows.length}건`
|
||||
);
|
||||
|
||||
const buckets = {
|
||||
'🔴 이번 주 마감 (D-0 ~ D-7)': (d) => d <= 7,
|
||||
@@ -92,7 +121,7 @@ const today = new Date().toISOString().slice(0, 10);
|
||||
const out = [];
|
||||
out.push('# 정부지원사업 신청 체크리스트');
|
||||
out.push('');
|
||||
out.push(`> 생성일: ${today} · 대상: **예비창업자 자격 + 현재 신청 가능 + 서울 거주 지원 가능**(타 지역 한정 제외) 공고 (총 ${rows.length}건)`);
|
||||
out.push(`> 생성일: ${today} · 대상: **예비창업자 + 현재 신청 가능 + 서울 거주 + 만 ${MY_AGE}세 지원 가능**(타 지역·청년 연령초과 제외) 공고 (총 ${rows.length}건)`);
|
||||
out.push('> 신청을 마치면 `[ ]` → `[x]` 로 체크하세요. 갱신: `LD_LIBRARY_PATH=$ORACLE_IC_LIB_DIR node scripts/generate_checklist.js`');
|
||||
out.push('> ⚠️ 마감 "시각"과 정확한 자격요건은 각 공고 원문에서 반드시 확인하세요.');
|
||||
out.push('');
|
||||
|
||||
Reference in New Issue
Block a user