/** * @file FlashcardManager.ts * @description This file defines the FlashcardManager class, responsible for managing API routes * related to flashcard creation and manipulation. It provides functionality for handling file processing, * running Python scripts in a virtual environment, and managing dependencies. */ import { spawn } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import { Method } from '../RouteManager'; import ApiManager, { Registration } from './ApiManager'; /** * Runs a Python script using the provided virtual environment and passes file and option arguments. * @param {string} venvPath - Path to the virtual environment. * @param {string} scriptPath - Path to the Python script. * @param {string} [file] - Optional file to pass to the Python script. * @param {string} [drag] - Optional argument to control drag mode. * @param {string} [smart] - Optional argument to control smart mode. * @returns {Promise} - Resolves with the output from the Python script, or rejects on error. */ function runPythonScript(venvPath: string, scriptPath: string, file?: string, drag?: string, smart?: string): Promise { return new Promise((resolve, reject) => { const pythonPath = process.platform === 'win32' ? path.join(venvPath, 'Scripts', 'python.exe') : path.join(venvPath, 'bin', 'python3'); const tempFilePath = path.join(__dirname, `temp_data.txt`); // Unique temp file name if (file) { // Write the raw file data to the temp file without conversion fs.writeFileSync(tempFilePath, file, 'utf8'); } const pythonProcess = spawn( pythonPath, [scriptPath, file ? tempFilePath : undefined, drag, smart].filter(arg => arg !== undefined) ); let pythonOutput = ''; let stderrOutput = ''; pythonProcess.stdout.on('data', data => { pythonOutput += data.toString(); }); pythonProcess.stderr.on('data', data => { stderrOutput += data.toString(); }); pythonProcess.on('close', code => { if (code === 0) { resolve(pythonOutput); } else { reject(`Python process exited with code ${code}: ${stderrOutput}`); } }); }); } /** * Installs Python dependencies using pip in the specified virtual environment. * @param {string} venvPath - Path to the virtual environment. * @param {string} requirementsPath - Path to the requirements.txt file. * @returns {Promise} - Resolves when dependencies are successfully installed, rejects on failure. */ function installDependencies(venvPath: string, requirementsPath: string): Promise { return new Promise((resolve, reject) => { const pipPath = process.platform === 'win32' ? path.join(venvPath, 'Scripts', 'pip.exe') : path.join(venvPath, 'bin', 'pip3'); const installProcess = spawn(pipPath, ['install', '-r', requirementsPath]); installProcess.stdout.on('data', data => { console.log(`pip stdout: ${data}`); }); installProcess.stderr.on('data', data => { console.error(`pip stderr: ${data}`); }); installProcess.on('close', code => { if (code !== 0) { reject(`Failed to install dependencies. Exit code: ${code}`); } else { resolve(); } }); }); } /** * Creates a new Python virtual environment. * @param {string} venvPath - Path to the virtual environment that will be created. * @returns {Promise} - Resolves when the virtual environment is successfully created, rejects on failure. */ function createVirtualEnvironment(venvPath: string): Promise { return new Promise((resolve, reject) => { const createVenvProcess = spawn('python3', ['-m', 'venv', venvPath]); createVenvProcess.on('close', code => { if (code !== 0) { reject(`Failed to create virtual environment. Exit code: ${code}`); } else { resolve(); } }); }); } /** * Manages the creation of the virtual environment, installation of dependencies, and running of the Python script. * @param {string} [file] - Optional file data to be processed by the Python script. * @param {string} [drag] - Optional argument controlling drag mode. * @param {string} [smart] - Optional argument controlling smart mode. * @returns {Promise} - Resolves with the Python script output, or rejects on failure. */ async function manageVenvAndRunScript(file?: string, drag?: string, smart?: string): Promise { const venvPath = path.join(__dirname, '../flashcard/venv'); // Virtual environment path const requirementsPath = path.join(__dirname, '../flashcard/requirements.txt'); const pythonScriptPath = path.join(__dirname, '../flashcard/labels.py'); console.log('venvPath:', venvPath); // Check if the virtual environment exists if (!fs.existsSync(path.join(venvPath, 'bin', 'python3')) && !fs.existsSync(path.join(venvPath, 'Scripts', 'python.exe'))) { await createVirtualEnvironment(venvPath); await installDependencies(venvPath, requirementsPath); } return runPythonScript(venvPath, pythonScriptPath, file, drag, smart); } /** * FlashcardManager class responsible for managing API routes related to flashcard functionality. * It initializes API routes for handling YouTube subscriptions and label creation using a Python backend. */ export default class FlashcardManager extends ApiManager { /** * Initializes the API routes for the FlashcardManager class. * @param {Registration} register - The registration function for defining API routes. */ protected initialize(register: Registration): void { register({ method: Method.POST, subscription: '/labels', secureHandler: async ({ req, res }) => { const { file, drag, smart } = req.body; try { // Run the Python process const result = await manageVenvAndRunScript(file, drag, smart); res.status(200).send({ result }); } catch (error) { console.error('Error initiating document creation:', error); res.status(500).send({ error: 'Failed to initiate document creation', }); } }, }); } }