diff --git a/app/Jobs/AtualizarDadosSemantic.php b/app/Jobs/AtualizarDadosSemantic.php new file mode 100644 index 000000000..c9773c163 --- /dev/null +++ b/app/Jobs/AtualizarDadosSemantic.php @@ -0,0 +1,90 @@ +paperId = $paperId; + $this->doi = $doi; + $this->title = $title; + Log::info("Job criado para atualização via Semantic Scholar (paper ID: {$this->paperId})"); + } + + public function handle() + { + Log::info("Iniciando atualização via Semantic Scholar para o paper ID {$this->paperId}"); + + $service = new SnowballingService(); + $query = !empty($this->doi) ? $this->doi : $this->title; + + $result = $service->fetch($query); + + if (!$result || empty($result['article'])) { + Log::warning("Nenhum dado retornado do Semantic Scholar para o paper ID {$this->paperId}"); + return; + } + + $article = $result['article']; + $paper = Papers::find($this->paperId); + + if (!$paper) { + Log::warning("Paper com ID {$this->paperId} não encontrado no banco de dados."); + return; + } + + // Atualizar campos conforme tipo de consulta (via DOI ou via título) + if (empty($this->doi) && !empty($this->title)) { + // Atualização via título → preencher DOI + $paper->doi = $article['doi'] ?? $paper->doi; + } elseif (!empty($this->doi) && empty($this->title)) { + // Atualização via DOI → preencher título + $paper->title = $article['title'] ?? $paper->title; + } + + // Atualizar campos comuns + $paper->abstract = $article['abstract'] ?? $paper->abstract; + $paper->keywords = $this->extractKeywords($article); + $paper->author = $article['authors'] ?? $paper->author; + + $paper->save(); + + Log::info("Atualização via Semantic Scholar concluída para o paper ID {$this->paperId}"); + } + + private function extractKeywords(array $article): string + { + if (!empty($article['keywords'])) { + if (is_array($article['keywords'])) { + return implode(', ', $article['keywords']); + } + return $article['keywords']; + } + + // Fallback: tentar inferir palavras-chave a partir do abstract + if (!empty($article['abstract'])) { + $abstract = strtolower($article['abstract']); + $words = array_filter(explode(' ', $abstract), fn($w) => strlen($w) > 6); + $unique = array_slice(array_unique($words), 0, 8); + return implode(', ', $unique); + } + + return ''; + } +} diff --git a/app/Livewire/Conducting/StudySelection/ButtonsUpdatePaper.php b/app/Livewire/Conducting/StudySelection/ButtonsUpdatePaper.php index 5353c92fb..50788eb64 100644 --- a/app/Livewire/Conducting/StudySelection/ButtonsUpdatePaper.php +++ b/app/Livewire/Conducting/StudySelection/ButtonsUpdatePaper.php @@ -4,8 +4,9 @@ use App\Jobs\AtualizarDadosCrossref; use App\Jobs\AtualizarDadosSpringer; +use App\Jobs\AtualizarDadosSemantic; use App\Models\Project\Conducting\Papers; -use App\Utils\ToastHelper; // Certifique-se de que o ToastHelper está corretamente importado +use App\Utils\ToastHelper; use Illuminate\Support\Facades\Log; use Livewire\Attributes\On; use Livewire\Component; @@ -22,14 +23,12 @@ public function mount($paperId, $projectId) { $this->projectId = $projectId; $this->paperId = $paperId; - $this->refreshData(); } #[On('refresh-paper-data')] public function refreshData() { - // Recarrega as informações do paper $paper = Papers::find($this->paperId); if ($paper) { $this->abstract = $paper->abstract; @@ -42,40 +41,48 @@ public function refreshData() public function atualizarDadosFaltantes() { if (empty($this->doi) && empty($this->title)) { - // Usar ToastHelper para exibir mensagem de erro - $this->toast('DOI ou título do paper necessário para buscar dados.', 'error'); + $this->toast(__('project/conducting.study-selection.modal.buttons.crossref.error_missing_data'), 'error'); $this->dispatch('refresh-paper-data'); return; } Log::info("Despachando Job para paper ID {$this->paperId}, DOI: {$this->doi} e Título: {$this->title}"); - AtualizarDadosCrossref::dispatch($this->paperId, $this->doi, $this->title); - // Usar ToastHelper para exibir mensagem de sucesso - $this->toast('A atualização dos dados foi solicitada via CrossRef, verifique se os dados foram atualizados.', 'success'); + $this->toast(__('project/conducting.study-selection.modal.buttons.crossref.success'), 'success'); $this->dispatch('refresh-paper-data'); } public function atualizarDadosSpringer() { if (empty($this->doi)) { - // Usar ToastHelper para exibir mensagem de erro - $this->toast('DOI necessário para buscar dados via Springer.', 'error'); + $this->toast(__('project/conducting.study-selection.modal.buttons.springer.error_missing_doi'), 'error'); $this->dispatch('refresh-paper-data'); return; } Log::info("Despachando Job para atualização via Springer para paper ID {$this->paperId}"); - AtualizarDadosSpringer::dispatch($this->paperId, $this->doi); - // Usar ToastHelper para exibir mensagem de sucesso - $this->toast('A atualização dos dados foi solicitada via Springer, verifique se os dados foram atualizados.', 'success'); + $this->toast(__('project/conducting.study-selection.modal.buttons.springer.success'), 'success'); + $this->dispatch('refresh-paper-data'); + } + + public function atualizarDadosSemantic() + { + if (empty($this->doi) && empty($this->title)) { + $this->toast(__('project/conducting.study-selection.modal.buttons.semantic.error_missing_query'), 'error'); + $this->dispatch('refresh-paper-data'); + return; + } + + Log::info("Despachando Job para atualização via Semantic Scholar para paper ID {$this->paperId}"); + AtualizarDadosSemantic::dispatch($this->paperId, $this->doi, $this->title); + + $this->toast(__('project/conducting.study-selection.modal.buttons.semantic.success'), 'success'); $this->dispatch('refresh-paper-data'); } - // Função auxiliar para enviar mensagens de toast private function toast(string $message, string $type) { $this->dispatch('buttons-update-paper', ToastHelper::dispatch($type, $message)); diff --git a/app/Livewire/Conducting/StudySelection/PaperDoiUrl.php b/app/Livewire/Conducting/StudySelection/PaperDoiUrl.php new file mode 100644 index 000000000..02aa019ba --- /dev/null +++ b/app/Livewire/Conducting/StudySelection/PaperDoiUrl.php @@ -0,0 +1,64 @@ +projectId = $projectId; + $this->paperId = $paperId; + $this->loadPaperData(); + } + + #[On('refresh-paper-data')] + public function loadPaperData() + { + $paper = Papers::find($this->paperId); + + if ($paper) { + $this->doi = $paper->doi; + $this->url = $paper->url; + + // Caso a URL esteja vazia, preencher com o link completo do DOI (se existir) + if (empty($this->url) && !empty($this->doi)) { + $this->url = $this->buildDoiUrl($this->doi); + } + } + } + + private function buildDoiUrl(?string $doi): ?string + { + if (empty($doi)) { + return null; + } + + // Se já contiver "doi.org", retorna como está + if (str_contains($doi, 'doi.org')) { + return $doi; + } + + // Se já for um link completo (http/https), retorna direto + if (preg_match('/^https?:\/\//', $doi)) { + return $doi; + } + + // Caso contrário, monta o link padrão + return "https://doi.org/" . ltrim($doi, '/'); + } + + public function render() + { + return view('livewire.conducting.study-selection.paper-doi-url'); + } +} diff --git a/lang/en/project/conducting.php b/lang/en/project/conducting.php index f11629fd4..0a91471fd 100644 --- a/lang/en/project/conducting.php +++ b/lang/en/project/conducting.php @@ -112,6 +112,21 @@ 'unclassified' => 'Unclassified', 'final-decision' => 'Final Decision Group about paper?', ], + 'buttons' => [ + 'crossref' => [ + 'error_missing_data' => 'DOI or paper title is required to fetch data via CrossRef.', + 'success' => 'Data update requested via CrossRef. Please verify if the information has been updated.', + ], + 'springer' => [ + 'error_missing_doi' => 'DOI is required to fetch data via Springer.', + 'success' => 'Data update requested via Springer. Please verify if the information has been updated.', + ], + 'semantic' => [ + 'error_missing_query' => 'DOI or title is required to fetch data via Semantic Scholar.', + 'success' => 'Data update requested via Semantic Scholar. Please verify if the information has been updated.', + ], + ], + 'update_via' => 'Update via:', 'save' => 'Save', 'update' => 'Update', 'confirm' => 'Confirm', @@ -481,12 +496,12 @@
  • To add researchers, navigate to "My Projects->Team"

  • - CSV Format Guidelines:
    + CSV Format Guidelines (Springer Link):
    Your CSV file must include the following column headers:

    - Orientações para o formato CSV:
    + Orientações para o formato CSV (Springer Link):
    O arquivo CSV deve conter os seguintes cabeçalhos de coluna: