AILIXIR Bot commited on
Commit ·
ac4199a
1
Parent(s): 2b955c4
Auto-sync: 08806a730f2c016df06582960edb16f4ca78accf
Browse files- app/Http/Controllers/Api/ChemistryController.php +112 -22
- app/Models/ChemistryCsvJob.php +2 -0
- app/Models/ChemistryThread.php +4 -0
- database/migrations/2026_07_02_213106_add_thread_id_to_chemistry_csv_jobs_table.php +27 -0
- database/migrations/2026_07_02_214407_add_result_content_to_chemistry_csv_jobs_table.php +22 -0
- routes/api.php +1 -0
app/Http/Controllers/Api/ChemistryController.php
CHANGED
|
@@ -118,7 +118,7 @@ class ChemistryController extends BaseController
|
|
| 118 |
// ==================== Analyze SMILES ====================
|
| 119 |
public function analyzeSmiles(Request $request): JsonResponse
|
| 120 |
{
|
| 121 |
-
$user =
|
| 122 |
|
| 123 |
$request->validate([
|
| 124 |
'smiles' => 'required|string|max:1000',
|
|
@@ -166,7 +166,7 @@ class ChemistryController extends BaseController
|
|
| 166 |
return $this->saveAndRespond($result, $user->id, 'docking', $request->input('docking_data'), $thread?->id);
|
| 167 |
}
|
| 168 |
|
| 169 |
-
// ==================== CSV ====================
|
| 170 |
public function uploadCsv(Request $request): JsonResponse
|
| 171 |
{
|
| 172 |
$user = Auth::user();
|
|
@@ -174,10 +174,24 @@ class ChemistryController extends BaseController
|
|
| 174 |
$request->validate([
|
| 175 |
'file' => 'required|file|mimes:csv,txt|max:2048',
|
| 176 |
'analysis_type' => 'nullable|in:full,quick,admet,classify',
|
|
|
|
| 177 |
]);
|
| 178 |
|
| 179 |
$file = $request->file('file');
|
| 180 |
$analysisType = $request->input('analysis_type', 'full');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
$result = $this->chemistryService->uploadCsv($file, $analysisType);
|
| 183 |
|
|
@@ -197,6 +211,7 @@ class ChemistryController extends BaseController
|
|
| 197 |
|
| 198 |
$job = ChemistryCsvJob::create([
|
| 199 |
'user_id' => $user->id,
|
|
|
|
| 200 |
'job_id' => $apiJobId,
|
| 201 |
'filename' => $file->getClientOriginalName(),
|
| 202 |
'analysis_type' => $analysisType,
|
|
@@ -204,6 +219,9 @@ class ChemistryController extends BaseController
|
|
| 204 |
'status' => 'queued',
|
| 205 |
]);
|
| 206 |
|
|
|
|
|
|
|
|
|
|
| 207 |
return response()->json([
|
| 208 |
'success' => true,
|
| 209 |
'data' => [
|
|
@@ -282,16 +300,19 @@ class ChemistryController extends BaseController
|
|
| 282 |
], 400);
|
| 283 |
}
|
| 284 |
|
| 285 |
-
if ($job->
|
| 286 |
-
return
|
|
|
|
|
|
|
| 287 |
}
|
| 288 |
|
| 289 |
$result = $this->chemistryService->getCsvResults($jobId);
|
| 290 |
|
| 291 |
if (is_string($result)) {
|
| 292 |
-
$
|
| 293 |
-
|
| 294 |
-
|
|
|
|
| 295 |
|
| 296 |
return response($result)
|
| 297 |
->header('Content-Type', 'text/csv')
|
|
@@ -301,22 +322,36 @@ class ChemistryController extends BaseController
|
|
| 301 |
return response()->json(new ChemistryResponseResource($result));
|
| 302 |
}
|
| 303 |
|
| 304 |
-
public function listJobs(): JsonResponse
|
| 305 |
{
|
| 306 |
-
$
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
'
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
'
|
| 318 |
-
|
| 319 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
|
| 321 |
return response()->json([
|
| 322 |
'success' => true,
|
|
@@ -375,6 +410,61 @@ class ChemistryController extends BaseController
|
|
| 375 |
]);
|
| 376 |
}
|
| 377 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
// ==================== Helper Methods ====================
|
| 379 |
private function validateThread(int $userId, ?string $threadId): ?ChemistryThread
|
| 380 |
{
|
|
|
|
| 118 |
// ==================== Analyze SMILES ====================
|
| 119 |
public function analyzeSmiles(Request $request): JsonResponse
|
| 120 |
{
|
| 121 |
+
$user = Auth::user();
|
| 122 |
|
| 123 |
$request->validate([
|
| 124 |
'smiles' => 'required|string|max:1000',
|
|
|
|
| 166 |
return $this->saveAndRespond($result, $user->id, 'docking', $request->input('docking_data'), $thread?->id);
|
| 167 |
}
|
| 168 |
|
| 169 |
+
// ==================== CSV (معدل) ====================
|
| 170 |
public function uploadCsv(Request $request): JsonResponse
|
| 171 |
{
|
| 172 |
$user = Auth::user();
|
|
|
|
| 174 |
$request->validate([
|
| 175 |
'file' => 'required|file|mimes:csv,txt|max:2048',
|
| 176 |
'analysis_type' => 'nullable|in:full,quick,admet,classify',
|
| 177 |
+
'thread_id' => 'required|string|max:255', // ← أصبح required
|
| 178 |
]);
|
| 179 |
|
| 180 |
$file = $request->file('file');
|
| 181 |
$analysisType = $request->input('analysis_type', 'full');
|
| 182 |
+
$threadId = $request->input('thread_id');
|
| 183 |
+
|
| 184 |
+
// ← Validate thread belongs to user
|
| 185 |
+
$thread = ChemistryThread::where('user_id', $user->id)
|
| 186 |
+
->where('thread_id', $threadId)
|
| 187 |
+
->first();
|
| 188 |
+
|
| 189 |
+
if (!$thread) {
|
| 190 |
+
return response()->json([
|
| 191 |
+
'success' => false,
|
| 192 |
+
'error' => 'Thread not found or access denied',
|
| 193 |
+
], 403);
|
| 194 |
+
}
|
| 195 |
|
| 196 |
$result = $this->chemistryService->uploadCsv($file, $analysisType);
|
| 197 |
|
|
|
|
| 211 |
|
| 212 |
$job = ChemistryCsvJob::create([
|
| 213 |
'user_id' => $user->id,
|
| 214 |
+
'chemistry_thread_id' => $thread->id, // ← جديد: ربط بالـ thread
|
| 215 |
'job_id' => $apiJobId,
|
| 216 |
'filename' => $file->getClientOriginalName(),
|
| 217 |
'analysis_type' => $analysisType,
|
|
|
|
| 219 |
'status' => 'queued',
|
| 220 |
]);
|
| 221 |
|
| 222 |
+
// ← Update thread last_used_at
|
| 223 |
+
$thread->update(['last_used_at' => now()]);
|
| 224 |
+
|
| 225 |
return response()->json([
|
| 226 |
'success' => true,
|
| 227 |
'data' => [
|
|
|
|
| 300 |
], 400);
|
| 301 |
}
|
| 302 |
|
| 303 |
+
if ($job->result_content) {
|
| 304 |
+
return response($job->result_content)
|
| 305 |
+
->header('Content-Type', 'text/csv')
|
| 306 |
+
->header('Content-Disposition', 'attachment; filename="results_' . $jobId . '.csv"');
|
| 307 |
}
|
| 308 |
|
| 309 |
$result = $this->chemistryService->getCsvResults($jobId);
|
| 310 |
|
| 311 |
if (is_string($result)) {
|
| 312 |
+
$job->update([
|
| 313 |
+
'result_content' => $result,
|
| 314 |
+
'result_file_path' => null,
|
| 315 |
+
]);
|
| 316 |
|
| 317 |
return response($result)
|
| 318 |
->header('Content-Type', 'text/csv')
|
|
|
|
| 322 |
return response()->json(new ChemistryResponseResource($result));
|
| 323 |
}
|
| 324 |
|
| 325 |
+
public function listJobs(Request $request): JsonResponse
|
| 326 |
{
|
| 327 |
+
$user = Auth::user();
|
| 328 |
+
|
| 329 |
+
$query = $user->chemistryCsvJobs()
|
| 330 |
+
->orderBy('created_at', 'desc');
|
| 331 |
+
|
| 332 |
+
if ($request->has('thread_id')) {
|
| 333 |
+
$thread = ChemistryThread::where('user_id', $user->id)
|
| 334 |
+
->where('thread_id', $request->input('thread_id'))
|
| 335 |
+
->first();
|
| 336 |
+
|
| 337 |
+
if ($thread) {
|
| 338 |
+
$query->where('chemistry_thread_id', $thread->id);
|
| 339 |
+
}
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
$jobs = $query->get([
|
| 343 |
+
'id',
|
| 344 |
+
'job_id',
|
| 345 |
+
'chemistry_thread_id',
|
| 346 |
+
'filename',
|
| 347 |
+
'analysis_type',
|
| 348 |
+
'status',
|
| 349 |
+
'total_rows',
|
| 350 |
+
'completed_rows',
|
| 351 |
+
'failed_rows',
|
| 352 |
+
'progress_percent',
|
| 353 |
+
'created_at'
|
| 354 |
+
]);
|
| 355 |
|
| 356 |
return response()->json([
|
| 357 |
'success' => true,
|
|
|
|
| 410 |
]);
|
| 411 |
}
|
| 412 |
|
| 413 |
+
// ==================== Thread Messages ====================
|
| 414 |
+
public function threadMessages(Request $request): JsonResponse
|
| 415 |
+
{
|
| 416 |
+
$user = Auth::user();
|
| 417 |
+
|
| 418 |
+
$request->validate([
|
| 419 |
+
'thread_id' => 'required|string|max:255',
|
| 420 |
+
]);
|
| 421 |
+
|
| 422 |
+
$threadId = $request->input('thread_id');
|
| 423 |
+
|
| 424 |
+
$thread = ChemistryThread::where('user_id', $user->id)
|
| 425 |
+
->where('thread_id', $threadId)
|
| 426 |
+
->first();
|
| 427 |
+
|
| 428 |
+
if (!$thread) {
|
| 429 |
+
return response()->json([
|
| 430 |
+
'success' => false,
|
| 431 |
+
'error' => 'Thread not found or access denied',
|
| 432 |
+
], 403);
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
$messages = ChemistryAnalysis::where('user_id', $user->id)
|
| 436 |
+
->where('chemistry_thread_id', $thread->id)
|
| 437 |
+
->orderBy('created_at', 'asc')
|
| 438 |
+
->get(['id', 'type', 'input_data', 'response', 'status', 'created_at']);
|
| 439 |
+
|
| 440 |
+
$csvJobs = ChemistryCsvJob::where('user_id', $user->id)
|
| 441 |
+
->where('chemistry_thread_id', $thread->id)
|
| 442 |
+
->orderBy('created_at', 'asc')
|
| 443 |
+
->get([
|
| 444 |
+
'id',
|
| 445 |
+
'job_id',
|
| 446 |
+
'filename',
|
| 447 |
+
'analysis_type',
|
| 448 |
+
'status',
|
| 449 |
+
'total_rows',
|
| 450 |
+
'completed_rows',
|
| 451 |
+
'failed_rows',
|
| 452 |
+
'progress_percent',
|
| 453 |
+
'result_content',
|
| 454 |
+
'created_at',
|
| 455 |
+
'completed_at'
|
| 456 |
+
]);
|
| 457 |
+
|
| 458 |
+
return response()->json([
|
| 459 |
+
'success' => true,
|
| 460 |
+
'data' => [
|
| 461 |
+
'thread_id' => $thread->thread_id,
|
| 462 |
+
'title' => $thread->title,
|
| 463 |
+
'messages' => $messages,
|
| 464 |
+
'csv_jobs' => $csvJobs,
|
| 465 |
+
],
|
| 466 |
+
]);
|
| 467 |
+
}
|
| 468 |
// ==================== Helper Methods ====================
|
| 469 |
private function validateThread(int $userId, ?string $threadId): ?ChemistryThread
|
| 470 |
{
|
app/Models/ChemistryCsvJob.php
CHANGED
|
@@ -12,6 +12,7 @@ class ChemistryCsvJob extends Model
|
|
| 12 |
|
| 13 |
protected $fillable = [
|
| 14 |
'user_id',
|
|
|
|
| 15 |
'job_id',
|
| 16 |
'filename',
|
| 17 |
'analysis_type',
|
|
@@ -21,6 +22,7 @@ class ChemistryCsvJob extends Model
|
|
| 21 |
'progress_percent',
|
| 22 |
'status',
|
| 23 |
'result_file_path',
|
|
|
|
| 24 |
'error_message',
|
| 25 |
'started_at',
|
| 26 |
'completed_at',
|
|
|
|
| 12 |
|
| 13 |
protected $fillable = [
|
| 14 |
'user_id',
|
| 15 |
+
'chemistry_thread_id',
|
| 16 |
'job_id',
|
| 17 |
'filename',
|
| 18 |
'analysis_type',
|
|
|
|
| 22 |
'progress_percent',
|
| 23 |
'status',
|
| 24 |
'result_file_path',
|
| 25 |
+
'result_content',
|
| 26 |
'error_message',
|
| 27 |
'started_at',
|
| 28 |
'completed_at',
|
app/Models/ChemistryThread.php
CHANGED
|
@@ -32,4 +32,8 @@ class ChemistryThread extends Model
|
|
| 32 |
{
|
| 33 |
return $this->hasMany(ChemistryAnalysis::class);
|
| 34 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
}
|
|
|
|
| 32 |
{
|
| 33 |
return $this->hasMany(ChemistryAnalysis::class);
|
| 34 |
}
|
| 35 |
+
public function csvJobs(): HasMany
|
| 36 |
+
{
|
| 37 |
+
return $this->hasMany(ChemistryCsvJob::class, 'chemistry_thread_id');
|
| 38 |
+
}
|
| 39 |
}
|
database/migrations/2026_07_02_213106_add_thread_id_to_chemistry_csv_jobs_table.php
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
use Illuminate\Database\Migrations\Migration;
|
| 4 |
+
use Illuminate\Database\Schema\Blueprint;
|
| 5 |
+
use Illuminate\Support\Facades\Schema;
|
| 6 |
+
|
| 7 |
+
return new class extends Migration
|
| 8 |
+
{
|
| 9 |
+
public function up(): void
|
| 10 |
+
{
|
| 11 |
+
Schema::table('chemistry_csv_jobs', function (Blueprint $table) {
|
| 12 |
+
$table->foreignId('chemistry_thread_id')
|
| 13 |
+
->nullable()
|
| 14 |
+
->after('user_id')
|
| 15 |
+
->constrained('chemistry_threads')
|
| 16 |
+
->onDelete('cascade');
|
| 17 |
+
});
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
public function down(): void
|
| 21 |
+
{
|
| 22 |
+
Schema::table('chemistry_csv_jobs', function (Blueprint $table) {
|
| 23 |
+
$table->dropForeign(['chemistry_thread_id']);
|
| 24 |
+
$table->dropColumn('chemistry_thread_id');
|
| 25 |
+
});
|
| 26 |
+
}
|
| 27 |
+
};
|
database/migrations/2026_07_02_214407_add_result_content_to_chemistry_csv_jobs_table.php
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
use Illuminate\Database\Migrations\Migration;
|
| 4 |
+
use Illuminate\Database\Schema\Blueprint;
|
| 5 |
+
use Illuminate\Support\Facades\Schema;
|
| 6 |
+
|
| 7 |
+
return new class extends Migration
|
| 8 |
+
{
|
| 9 |
+
public function up(): void
|
| 10 |
+
{
|
| 11 |
+
Schema::table('chemistry_csv_jobs', function (Blueprint $table) {
|
| 12 |
+
$table->longText('result_content')->nullable()->after('result_file_path');
|
| 13 |
+
});
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
public function down(): void
|
| 17 |
+
{
|
| 18 |
+
Schema::table('chemistry_csv_jobs', function (Blueprint $table) {
|
| 19 |
+
$table->dropColumn('result_content');
|
| 20 |
+
});
|
| 21 |
+
}
|
| 22 |
+
};
|
routes/api.php
CHANGED
|
@@ -147,6 +147,7 @@ Route::middleware('auth:sanctum')->group(function () {
|
|
| 147 |
// Threads
|
| 148 |
Route::post('thread', [ChemistryController::class, 'createThread']);
|
| 149 |
Route::get('threads', [ChemistryController::class, 'listThreads']);
|
|
|
|
| 150 |
|
| 151 |
// Chat
|
| 152 |
Route::post('chat', [ChemistryController::class, 'chat']);
|
|
|
|
| 147 |
// Threads
|
| 148 |
Route::post('thread', [ChemistryController::class, 'createThread']);
|
| 149 |
Route::get('threads', [ChemistryController::class, 'listThreads']);
|
| 150 |
+
Route::post('/thread-messages', [ChemistryController::class, 'threadMessages']);
|
| 151 |
|
| 152 |
// Chat
|
| 153 |
Route::post('chat', [ChemistryController::class, 'chat']);
|