- .agent/, .claude/, .vscode/ 설정 파일 - design/ 디자인 리소스 - reports/, research/ 분석 문서 - testcase/ 테스트 케이스 문서 - db_sync_chandj.bat, sam.code-workspace Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
483 lines
13 KiB
JavaScript
483 lines
13 KiB
JavaScript
/**
|
|
* 통합 실행 스크립트
|
|
* 'ppt' 또는 '실행' 명령어로 source/*.txt → PPTX 변환 실행
|
|
*/
|
|
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
const readline = require('readline');
|
|
const { spawn } = require('child_process');
|
|
|
|
// 색상 출력을 위한 유틸리티
|
|
const colors = {
|
|
reset: '\x1b[0m',
|
|
bright: '\x1b[1m',
|
|
green: '\x1b[32m',
|
|
blue: '\x1b[34m',
|
|
yellow: '\x1b[33m',
|
|
red: '\x1b[31m',
|
|
cyan: '\x1b[36m'
|
|
};
|
|
|
|
class PPTGenerator {
|
|
constructor() {
|
|
this.sourceDir = 'source';
|
|
this.outputDir = 'pptx';
|
|
this.scriptPath = '.claude/skills/text-analyzer-skill/scripts/txt-to-pptx.js';
|
|
}
|
|
|
|
/**
|
|
* 메인 실행 함수
|
|
*/
|
|
async run() {
|
|
this.printHeader();
|
|
|
|
try {
|
|
// 1. 환경 체크
|
|
await this.checkEnvironment();
|
|
|
|
// 2. 텍스트 파일 찾기
|
|
const txtFiles = await this.findTxtFiles();
|
|
|
|
if (txtFiles.length === 0) {
|
|
this.printError('source 폴더에 txt 파일이 없습니다.');
|
|
await this.createSampleFile();
|
|
return;
|
|
}
|
|
|
|
// 3. 파일 선택
|
|
const selectedFile = await this.selectFile(txtFiles);
|
|
|
|
if (!selectedFile) {
|
|
this.printInfo('작업이 취소되었습니다.');
|
|
return;
|
|
}
|
|
|
|
// 4. PPTX 생성
|
|
await this.generatePPTX(selectedFile);
|
|
|
|
} catch (error) {
|
|
this.printError(`실행 중 오류 발생: ${error.message}`);
|
|
console.error(error.stack);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 헤더 출력
|
|
*/
|
|
printHeader() {
|
|
console.clear();
|
|
console.log(colors.cyan + colors.bright);
|
|
console.log('██████████████████████████████████████████');
|
|
console.log('█ TXT → PPTX 자동 변환 시스템 █');
|
|
console.log('█ Text to PowerPoint Converter █');
|
|
console.log('██████████████████████████████████████████');
|
|
console.log(colors.reset);
|
|
console.log();
|
|
}
|
|
|
|
/**
|
|
* 환경 체크
|
|
*/
|
|
async checkEnvironment() {
|
|
this.printInfo('🔍 환경 확인 중...');
|
|
|
|
// source 디렉토리 체크
|
|
try {
|
|
await fs.access(this.sourceDir);
|
|
} catch {
|
|
await fs.mkdir(this.sourceDir, { recursive: true });
|
|
this.printSuccess(`✅ source 디렉토리 생성됨`);
|
|
}
|
|
|
|
// pptx 출력 디렉토리 체크
|
|
try {
|
|
await fs.access(this.outputDir);
|
|
} catch {
|
|
await fs.mkdir(this.outputDir, { recursive: true });
|
|
this.printSuccess(`✅ ${this.outputDir} 디렉토리 생성됨`);
|
|
}
|
|
|
|
// 변환 스크립트 체크
|
|
try {
|
|
await fs.access(this.scriptPath);
|
|
this.printSuccess('✅ 변환 스크립트 확인됨');
|
|
} catch {
|
|
this.printError(`❌ 변환 스크립트를 찾을 수 없습니다: ${this.scriptPath}`);
|
|
throw new Error('변환 스크립트가 없습니다.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 텍스트 파일 검색
|
|
*/
|
|
async findTxtFiles() {
|
|
try {
|
|
const files = await fs.readdir(this.sourceDir);
|
|
const txtFiles = files.filter(file => file.endsWith('.txt'));
|
|
|
|
this.printInfo(`📁 ${this.sourceDir} 디렉토리에서 ${txtFiles.length}개의 txt 파일을 찾았습니다.`);
|
|
|
|
return txtFiles.map(file => ({
|
|
name: file,
|
|
path: path.join(this.sourceDir, file),
|
|
size: 0 // 크기는 나중에 필요시 추가
|
|
}));
|
|
|
|
} catch (error) {
|
|
this.printError(`디렉토리 읽기 실패: ${error.message}`);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 파일 선택 인터페이스
|
|
*/
|
|
async selectFile(files) {
|
|
if (files.length === 1) {
|
|
this.printInfo(`📄 파일 자동 선택: ${files[0].name}`);
|
|
return files[0];
|
|
}
|
|
|
|
console.log(colors.yellow + '\n📋 변환할 파일을 선택하세요:' + colors.reset);
|
|
files.forEach((file, index) => {
|
|
console.log(` ${index + 1}. ${file.name}`);
|
|
});
|
|
console.log(` 0. 취소`);
|
|
console.log();
|
|
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout
|
|
});
|
|
|
|
return new Promise((resolve) => {
|
|
rl.question('선택 (번호 입력): ', (answer) => {
|
|
rl.close();
|
|
|
|
const choice = parseInt(answer);
|
|
|
|
if (choice === 0) {
|
|
resolve(null);
|
|
} else if (choice >= 1 && choice <= files.length) {
|
|
resolve(files[choice - 1]);
|
|
} else {
|
|
this.printError('잘못된 선택입니다.');
|
|
resolve(null);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* PPTX 생성 실행
|
|
*/
|
|
async generatePPTX(file) {
|
|
this.printInfo(`🚀 PPTX 생성 시작: ${file.name}`);
|
|
|
|
const outputFileName = file.name.replace('.txt', '_presentation.pptx');
|
|
const outputPath = path.join(this.outputDir, outputFileName);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn('node', [
|
|
this.scriptPath,
|
|
`--input=${file.path}`,
|
|
`--output=${outputPath}`
|
|
], {
|
|
stdio: 'inherit'
|
|
});
|
|
|
|
child.on('close', (code) => {
|
|
if (code === 0) {
|
|
this.printSuccess(`🎉 변환 완료!`);
|
|
this.printInfo(`📊 생성된 파일: ${outputPath}`);
|
|
|
|
// 파일 크기 표시
|
|
fs.stat(outputPath)
|
|
.then(stats => {
|
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
this.printInfo(`📏 파일 크기: ${sizeKB} KB`);
|
|
})
|
|
.catch(() => {});
|
|
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`변환 프로세스가 코드 ${code}로 종료되었습니다.`));
|
|
}
|
|
});
|
|
|
|
child.on('error', (error) => {
|
|
reject(new Error(`변환 프로세스 실행 실패: ${error.message}`));
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 샘플 파일 생성
|
|
*/
|
|
async createSampleFile() {
|
|
const samplePath = path.join(this.sourceDir, 'sample_project.txt');
|
|
|
|
try {
|
|
await fs.access(samplePath);
|
|
this.printInfo('샘플 파일이 이미 존재합니다: sample_project.txt');
|
|
return;
|
|
} catch {
|
|
// 파일이 없으면 생성
|
|
}
|
|
|
|
const sampleContent = `프로젝트명: 샘플 프로젝트
|
|
작성일: ${new Date().toISOString().split('T')[0].replace(/-/g, '.')}
|
|
회사명: Sample Company
|
|
작성자: 기획팀
|
|
|
|
=== 프로젝트 개요 ===
|
|
이것은 샘플 프로젝트입니다.
|
|
TXT → PPTX 변환 기능을 테스트하기 위한 예시 파일입니다.
|
|
|
|
=== 주요 기능 ===
|
|
|
|
1. 사용자 관리
|
|
- 회원 가입 및 로그인
|
|
- 프로필 관리
|
|
- 권한 설정
|
|
|
|
2. 콘텐츠 관리
|
|
- 게시글 작성 및 편집
|
|
- 파일 업로드
|
|
- 검색 기능
|
|
|
|
3. 시스템 관리
|
|
- 대시보드
|
|
- 설정 관리
|
|
- 로그 조회
|
|
|
|
=== 화면 구성 ===
|
|
|
|
메인 화면:
|
|
- 상단 네비게이션
|
|
- 콘텐츠 영역
|
|
- 사이드바
|
|
|
|
로그인 화면:
|
|
- 이메일 입력
|
|
- 비밀번호 입력
|
|
- 로그인 버튼
|
|
|
|
=== 기술 요구사항 ===
|
|
- 웹 기반 시스템
|
|
- 반응형 디자인
|
|
- 모바일 지원
|
|
- 클라우드 호스팅`;
|
|
|
|
try {
|
|
await fs.writeFile(samplePath, sampleContent, 'utf8');
|
|
this.printSuccess(`✅ 샘플 파일 생성됨: ${samplePath}`);
|
|
this.printInfo('💡 이제 다시 실행해보세요!');
|
|
} catch (error) {
|
|
this.printError(`샘플 파일 생성 실패: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 출력 유틸리티
|
|
*/
|
|
printSuccess(message) {
|
|
console.log(colors.green + message + colors.reset);
|
|
}
|
|
|
|
printInfo(message) {
|
|
console.log(colors.blue + message + colors.reset);
|
|
}
|
|
|
|
printError(message) {
|
|
console.log(colors.red + message + colors.reset);
|
|
}
|
|
|
|
printWarning(message) {
|
|
console.log(colors.yellow + message + colors.reset);
|
|
}
|
|
}
|
|
|
|
// 견적서 생성 함수
|
|
async function generateEstimate() {
|
|
try {
|
|
console.log(colors.cyan + '📊 SAM ERP 견적서 생성 중...' + colors.reset);
|
|
|
|
const { generateEstimatePPTX } = require('./estimate-pptx-generator.js');
|
|
const outputPath = await generateEstimatePPTX();
|
|
|
|
console.log(colors.green + `✅ 견적서 생성 완료: ${outputPath}` + colors.reset);
|
|
|
|
// 파일 크기 표시
|
|
const fs = require('fs').promises;
|
|
try {
|
|
const stats = await fs.stat(outputPath);
|
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
console.log(colors.blue + `📏 파일 크기: ${sizeKB} KB` + colors.reset);
|
|
} catch (error) {
|
|
// 파일 크기 확인 실패 시 무시
|
|
}
|
|
} catch (error) {
|
|
console.log(colors.red + `❌ 견적서 생성 실패: ${error.message}` + colors.reset);
|
|
}
|
|
}
|
|
|
|
// 템플릿 기반 생성 함수
|
|
async function generateFromTemplate() {
|
|
try {
|
|
console.log(colors.cyan + '📋 템플릿 기반 PPTX 생성 중...' + colors.reset);
|
|
|
|
const { generateFromTemplate } = require('./.claude/skills/pdf-template-skill/scripts/generate-from-template.js');
|
|
|
|
const templatePath = 'templates/sam_estimate_template.json';
|
|
const dataPath = 'data/sample_estimate_data.json';
|
|
const outputPath = 'pptx/template_customized.pptx';
|
|
|
|
await generateFromTemplate(templatePath, dataPath, outputPath);
|
|
|
|
console.log(colors.green + `✅ 템플릿 기반 PPTX 생성 완료: ${outputPath}` + colors.reset);
|
|
|
|
// 파일 크기 표시
|
|
const fs = require('fs').promises;
|
|
try {
|
|
const stats = await fs.stat(outputPath);
|
|
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
console.log(colors.blue + `📏 파일 크기: ${sizeKB} KB` + colors.reset);
|
|
} catch (error) {
|
|
// 파일 크기 확인 실패 시 무시
|
|
}
|
|
} catch (error) {
|
|
console.log(colors.red + `❌ 템플릿 기반 생성 실패: ${error.message}` + colors.reset);
|
|
}
|
|
}
|
|
|
|
// 대화형 실행 인터페이스
|
|
async function interactiveMode() {
|
|
const generator = new PPTGenerator();
|
|
|
|
console.log();
|
|
console.log(colors.cyan + '🎯 명령어 입력:' + colors.reset);
|
|
console.log(' • "ppt" 또는 "실행" - TXT → PPTX 변환');
|
|
console.log(' • "estimate" 또는 "견적서" - SAM ERP 견적서 생성');
|
|
console.log(' • "template" 또는 "템플릿" - 템플릿 기반 PPTX 생성');
|
|
console.log(' • "help" 또는 "도움말" - 도움말');
|
|
console.log(' • "exit" 또는 "종료" - 프로그램 종료');
|
|
console.log();
|
|
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout,
|
|
prompt: colors.bright + '> ' + colors.reset
|
|
});
|
|
|
|
rl.prompt();
|
|
|
|
rl.on('line', async (input) => {
|
|
const command = input.trim().toLowerCase();
|
|
|
|
switch (command) {
|
|
case 'ppt':
|
|
case '실행':
|
|
await generator.run();
|
|
break;
|
|
|
|
case 'estimate':
|
|
case '견적서':
|
|
await generateEstimate();
|
|
break;
|
|
|
|
case 'template':
|
|
case '템플릿':
|
|
await generateFromTemplate();
|
|
break;
|
|
|
|
case 'help':
|
|
case '도움말':
|
|
showHelp();
|
|
break;
|
|
|
|
case 'exit':
|
|
case '종료':
|
|
case 'quit':
|
|
console.log(colors.green + '👋 프로그램을 종료합니다.' + colors.reset);
|
|
rl.close();
|
|
return;
|
|
|
|
case '':
|
|
// 빈 입력 무시
|
|
break;
|
|
|
|
default:
|
|
console.log(colors.yellow + '❓ 알 수 없는 명령어입니다. "help"를 입력하세요.' + colors.reset);
|
|
break;
|
|
}
|
|
|
|
rl.prompt();
|
|
});
|
|
|
|
rl.on('close', () => {
|
|
process.exit(0);
|
|
});
|
|
}
|
|
|
|
// 도움말 표시
|
|
function showHelp() {
|
|
console.log();
|
|
console.log(colors.cyan + colors.bright + '📖 TXT → PPTX 변환기 사용법' + colors.reset);
|
|
console.log();
|
|
console.log('🔹 기본 사용법:');
|
|
console.log(' 1. source 폴더에 txt 파일을 넣으세요');
|
|
console.log(' 2. "ppt" 또는 "실행" 명령어를 입력하세요');
|
|
console.log(' 3. 파일을 선택하고 변환을 실행하세요');
|
|
console.log();
|
|
console.log('🔹 txt 파일 형식:');
|
|
console.log(' - 프로젝트명: [제목]');
|
|
console.log(' - 작성일: [날짜]');
|
|
console.log(' - === 섹션 제목 ===');
|
|
console.log(' - 1. 번호 목록');
|
|
console.log(' - • 불릿 포인트');
|
|
console.log();
|
|
console.log('🔹 출력:');
|
|
console.log(' - pptx 폴더에 PowerPoint 파일 생성');
|
|
console.log(' - PDF 샘플과 동일한 구조');
|
|
console.log();
|
|
console.log('🔹 명령어:');
|
|
console.log(' • ppt, 실행 - TXT → PPTX 변환 실행');
|
|
console.log(' • estimate, 견적서 - SAM ERP 견적서 생성');
|
|
console.log(' • help, 도움말 - 이 도움말');
|
|
console.log(' • exit, 종료 - 프로그램 종료');
|
|
console.log();
|
|
}
|
|
|
|
// 메인 실행
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
|
|
// 명령행 인수가 있으면 직접 실행
|
|
if (args.length > 0) {
|
|
const command = args[0].toLowerCase();
|
|
|
|
if (['ppt', '실행', 'run'].includes(command)) {
|
|
const generator = new PPTGenerator();
|
|
await generator.run();
|
|
} else if (['help', '도움말', '--help'].includes(command)) {
|
|
showHelp();
|
|
} else {
|
|
console.log('사용법: node run.js [ppt|실행|help|도움말]');
|
|
}
|
|
} else {
|
|
// 대화형 모드
|
|
await interactiveMode();
|
|
}
|
|
}
|
|
|
|
// 직접 실행시
|
|
if (require.main === module) {
|
|
main().catch(error => {
|
|
console.error(colors.red + '❌ 실행 오류:', error.message + colors.reset);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { PPTGenerator }; |