Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/arts905_ccf-deadlines-pro.json
T

1 line
20 KiB
JSON

{"repo": "arts905/ccf-deadlines-pro", "files": {"scripts/enrich-conference-data.js": {"9c059675ba1b3d361b477c07d389319a183bc12e": {"t": 1785746672.6864185, "n": 8475, "h": "b2c965b0ad0a4c0b", "c": "/**\n * 会议数据增强脚本\n * 1. 从 GitHub 获取录用率数据\n * 2. 用 DeepSeek 生成关键词\n * 3. 更新到 Supabase\n */\n\nrequire('dotenv').config({ path: require('path').join(__dirname, '../.env.local') });\n\nconst https = require('https');\nconst { createClient } = require('@supabase/supabase-js');\n\n// 配置\nconst GITHUB_RAW_URL = 'https://raw.githubusercontent.com/lixin4ever/Conference-Acceptance-Rate/master/README.md';\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\nconst SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL;\nconst SUPABASE_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;\n\n// 分批处理配置\nconst BATCH_SIZE = 30; // 每批处理数量(减小以避免超时)\nconst BATCH_DELAY = 2000; // 批次间隔(毫秒)\n// 支持: node script.js 50 或 START_FROM=50 node script.js\nconst START_FROM = parseInt(process.argv[2] || process.env.START_FROM || '0'); // 从第几个开始\n\n// 会议名称映射(GitHub名称 -> 数据库名称)\nconst CONF_NAME_MAP = {\n 'ACL': 'ACL',\n 'EMNLP': 'EMNLP',\n 'NAACL-HLT': 'NAACL',\n 'NAACL': 'NAACL',\n 'COLING': 'COLING',\n 'CVPR': 'CVPR',\n 'ICCV': 'ICCV',\n 'ECCV': 'ECCV',\n 'ICML': 'ICML',\n 'NeurIPS': 'NeurIPS',\n 'NIPS': 'NeurIPS',\n 'ICLR': 'ICLR',\n 'COLT': 'COLT',\n 'UAI': 'UAI',\n 'AISTATS': 'AISTATS',\n 'AAAI': 'AAAI',\n 'IJCAI': 'IJCAI',\n 'KDD': 'KDD',\n 'SIGIR': 'SIGIR',\n 'TheWebConf': 'TheWebConf',\n 'WWW': 'TheWebConf',\n 'WSDM': 'WSDM',\n 'CIKM': 'CIKM',\n 'ICDM': 'ICDM',\n 'RecSys': 'RecSys',\n 'INTERSPEECH': 'INTERSPEECH',\n 'ICASSP': 'ICASSP',\n};\n\n// HTTP GET 请求\nfunction httpsGet(url) {\n return new Promise((resolve, reject) => {\n https.get(url, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => resolve(data));\n }).on('error', reject);\n });\n}\n\n// 解析 GitHub README 中的录用率数据\nfunction parseAcceptanceRates(markdown) {\n const rates = {};\n\n // 匹配表格行: | Conference'YY | XX.X% (accepted/total) | ... |\n const rowRegex = /\\|\\s*([A-Z][A-Za-z\\-]+)'(\\d{2})\\s*\\|\\s*([\\d.]+)%\\s*\\((\\d+)\\/(\\d+)\\)/g;\n\n let match;\n while ((match = rowRegex.exec(markdown)) !== null) {\n const [, confName, yearShort, rate, accepted, total] = match;\n const fullYear = 2000 + parseInt(yearShort);\n const normalizedName = CONF_NAME_MAP[confName] || confName;\n\n if (!rates[normalizedName]) {\n rates[normalizedName] = [];\n }\n\n rates[normalizedName].push({\n year: fullYear,\n rate: parseFloat(rate),\n accepted: parseInt(accepted),\n total: parseInt(total)\n });\n }\n\n // 按年份排序\n for (const conf in rates) {\n rates[conf].sort((a, b) => a.year - b.year);\n }\n\n return rates;\n}\n\n// 延迟函数\nfunction delay(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n// 带重试的 DeepSeek API 调用\nasync function callDeepSeekAPI(prompt, retries = 3) {\n for (let i = 0; i < retries; i++) {\n try {\n const response = await fetch('https://api.deepseek.com/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`\n },\n body: JSON.stringify({\n model: 'deepseek-chat',\n messages: [{ role: 'user', content: prompt }],\n temperature: 0.3,\n max_tokens: 200\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(` [API错误] HTTP ${response.status}: ${errorText.substring(0, 100)}`);\n\n // 如果是 429 (限流) 或 500 (服务器错误),等待后重试\n if (response.status === 429 || response.status >= 500) {\n const waitTime = (i + 1) * 3000; // 递增等待时间\n console.log(` [重试] 等待 ${waitTime/1000}s 后重试 (${i + 1}/${retries})...`);\n await delay(waitTime);\n continue;\n }\n return null;\n }\n\n const data = await response.json();\n return data.choices?.[0]?.message?.content || '[]';\n } catch (e) {\n console.error(` [网络错误] ${e.message}`);\n if (i < retries - 1) {\n console.log(` [重试] 等待 3s 后重试 (${i + 1}/${retries})...`);\n await delay(3000);\n }\n }\n }\n return null;\n}\n\n// 用 DeepSeek 生成关键词\nasync function generateKeywords(conference) {\n if (!DEEPSEEK_API_KEY) {\n return null;\n }\n\n const prompt = `请为以下学术会议生成 5-8 个研究主题关键词(中文),用于论文匹配。\n\n会议名称: ${conference.title}\n会议描述: ${conference.description || '无'}\n领域分类: ${conference.sub || '未知'}\n\n要求:\n1. 关键词应涵盖该会议的主要研究方向\n2. 使用中文,每个关键词 2-6 个字\n3. 返回 JSON 数组格式,如 [\"深度学习\", \"计算机视觉\", \"自然语言处理\"]\n\n只返回 JSON 数组,不要其他内容。`;\n\n const content = await callDeepSeekAPI(prompt);\n if (!content) return null;\n\n try {\n // 提取 JSON 数组\n const jsonMatch = content.match(/\\[.*\\]/s);\n if (jsonMatch) {\n return JSON.parse(jsonMatch[0]);\n }\n return null;\n } catch (e) {\n console.error(` [解析错误] ${e.message}`);\n return null;\n }\n}\n\n// 主函数\nasync function main() {\n console.log('='.repeat(50));\n console.log('会议数据增强脚本');\n console.log('='.repeat(50));\n\n // 1. 获取 GitHub 录用率数据\n console.log('\\n[1/4] 从 GitHub 获取录用率数据...');\n let acceptanceRates = {};\n try {\n const markdown = await httpsGet(GITHUB_RAW_URL);\n acceptanceRates = parseAcceptanceRates(markdown);\n console.log(` 成功解析 ${Object.keys(acceptanceRates).length} 个会议的录用率数据`);\n\n // 打印示例\n const sampleConf = Object.keys(acceptanceRates)[0];\n if (sampleConf) {\n console.log(` 示例: ${sampleConf} - ${JSON.stringify(acceptanceRates[sampleConf].slice(-2))}`);\n }\n } catch (e) {\n console.error(` [错误] 获取录用率失败: ${e.message}`);\n }\n\n // 2. 连接 Supabase\n console.log('\\n[2/4] 连接 Supabase...');\n if (!SUPABASE_URL || !SUPABASE_KEY) {\n console.error(' [错误] 缺少 Supabase 配置');\n process.exit(1);\n }\n const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);\n\n // 获取所有会议\n const { data: conferences, error } = await supabase\n .from('conferences')\n .select('id, title, description, sub');\n\n if (error) {\n console.error(` [错误] 查询会议失败: ${error.message}`);\n process.exit(1);\n }\n console.log(` 找到 ${conferences.length} 个会议`);\n\n // 3. 为每个会议匹配录用率和生成关键词\n console.log('\\n[3/4] 处理会议数据...');\n let updated = 0;\n\n // 分批处理\n const totalBatches = Math.ceil(conferences.length / BATCH_SIZE);\n const startIndex = START_FROM;\n const endIndex = Math.min(startIndex + BATCH_SIZE, conferences.length);\n const batchConferences = conferences.slice(startIndex, endIndex);\n\n console.log(` 总共 ${conferences.length} 个会议`);\n console.log(` 本次处理: ${startIndex + 1} - ${endIndex} (共 ${batchConferences.length} 个)`);\n console.log(` 提示: 设置 START_FROM 环境变量可从指定位置继续\\n`);\n\n for (const conf of batchConferences) {\n const shortName = conf.title.split(' ')[0].toUpperCase();\n const rates = acceptanceRates[shortName];\n\n if (!rates && !DEEPSEEK_API_KEY) continue;\n\n console.log(`\\n处理: ${conf.title}`);\n\n // 匹配录用率\n if (rates) {\n console.log(` 录用率: ${rates.length} 年数据`);\n console.log(` 最新: ${rates[rates.length - 1].year} - ${rates[rates.length - 1].rate}%`);\n }\n\n // 生成关键词\n let keywords = null;\n if (DEEPSEEK_API_KEY) {\n console.log(' 正在生成关键词...');\n keywords = await generateKeywords(conf);\n if (keywords) {\n console.log(` 关键词: ${keywords.slice(0, 3).join(', ')}...`);\n }\n // 避免请求过快\n await delay(1500);\n }\n\n // 更新数据库\n const updateData = {};\n if (rates) updateData.acceptance_rate = rates;\n if (keywords) updateData.keywords = keywords;\n\n if (Object.keys(updateData).length > 0) {\n const { error: updateError } = await supabase\n .from('conferences')\n .update(updateData)\n .eq('id', conf.id);\n\n if (updateError) {\n console.error(` [错误] 更新失败: ${updateError.message}`);\n } else {\n updated++;\n console.log(` ✓ 已更新`);\n }\n }\n }\n\n console.log('\\n[4/4] 完成');\n console.log(` 本批处理了 ${updated} 个会议`);\n\n // 显示进度和下一步\n const progress = endIndex >= conferences.length;\n if (progress) {\n console.log('\\n✅ 所有会议已处理完成!');\n } else {\n console.log(`\\n📊 进度: ${endIndex}/${conferences.length}`);\n console.log(`\\n💡 继续处理下一批,请运行:`);\n console.log(` START_FROM=${endIndex} npm run enrich-data`);\n }\n\n // 输出 JSON 供确认\n console.log('\\n' + '='.repeat(50));\n console.log('录用率数据预览 (前5个会议):');\n console.log('='.repeat(50));\n\n const preview = {};\n for (const [name, rates] of Object.entries(acceptanceRates).slice(0, 5)) {\n preview[name] = {\n 最新录用率: rates[rates.length - 1]?.rate + '%',\n 年份数: rates.length,\n 最近三年: rates.slice(-3).map(r => `${r.year}: ${r.rate}%`)\n };\n }\n console.log(JSON.stringify(preview, null, 2));\n}\n\nmain().catch(console.error);\n"}, "699411d296f90a39481b8b31864a05c7deb9a85e": {"t": 1785766736.7599986, "n": 8475, "h": "b2c965b0ad0a4c0b", "c": "/**\n * 会议数据增强脚本\n * 1. 从 GitHub 获取录用率数据\n * 2. 用 DeepSeek 生成关键词\n * 3. 更新到 Supabase\n */\n\nrequire('dotenv').config({ path: require('path').join(__dirname, '../.env.local') });\n\nconst https = require('https');\nconst { createClient } = require('@supabase/supabase-js');\n\n// 配置\nconst GITHUB_RAW_URL = 'https://raw.githubusercontent.com/lixin4ever/Conference-Acceptance-Rate/master/README.md';\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\nconst SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL;\nconst SUPABASE_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;\n\n// 分批处理配置\nconst BATCH_SIZE = 30; // 每批处理数量(减小以避免超时)\nconst BATCH_DELAY = 2000; // 批次间隔(毫秒)\n// 支持: node script.js 50 或 START_FROM=50 node script.js\nconst START_FROM = parseInt(process.argv[2] || process.env.START_FROM || '0'); // 从第几个开始\n\n// 会议名称映射(GitHub名称 -> 数据库名称)\nconst CONF_NAME_MAP = {\n 'ACL': 'ACL',\n 'EMNLP': 'EMNLP',\n 'NAACL-HLT': 'NAACL',\n 'NAACL': 'NAACL',\n 'COLING': 'COLING',\n 'CVPR': 'CVPR',\n 'ICCV': 'ICCV',\n 'ECCV': 'ECCV',\n 'ICML': 'ICML',\n 'NeurIPS': 'NeurIPS',\n 'NIPS': 'NeurIPS',\n 'ICLR': 'ICLR',\n 'COLT': 'COLT',\n 'UAI': 'UAI',\n 'AISTATS': 'AISTATS',\n 'AAAI': 'AAAI',\n 'IJCAI': 'IJCAI',\n 'KDD': 'KDD',\n 'SIGIR': 'SIGIR',\n 'TheWebConf': 'TheWebConf',\n 'WWW': 'TheWebConf',\n 'WSDM': 'WSDM',\n 'CIKM': 'CIKM',\n 'ICDM': 'ICDM',\n 'RecSys': 'RecSys',\n 'INTERSPEECH': 'INTERSPEECH',\n 'ICASSP': 'ICASSP',\n};\n\n// HTTP GET 请求\nfunction httpsGet(url) {\n return new Promise((resolve, reject) => {\n https.get(url, (res) => {\n let data = '';\n res.on('data', chunk => data += chunk);\n res.on('end', () => resolve(data));\n }).on('error', reject);\n });\n}\n\n// 解析 GitHub README 中的录用率数据\nfunction parseAcceptanceRates(markdown) {\n const rates = {};\n\n // 匹配表格行: | Conference'YY | XX.X% (accepted/total) | ... |\n const rowRegex = /\\|\\s*([A-Z][A-Za-z\\-]+)'(\\d{2})\\s*\\|\\s*([\\d.]+)%\\s*\\((\\d+)\\/(\\d+)\\)/g;\n\n let match;\n while ((match = rowRegex.exec(markdown)) !== null) {\n const [, confName, yearShort, rate, accepted, total] = match;\n const fullYear = 2000 + parseInt(yearShort);\n const normalizedName = CONF_NAME_MAP[confName] || confName;\n\n if (!rates[normalizedName]) {\n rates[normalizedName] = [];\n }\n\n rates[normalizedName].push({\n year: fullYear,\n rate: parseFloat(rate),\n accepted: parseInt(accepted),\n total: parseInt(total)\n });\n }\n\n // 按年份排序\n for (const conf in rates) {\n rates[conf].sort((a, b) => a.year - b.year);\n }\n\n return rates;\n}\n\n// 延迟函数\nfunction delay(ms) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n// 带重试的 DeepSeek API 调用\nasync function callDeepSeekAPI(prompt, retries = 3) {\n for (let i = 0; i < retries; i++) {\n try {\n const response = await fetch('https://api.deepseek.com/chat/completions', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`\n },\n body: JSON.stringify({\n model: 'deepseek-chat',\n messages: [{ role: 'user', content: prompt }],\n temperature: 0.3,\n max_tokens: 200\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n console.error(` [API错误] HTTP ${response.status}: ${errorText.substring(0, 100)}`);\n\n // 如果是 429 (限流) 或 500 (服务器错误),等待后重试\n if (response.status === 429 || response.status >= 500) {\n const waitTime = (i + 1) * 3000; // 递增等待时间\n console.log(` [重试] 等待 ${waitTime/1000}s 后重试 (${i + 1}/${retries})...`);\n await delay(waitTime);\n continue;\n }\n return null;\n }\n\n const data = await response.json();\n return data.choices?.[0]?.message?.content || '[]';\n } catch (e) {\n console.error(` [网络错误] ${e.message}`);\n if (i < retries - 1) {\n console.log(` [重试] 等待 3s 后重试 (${i + 1}/${retries})...`);\n await delay(3000);\n }\n }\n }\n return null;\n}\n\n// 用 DeepSeek 生成关键词\nasync function generateKeywords(conference) {\n if (!DEEPSEEK_API_KEY) {\n return null;\n }\n\n const prompt = `请为以下学术会议生成 5-8 个研究主题关键词(中文),用于论文匹配。\n\n会议名称: ${conference.title}\n会议描述: ${conference.description || '无'}\n领域分类: ${conference.sub || '未知'}\n\n要求:\n1. 关键词应涵盖该会议的主要研究方向\n2. 使用中文,每个关键词 2-6 个字\n3. 返回 JSON 数组格式,如 [\"深度学习\", \"计算机视觉\", \"自然语言处理\"]\n\n只返回 JSON 数组,不要其他内容。`;\n\n const content = await callDeepSeekAPI(prompt);\n if (!content) return null;\n\n try {\n // 提取 JSON 数组\n const jsonMatch = content.match(/\\[.*\\]/s);\n if (jsonMatch) {\n return JSON.parse(jsonMatch[0]);\n }\n return null;\n } catch (e) {\n console.error(` [解析错误] ${e.message}`);\n return null;\n }\n}\n\n// 主函数\nasync function main() {\n console.log('='.repeat(50));\n console.log('会议数据增强脚本');\n console.log('='.repeat(50));\n\n // 1. 获取 GitHub 录用率数据\n console.log('\\n[1/4] 从 GitHub 获取录用率数据...');\n let acceptanceRates = {};\n try {\n const markdown = await httpsGet(GITHUB_RAW_URL);\n acceptanceRates = parseAcceptanceRates(markdown);\n console.log(` 成功解析 ${Object.keys(acceptanceRates).length} 个会议的录用率数据`);\n\n // 打印示例\n const sampleConf = Object.keys(acceptanceRates)[0];\n if (sampleConf) {\n console.log(` 示例: ${sampleConf} - ${JSON.stringify(acceptanceRates[sampleConf].slice(-2))}`);\n }\n } catch (e) {\n console.error(` [错误] 获取录用率失败: ${e.message}`);\n }\n\n // 2. 连接 Supabase\n console.log('\\n[2/4] 连接 Supabase...');\n if (!SUPABASE_URL || !SUPABASE_KEY) {\n console.error(' [错误] 缺少 Supabase 配置');\n process.exit(1);\n }\n const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);\n\n // 获取所有会议\n const { data: conferences, error } = await supabase\n .from('conferences')\n .select('id, title, description, sub');\n\n if (error) {\n console.error(` [错误] 查询会议失败: ${error.message}`);\n process.exit(1);\n }\n console.log(` 找到 ${conferences.length} 个会议`);\n\n // 3. 为每个会议匹配录用率和生成关键词\n console.log('\\n[3/4] 处理会议数据...');\n let updated = 0;\n\n // 分批处理\n const totalBatches = Math.ceil(conferences.length / BATCH_SIZE);\n const startIndex = START_FROM;\n const endIndex = Math.min(startIndex + BATCH_SIZE, conferences.length);\n const batchConferences = conferences.slice(startIndex, endIndex);\n\n console.log(` 总共 ${conferences.length} 个会议`);\n console.log(` 本次处理: ${startIndex + 1} - ${endIndex} (共 ${batchConferences.length} 个)`);\n console.log(` 提示: 设置 START_FROM 环境变量可从指定位置继续\\n`);\n\n for (const conf of batchConferences) {\n const shortName = conf.title.split(' ')[0].toUpperCase();\n const rates = acceptanceRates[shortName];\n\n if (!rates && !DEEPSEEK_API_KEY) continue;\n\n console.log(`\\n处理: ${conf.title}`);\n\n // 匹配录用率\n if (rates) {\n console.log(` 录用率: ${rates.length} 年数据`);\n console.log(` 最新: ${rates[rates.length - 1].year} - ${rates[rates.length - 1].rate}%`);\n }\n\n // 生成关键词\n let keywords = null;\n if (DEEPSEEK_API_KEY) {\n console.log(' 正在生成关键词...');\n keywords = await generateKeywords(conf);\n if (keywords) {\n console.log(` 关键词: ${keywords.slice(0, 3).join(', ')}...`);\n }\n // 避免请求过快\n await delay(1500);\n }\n\n // 更新数据库\n const updateData = {};\n if (rates) updateData.acceptance_rate = rates;\n if (keywords) updateData.keywords = keywords;\n\n if (Object.keys(updateData).length > 0) {\n const { error: updateError } = await supabase\n .from('conferences')\n .update(updateData)\n .eq('id', conf.id);\n\n if (updateError) {\n console.error(` [错误] 更新失败: ${updateError.message}`);\n } else {\n updated++;\n console.log(` ✓ 已更新`);\n }\n }\n }\n\n console.log('\\n[4/4] 完成');\n console.log(` 本批处理了 ${updated} 个会议`);\n\n // 显示进度和下一步\n const progress = endIndex >= conferences.length;\n if (progress) {\n console.log('\\n✅ 所有会议已处理完成!');\n } else {\n console.log(`\\n📊 进度: ${endIndex}/${conferences.length}`);\n console.log(`\\n💡 继续处理下一批,请运行:`);\n console.log(` START_FROM=${endIndex} npm run enrich-data`);\n }\n\n // 输出 JSON 供确认\n console.log('\\n' + '='.repeat(50));\n console.log('录用率数据预览 (前5个会议):');\n console.log('='.repeat(50));\n\n const preview = {};\n for (const [name, rates] of Object.entries(acceptanceRates).slice(0, 5)) {\n preview[name] = {\n 最新录用率: rates[rates.length - 1]?.rate + '%',\n 年份数: rates.length,\n 最近三年: rates.slice(-3).map(r => `${r.year}: ${r.rate}%`)\n };\n }\n console.log(JSON.stringify(preview, null, 2));\n}\n\nmain().catch(console.error);\n"}}}}