Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions app/Jobs/AtualizarDadosSemantic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

namespace App\Jobs;

use App\Models\Project\Conducting\Papers;
use App\Services\SnowballingService;
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;

class AtualizarDadosSemantic implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

protected $paperId;
protected $doi;
protected $title;

public function __construct($paperId, $doi = null, $title = null)
{
$this->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 '';
}
}
35 changes: 21 additions & 14 deletions app/Livewire/Conducting/StudySelection/ButtonsUpdatePaper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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));
Expand Down
64 changes: 64 additions & 0 deletions app/Livewire/Conducting/StudySelection/PaperDoiUrl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace App\Livewire\Conducting\StudySelection;

use App\Models\Project;
use App\Models\Project\Conducting\Papers;
use Livewire\Attributes\On;
use Livewire\Component;

class PaperDoiUrl extends Component
{
public $paperId;
public $projectId;
public $doi;
public $url;

public function mount($paperId, $projectId)
{
$this->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');
}
}
19 changes: 17 additions & 2 deletions lang/en/project/conducting.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -481,12 +496,12 @@
<li>To add researchers, navigate to <b>"My Projects->Team"</b></li>
</ul>
<br>
<b>CSV Format Guidelines:</b><br>
<b>CSV Format Guidelines (Springer Link):</b><br>
Your CSV file must include the following column headers:<br>
<ul>
<li>"<b>Item Title</b>" – used as the paper title</li>
<li>"<b>Authors</b>" – list of authors</li>
<li>"<b>Item DOI</b>" – Digital Object Identifier</li>
<li>"Item DOI" – Digital Object Identifier</li>
<li>"URL" – link to the paper</li>
<li>"Publication Year" – publication year</li>
<li>"Book Series Title" – optional book series name</li>
Expand Down
19 changes: 17 additions & 2 deletions lang/pt_BR/project/conducting.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@
'unclassified' => 'Unclassified',
'final-decision' => 'Decisão final do grupo sobre o paper?',
],
'buttons' => [
'crossref' => [
'error_missing_data' => 'É necessário informar o DOI ou o título do paper para buscar dados via CrossRef.',
'success' => 'A atualização dos dados foi solicitada via CrossRef. Verifique se as informações foram atualizadas.',
],
'springer' => [
'error_missing_doi' => 'É necessário informar o DOI para buscar dados via Springer.',
'success' => 'A atualização dos dados foi solicitada via Springer. Verifique se as informações foram atualizadas.',
],
'semantic' => [
'error_missing_query' => 'É necessário informar o DOI ou o título para buscar dados via Semantic Scholar.',
'success' => 'A atualização dos dados foi solicitada via Semantic Scholar. Verifique se as informações foram atualizadas.',
],
],
'update_via' => 'Atualizar via:',
'save' => 'Salvar',
'update' => 'Atualizar',
'confirm' => 'Confirmar',
Expand Down Expand Up @@ -302,12 +317,12 @@
<li>Para adicionar pesquisadores, navegue até <b>"Meus Projetos->Colaboradores"</b></li>
</ul>
<br>
<b>Orientações para o formato CSV:</b><br>
<b>Orientações para o formato CSV (Springer Link):</b><br>
O arquivo CSV deve conter os seguintes cabeçalhos de coluna:<br>
<ul>
<li>"<b>Item Title</b>" – usado como o título do estudo</li>
<li>"<b>Authors</b>" – lista de autores</li>
<li>"<b>Item DOI</b>" – identificador digital do objeto</li>
<li>"Item DOI" – identificador digital do objeto</li>
<li>"URL" – link opcional para o estudo</li>
<li>"Publication Year" – ano de publicação</li>
<li>"Book Series Title" – nome da série de livros</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
<div>
<p>{{ __('project/conducting.study-selection.modal.update_via') }}</p>
@if(empty($abstract) || empty($keywords) || empty($doi) || empty($title))
<a class="btn py-1 px-3 btn-outline-primary" data-toggle="tooltip" data-original-title="Atualizar Dados Via Semantic Scholar" wire:click="atualizarDadosSemantic">
<i class="fa-solid fa-refresh"></i>
Semantic Scholar
</a>
@endif
@if(empty($abstract) || empty($keywords))
<a class="btn py-1 px-3 btn-outline-primary" data-toggle="tooltip" data-original-title="Atualizar Dados Via CrossRef" wire:click="atualizarDadosFaltantes">
<i class="fa-solid fa-refresh"></i>
Via CrossRef
CrossRef
</a>
<a class="btn py-1 px-3 btn-outline-primary" data-toggle="tooltip" data-original-title="Atualizar Dados Via Springer" wire:click="atualizarDadosSpringer">
<i class="fa-solid fa-refresh"></i>
Via SpringerLink
SpringerLink
</a>
@endif

<div wire:ignore.self class="modal fade" id="successModalUpdate" tabindex="-1" role="dialog" aria-labelledby="successModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="successModalLabel">Success</h5>
<h5 class="modal-title" id="successModalLabel">{{ __('project/conducting.study-selection.modal.success') }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<div>

{{-- Botão DOI --}}
@if(!empty($doi))
@php
// Se o DOI já contiver "http", usar direto. Caso contrário, montar o link completo
$doiLink = str_contains($doi, 'http') ? $doi : 'https://doi.org/' . ltrim($doi);
@endphp

<a class="btn py-1 px-3 btn-outline-dark"
data-toggle="tooltip"
data-original-title="DOI"
href="{{ $doiLink }}"
target="_blank">
<i class="fa-solid fa-arrow-up-right-from-square"></i>
DOI
</a>
@endif

{{-- Botão URL --}}
@php
// Se a URL estiver vazia, usar o link do DOI (se existir)
$urlLink = !empty($url)
? $url
: (!empty($doi) ? (str_contains($doi, 'http') ? $doi : 'https://doi.org/' . ltrim($doi)) : null);
@endphp

@if(!empty($urlLink))
<a class="btn py-1 px-3 btn-outline-success"
data-toggle="tooltip"
data-original-title="URL"
href="{{ $urlLink }}"
target="_blank">
<i class="fa-solid fa-link"></i>
URL
</a>
@endif

</div>

@script
<script>
$wire.on('paper-doi-url', ([{ message, type }]) => {
toasty({ message, type });
});
</script>
@endscript
Loading
Loading