ailixir-api / app /Jobs /RunDockingJob.php
AILIXIR Bot
Auto-sync: 201d269f287526269b8ffded5f450ff36b46c740
907b200
Raw
History Blame Contribute Delete
4.32 kB
<?php
namespace App\Jobs;
use App\Models\DockingJob;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Process;
class RunDockingJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $dockingJob;
public $params;
/**
* The number of seconds the job can run before timing out.
*
* @var int
*/
public $timeout = 1800;
/**
* Create a new job instance.
*/
public function __construct(DockingJob $dockingJob, array $params)
{
$this->dockingJob = $dockingJob;
$this->params = $params;
}
/**
* Execute the job.
*/
public function handle(): void
{
// Mark as processing
$this->dockingJob->update(['status' => 'processing']);
try {
$scriptPath = base_path('scripts/vina_docking.py');
$pythonPath = env('DOCKING_PYTHON_PATH', base_path('vina_env/bin/python'));
// Build command
$cmd = sprintf(
'%s %s %s %s %f %f %f %f %f %f %d %d',
escapeshellarg($pythonPath),
escapeshellarg($scriptPath),
escapeshellarg($this->dockingJob->protein_path),
escapeshellarg($this->dockingJob->ligand_path),
$this->params['center_x'],
$this->params['center_y'],
$this->params['center_z'],
$this->params['box_size_x'],
$this->params['box_size_y'],
$this->params['box_size_z'],
$this->params['exhaustiveness'] ?? 8,
$this->params['n_poses'] ?? 5
);
// Run process
$result = Process::timeout($this->timeout)->run($cmd);
// Using regex to reliably find our JSON object in the mixed python Vina logs output
$outputData = null;
$fullOutput = $result->output();
if (preg_match('/\{"status":\s*"(success|error)".*\}/s', $fullOutput, $matches)) {
$outputData = json_decode($matches[0], true);
}
// Also check standard error in case Python printed an error payload there
if (! $outputData && preg_match('/\{"status":\s*"(success|error)".*\}/s', $result->errorOutput(), $matches)) {
$outputData = json_decode($matches[0], true);
}
if (! $result->successful()) {
$errorMessage = $outputData['message'] ?? $result->errorOutput();
if (empty(trim($errorMessage))) {
$errorMessage = trim($fullOutput) ?: 'Unknown Error / Empty Output';
}
throw new \Exception(trim($errorMessage));
}
if (isset($outputData['status']) && $outputData['status'] === 'error') {
throw new \Exception($outputData['message'] ?? 'Unknown script error');
}
$vinaScores = [];
if ($outputData && isset($outputData['energies'])) {
foreach ($outputData['energies'] as $pose) {
$vinaScores[] = [
'affinity' => (float) ($pose[0] ?? 0.0),
'inter' => (float) ($pose[1] ?? 0.0),
'intra' => (float) ($pose[2] ?? 0.0),
'torsions' => (float) ($pose[3] ?? 0.0),
'unbound' => (float) ($pose[4] ?? 0.0),
];
}
unset($outputData['energies']);
}
$this->dockingJob->update([
'status' => 'completed',
'result_data' => $outputData ?: ['raw_output' => $fullOutput],
'vina_scores' => $vinaScores,
]);
} catch (\Exception $e) {
Log::error('Docking Job Failed', ['error' => $e->getMessage(), 'job_id' => $this->dockingJob->id]);
$this->dockingJob->update([
'status' => 'failed',
'result_data' => ['error' => $e->getMessage()],
]);
}
}
}