356 lines
14 KiB
PHP
356 lines
14 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Yajra\DataTables\Facades\DataTables;
|
|
use App\Models\Publication;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use App\Services\ScholarService;
|
|
|
|
class PublicationsController extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
$user = auth()->user();
|
|
$scholarUrl = $user->scholar_url;
|
|
|
|
return view('pages.publications.index', compact('scholarUrl'));
|
|
}
|
|
|
|
public function edit($id)
|
|
{
|
|
$publication = Publication::findOrFail($id);
|
|
$organisingInstitutes = Publication::select('organizing_institute')
|
|
->distinct()
|
|
->pluck('organizing_institute');
|
|
|
|
return view('pages.publications.edit', compact('publication', 'organisingInstitutes'));
|
|
}
|
|
|
|
public function update(Request $request, $id)
|
|
{
|
|
$publication = Publication::findOrFail($id);
|
|
|
|
$validated = $request->validate([
|
|
'first_author_name' => 'required|string',
|
|
'co_authors' => 'nullable|string',
|
|
'start_date' => 'required|date',
|
|
'start_time' => 'required|date_format:H:i',
|
|
'end_date' => 'required|date',
|
|
'end_time' => 'required|date_format:H:i',
|
|
'num_days' => 'required|integer',
|
|
'activity_type' => 'required|string',
|
|
'title' => 'required|string',
|
|
'affiliation' => 'required|string',
|
|
'organizing_institute' => 'required|string',
|
|
'venue_address' => 'required|string',
|
|
'is_peer_reviewed' => 'required|in:yes,no',
|
|
'scopus_link' => 'nullable|url',
|
|
'sci_link' => 'nullable|url',
|
|
'paper_file' => 'nullable|mimes:jpg,jpeg,png,pdf,doc,docx,zip',
|
|
]);
|
|
|
|
$startDateTime = date('Y-m-d H:i:s', strtotime("{$validated['start_date']} {$validated['start_time']}"));
|
|
$endDateTime = date('Y-m-d H:i:s', strtotime("{$validated['end_date']} {$validated['end_time']}"));
|
|
|
|
if ($request->hasFile('paper_file')) {
|
|
if ($publication->paper_file && Storage::disk('public')->exists($publication->paper_file)) {
|
|
Storage::disk('public')->delete($publication->paper_file);
|
|
}
|
|
|
|
$year = date('Y', strtotime($validated['start_date']));
|
|
$username = $publication->user->name;
|
|
$userId = $publication->user->id;
|
|
|
|
$originalName = $request->file('paper_file')->getClientOriginalName();
|
|
$fileName = $username . '_' . $originalName;
|
|
|
|
$folderPath = 'proofs/' . $year . '/' . $username . '/Publications';
|
|
|
|
$paperFilePath = $request->file('paper_file')->storeAs($folderPath, $fileName, 'public');
|
|
|
|
$publication->paper_file = $paperFilePath;
|
|
}
|
|
|
|
$publication->first_author_name = $validated['first_author_name'];
|
|
$publication->co_authors = $validated['co_authors'];
|
|
$publication->start_date = $startDateTime;
|
|
$publication->end_date = $endDateTime;
|
|
$publication->num_days = $validated['num_days'];
|
|
$publication->activity_type = $validated['activity_type'];
|
|
$publication->title = $validated['title'];
|
|
$publication->affiliation = $validated['affiliation'];
|
|
$publication->organizing_institute = $validated['organizing_institute'];
|
|
$publication->venue_address = $validated['venue_address'];
|
|
$publication->is_peer_reviewed = $validated['is_peer_reviewed'];
|
|
$publication->scopus_link = $validated['scopus_link'];
|
|
$publication->sci_link = $validated['sci_link'];
|
|
|
|
$publication->save();
|
|
|
|
$userRole = auth()->user()->role->name;
|
|
|
|
if ($userRole === 'Admin') {
|
|
return redirect()->route('admin.PublicationsResponses')
|
|
->with('status', 'Publication updated successfully');
|
|
} elseif ($userRole === 'Coordinator') {
|
|
return redirect()->route('coordinator.PublicationsResponses')
|
|
->with('status', 'Publication updated successfully');
|
|
} else {
|
|
return redirect()->route('faculty.PublicationsResponses')
|
|
->with('status', 'Publication updated successfully');
|
|
}
|
|
}
|
|
|
|
public function destroy($id)
|
|
{
|
|
$publication = Publication::findOrFail($id);
|
|
|
|
if ($publication->paper_file && Storage::disk('public')->exists($publication->paper_file)) {
|
|
Storage::disk('public')->delete($publication->paper_file);
|
|
}
|
|
|
|
$publication->delete();
|
|
|
|
return response()->json(['success' => 'Publication deleted successfully']);
|
|
}
|
|
|
|
public function getPublicationsResponses(Request $request)
|
|
{
|
|
$user = auth()->user();
|
|
$isAdmin = $user->role->name === 'Admin';
|
|
$isCoordinator = $user->role->name === 'Coordinator';
|
|
|
|
if ($isAdmin) {
|
|
$publications = Publication::with('user', 'department');
|
|
} elseif ($isCoordinator) {
|
|
$publications = Publication::with('user', 'department')
|
|
->whereHas('user', function ($query) use ($user) {
|
|
$query->where('department_id', $user->department_id);
|
|
});
|
|
} else {
|
|
$publications = Publication::with('user', 'department')
|
|
->where('faculty_id', $user->id);
|
|
}
|
|
|
|
// Apply filters
|
|
if ($request->has('department') && !empty($request->department)) {
|
|
$publications->whereHas('department', function ($query) use ($request) {
|
|
$query->where('id', $request->department);
|
|
});
|
|
}
|
|
|
|
if ($request->has('category') && !empty($request->category)) {
|
|
$publications->where('category', $request->category);
|
|
}
|
|
|
|
if ($request->has('dateFrom') && !empty($request->dateFrom)) {
|
|
$publications->where('start_date', '>=', $request->dateFrom);
|
|
}
|
|
|
|
if ($request->has('dateTo') && !empty($request->dateTo)) {
|
|
$publications->where('end_date', '<=', $request->dateTo);
|
|
}
|
|
|
|
return DataTables::of($publications)
|
|
->addColumn('user_name', function ($publication) {
|
|
return $publication->user->name ?? 'Unknown';
|
|
})
|
|
->addColumn('department_name', function ($publication) {
|
|
return $publication->department->name ?? 'Unknown';
|
|
})
|
|
->addColumn('start_date', function ($publication) {
|
|
return \Carbon\Carbon::parse($publication->start_date)->format('d-m-Y');
|
|
})
|
|
->addColumn('start_time', function ($publication) {
|
|
return \Carbon\Carbon::parse($publication->start_date)->format('h:i A');
|
|
})
|
|
->addColumn('end_date', function ($publication) {
|
|
return \Carbon\Carbon::parse($publication->end_date)->format('d-m-Y');
|
|
})
|
|
->addColumn('end_time', function ($publication) {
|
|
return \Carbon\Carbon::parse($publication->end_date)->format('h:i A');
|
|
})
|
|
->addColumn('is_scopus_indexed', function ($publication) {
|
|
return $publication->isScopusIndexed() ? 'Yes' : 'No';
|
|
})
|
|
->addColumn('is_sci_indexed', function ($publication) {
|
|
return $publication->isSciIndexed() ? 'Yes' : 'No';
|
|
})
|
|
->addColumn('action', function ($publication) {
|
|
$actions = [];
|
|
|
|
if ($publication->paper_file) {
|
|
$actions[] = '<a href="' . asset('storage/' . $publication->paper_file) . '" target="_blank" class="btn btn-sm btn-primary mr-1"><i class="fas fa-eye"></i></a>';
|
|
} else {
|
|
$actions[] = '<span class="text-muted"><i class="fas fa-times-circle"></i></span>';
|
|
}
|
|
|
|
$userRole = auth()->user()->role->name;
|
|
if ($userRole === 'Admin') {
|
|
$editRoute = route('admin.Publications.edit', $publication->id);
|
|
} elseif ($userRole === 'Coordinator') {
|
|
$editRoute = route('coordinator.Publications.edit', $publication->id);
|
|
} else {
|
|
$editRoute = route('faculty.Publications.edit', $publication->id);
|
|
}
|
|
|
|
$actions[] = '<a href="' . $editRoute . '" class="btn btn-sm btn-info mx-1"><i class="fas fa-edit"></i></a>';
|
|
|
|
$deleteRoute = route('publications.destroy', $publication->id);
|
|
$actions[] = '<button type="button" class="btn btn-sm btn-danger delete-btn" data-id="' . $publication->id . '" data-url="' . $deleteRoute . '"><i class="fas fa-trash"></i></button>';
|
|
|
|
return implode(' ', $actions);
|
|
})
|
|
->rawColumns(['action'])
|
|
->make(true);
|
|
}
|
|
|
|
/**
|
|
* Fetch all publications from user's linked Google Scholar profile.
|
|
*/
|
|
public function fetchScholar(Request $request)
|
|
{
|
|
try {
|
|
|
|
$user = auth()->user();
|
|
\Log::info('📡 FetchScholar Triggered', ['scholar_url' => $user->scholar_url]);
|
|
|
|
|
|
if (!$user || !$user->scholar_url) {
|
|
return response()->json([
|
|
'error' => 'No Google Scholar URL found for the logged-in user.'
|
|
], 400);
|
|
}
|
|
|
|
$scholarService = new \App\Services\ScholarService();
|
|
$result = $scholarService->fetchPublications($user->scholar_url);
|
|
|
|
// If the service returned an error payload, forward it to UI
|
|
if (is_array($result) && array_key_exists('error', $result)) {
|
|
// Log more details if available
|
|
if (isset($result['body'])) {
|
|
\Log::error('ScholarService error body', ['body' => $result['body']]);
|
|
} elseif (isset($result['raw'])) {
|
|
\Log::warning('ScholarService returned empty articles', ['raw' => $result['raw']]);
|
|
}
|
|
|
|
return response()->json([
|
|
'error' => $result['error']
|
|
], 200);
|
|
}
|
|
|
|
// Expecting successful shape ['data' => [...]]
|
|
$publications = $result['data'] ?? [];
|
|
|
|
if (empty($publications)) {
|
|
return response()->json([
|
|
'error' => 'No publications found from the provided Scholar profile.'
|
|
], 200);
|
|
}
|
|
|
|
return response()->json([
|
|
'data' => $publications,
|
|
'count' => count($publications),
|
|
]);
|
|
|
|
} catch (\Throwable $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Error fetching publications: '.$e->getMessage(),
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Import selected publications (skipping duplicates).
|
|
*/
|
|
public function importScholar(Request $request)
|
|
{
|
|
try {
|
|
$user = auth()->user();
|
|
$facultyId = $user->id;
|
|
|
|
$validated = $request->validate([
|
|
'publications' => 'required|array',
|
|
'publications.*.title' => 'required|string',
|
|
'publications.*.authors' => 'nullable|string',
|
|
'publications.*.journal' => 'nullable|string',
|
|
'publications.*.year' => 'nullable|string',
|
|
'publications.*.link' => 'nullable|url',
|
|
]);
|
|
|
|
$rows = $validated['publications'];
|
|
$imported = 0;
|
|
$skipped = 0;
|
|
|
|
foreach ($rows as $pub) {
|
|
$exists = \App\Models\Publication::where('faculty_id', $facultyId)
|
|
->whereRaw('LOWER(title) = ?', [strtolower($pub['title'])])
|
|
->exists();
|
|
|
|
if ($exists) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
// 🔹 Derive year safely (default to current year if blank)
|
|
$year = (int)($pub['year'] ?? date('Y'));
|
|
$year = ($year < 1900 || $year > date('Y') + 1) ? date('Y') : $year;
|
|
|
|
// 🔹 Set start/end to that year's first/last day
|
|
$startDate = "{$year}-01-01";
|
|
$endDate = "{$year}-12-31";
|
|
|
|
// 🔍 Smart Tag Detection
|
|
$journalLower = strtolower($pub['journal'] ?? '');
|
|
|
|
// Detect peer-reviewed journals
|
|
$isPeerReviewed = (
|
|
str_contains($journalLower, 'ieee') ||
|
|
str_contains($journalLower, 'elsevier') ||
|
|
str_contains($journalLower, 'springer') ||
|
|
str_contains($journalLower, 'scopus') ||
|
|
str_contains($journalLower, 'wiley') ||
|
|
str_contains($journalLower, 'nature') ||
|
|
str_contains($journalLower, 'taylor & francis')
|
|
) ? 'yes' : 'no';
|
|
|
|
// Detect Scopus / SCI tags
|
|
$isScopus = str_contains($journalLower, 'scopus') ? 'Yes' : 'No';
|
|
$isSci = str_contains($journalLower, 'sci') ? 'Yes' : 'No';
|
|
|
|
\App\Models\Publication::create([
|
|
'faculty_id' => $facultyId,
|
|
'department_id' => $user->department_id,
|
|
'affiliation' => $pub['journal'] ?? '-',
|
|
'organizing_institute' => null,
|
|
'venue_address' => null,
|
|
'title' => $pub['title'],
|
|
'first_author_name' => $user->name,
|
|
'co_authors' => $pub['authors'] ?? '-',
|
|
'journal_name' => $pub['journal'] ?? '-',
|
|
'start_date' => $startDate,
|
|
'end_date' => $endDate,
|
|
'num_days' => 365,
|
|
'activity_type' => 'Google Scholar',
|
|
'is_peer_reviewed' => $isPeerReviewed,
|
|
'is_scopus_indexed' => $isScopus,
|
|
'is_sci_indexed' => $isSci,
|
|
'external_link' => $pub['link'] ?? null,
|
|
]);
|
|
|
|
$imported++;
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => "Imported $imported publications, skipped $skipped duplicates."
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
return response()->json(['error' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
}
|