AILIXIR Bot commited on
Commit
ac4199a
·
1 Parent(s): 2b955c4

Auto-sync: 08806a730f2c016df06582960edb16f4ca78accf

Browse files
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 = auth()->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->result_file_path && Storage::exists($job->result_file_path)) {
286
- return Storage::download($job->result_file_path);
 
 
287
  }
288
 
289
  $result = $this->chemistryService->getCsvResults($jobId);
290
 
291
  if (is_string($result)) {
292
- $path = "chemistry_results/{$jobId}.csv";
293
- Storage::put($path, $result);
294
- $job->update(['result_file_path' => $path]);
 
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
- $jobs = Auth::user()->chemistryCsvJobs()
307
- ->orderBy('created_at', 'desc')
308
- ->get([
309
- 'id',
310
- 'job_id',
311
- 'filename',
312
- 'analysis_type',
313
- 'status',
314
- 'total_rows',
315
- 'completed_rows',
316
- 'failed_rows',
317
- 'progress_percent',
318
- 'created_at'
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']);