Skip to content
Open
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
213 changes: 213 additions & 0 deletions app/Helpers/Publication.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php

namespace App\Helpers;

use Illuminate\Support\Arr;

class Publication
{
/** @var array Default citation formats (keyed in order) */
const CITATION_FORMAT_AUTHOR_NAMES = [
'APA' => ['last_name', 'comma', 'space', 'first_initial', 'space', 'middle_initial'],
//'MLA' => ['last_name', 'comma', 'space', 'first_name'], // Example ONLY, for testing purposes
//'Chicago' => ['first_name', 'space', 'last_name'], // Example ONLY, for testing purposes
];

/** @var array */
const REGEX_PATTERNS = [
'APA' => "/^[A-Za-z][\s\w\p{L}\p{M}áéíóúüññÑ'-]+,\s[A-Z][\.\s\b][\s]?[A-Z]?[\.\s\b]?$/",
'last_name_initials' => "/^[A-Za-z][\s\w\p{L}\p{M}áéíóúüññÑ'-]+,?\s[A-Z]{1,2}\b$/",
'first_name_last_name' => "/^[A-Za-z][\s\w\p{L}\p{M}áéíóúüññÑ'-]+\s[A-Za-z][\s\w\p{L}\p{M}áéíóúüññÑ'-]+$/",
// 'MLA' => "/^[\p{L}ñÑ'., -]+, [\p{Lu}ñÑ]\. ?[\p{Lu}ñÑ]?\.?$/", // Example ONLY, for testing purposes
// 'Chicago' => "/^[\p{L}ñÑ'., -]+, [\p{Lu}ñÑ]\. ?[\p{Lu}ñÑ]?\.?$/", // Example ONLY, for testing purposes
];

/** @var array */
const SEPARATORS = [
'comma' => ',',
'ellipsis' => '...',
'ampersand' => '&',
'space' => ' ',
'period' => '.',
];

public static function citationFormats(): array
{
return static::CITATION_FORMAT_AUTHOR_NAMES;
}

public static function citationFormatRegex(): array
{
return static::REGEX_PATTERNS;
}

public static function matchesRegexPattern($citation_format, $formatted_author_name) : int
{
return preg_match(static::citationFormatRegex()[$citation_format], $formatted_author_name);
}

public static function firstName(array $author_name_array, $pattern = null) : string
{
if (!is_null($pattern) && $pattern === 'last_name_initials') {
return $author_name_array[1];
}
return $author_name_array[0];
}

public static function lastName(array $author_name_array, $pattern = null) : string
{
if (!is_null($pattern) && $pattern === 'last_name_initials') {
return $author_name_array[0];
}

if (count($author_name_array) == 3) {
return $author_name_array[2];
}
return Arr::last($author_name_array);
}

public static function middleName(array $author_name_array, $pattern = null) : string
{
if (!is_null($pattern) && $pattern === 'last_name_initials') {
return strlen($author_name_array[1]) == 2 ? $author_name_array[1][1] : '';
}

if (count($author_name_array) == 3) {
return $author_name_array[1];
}
return '';
}

public static function initial(string $name) : string
{
return strlen($name) > 0 ? "{$name[0]}." : '';
}

/**
* Return a string with the authors names in APA format
* @param array $authors
* @return string
*/
public static function formatAuthorsApa(array $authors) : string
{
$authors = static::formatAuthorsNames($authors);

$string_authors_names = "";
$greater_than_20 = false;
$authors_count = count($authors);

if ($authors_count > 1) {
$last = $authors[$authors_count - 1];

if ($authors_count >= 20) {
$greater_than_20 = true;
array_splice($authors, 20);
}
else {
array_splice($authors, $authors_count - 1);
}

foreach ($authors as $key => $author) {
$string_authors_names = "{$string_authors_names} {$author['APA']}";

if ($key < count($authors) - 1) {
$string_authors_names = $string_authors_names . static::SEPARATORS['comma'] . static::SEPARATORS['space'];
}
else {
if ($greater_than_20) {
$string_authors_names = $string_authors_names . static::SEPARATORS['space'] . static::SEPARATORS['ellipsis'] . static::SEPARATORS['space'];
}
else {
$string_authors_names = $string_authors_names . static::SEPARATORS['space'] . static::SEPARATORS['ampersand'] . static::SEPARATORS['space'];
}
$string_authors_names = "{$string_authors_names} {$last['APA']}";
}
}
}
else {
$string_authors_names = $authors[0]['APA'];
}

return $string_authors_names;
}

/**
* Return a string with the authors names in MLA format
* @param array $authors
*/
public static function formatAuthorsMla(array $authors)
{

}

/**
* Return a string with the authors names in Chicago format
* @param array $authors
*/
public static function formatAuthorsChicago(array $authors)
{

}

/**
* Receive an array of author names, assuming each name is either already formatted or in the form of First Name Middle initial. Last Name
* Return an array formatted author name for each citation format
*
* @param $author_names
* @return array
*/
public static function formatAuthorsNames(array $author_names): array
{
/** @var array<array> */
$formatted_author_names = [];

foreach ($author_names as $author_name) {
$raw_author_name = trim($author_name);

foreach (array_keys(static::citationFormats()) as $key => $citation_format) {
//If matches given citation format pattern
if (static::matchesRegexPattern($citation_format, $raw_author_name)) {
$formatted_author_name[$citation_format] = ucwords($raw_author_name);
} //If matches last name first initial middle initial pattern
elseif (static::matchesRegexPattern('last_name_initials', $raw_author_name)) {
$formatted_author_name[$citation_format] = static::formatAuthorName($citation_format, $author_name, 'last_name_initials');
}
else { //If matches any other pattern, it will use the first name last name pattern by default to format the name
$formatted_author_name[$citation_format] = static::formatAuthorName($citation_format, $author_name);

}
}

$formatted_author_names[] = $formatted_author_name;
}
return $formatted_author_names;
}

/**
* Format an author name according to a given citation format
*
* @param $citation_format
* @param $full_name
* @return string
*/
public static function formatAuthorName($citation_format, $full_name, $pattern = null) : string
{
$result = '';
$format_components = static::citationFormats()[$citation_format];
$full_name_array = explode(" ", $full_name);
$first_name = static::firstName($full_name_array, $pattern);
$middle_name = static::middleName($full_name_array, $pattern);
$last_name = static::lastName($full_name_array, $pattern);
$first_initial = static::initial($first_name);
$middle_initial = static::initial($middle_name);
$comma = static::SEPARATORS['comma'];
$space = static::SEPARATORS['space'];

foreach ($format_components as $key => $value) {
$result = $result . array_values(compact($value))[0];
}

return trim($result);
}

}
21 changes: 11 additions & 10 deletions app/Http/Controllers/ProfilesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,13 @@ public function create(Request $request, User $user, LdapHelperContract $ldap):
*/
public function edit(Profile $profile, string $section): View|ViewContract|RedirectResponse
{
//dont manage auto-managed publications
//If auto-managed publications
if ($section == 'publications' && $profile->hasOrcidManagedPublications()) {
$profile->updateORCID();
return redirect()
->route('profiles.show', $profile->slug)
->with('flash_message', 'Publications updated via ORCID.');
if ($profile->updateORCID()) {
return redirect()->route('profiles.show', $profile->slug)->with('flash_message', 'Publications updated via ORCID.');
} else {
return redirect()->route('profiles.show', $profile->slug)->with('flash_message', 'Error updating your ORCID publications.');
}
}

$data = $profile->data()->$section()->get();
Expand All @@ -251,11 +252,11 @@ public function edit(Profile $profile, string $section): View|ViewContract|Redir
*/
public function orcid(Profile $profile): RedirectResponse
{
if ($profile->updateORCID()) {
return redirect()->route('profiles.show', $profile->slug)->with('flash_message', 'Publications updated via ORCID.');
} else {
return redirect()->route('profiles.show', $profile->slug)->with('flash_message', 'Error updating your ORCID publications.');
}
if ($profile->updateORCID()) {
return redirect()->route('profiles.show', $profile->slug)->with('flash_message', 'Publications updated via ORCID.');
} else {
return redirect()->route('profiles.show', $profile->slug)->with('flash_message', 'Error updating your ORCID publications.');
}
}

/**
Expand Down
90 changes: 34 additions & 56 deletions app/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\ProfileData;
use App\ProfileStudent;
use App\Repositories\OrcidPublicationsRepository;
use App\Student;
use App\User;
use Illuminate\Database\Eloquent\Model;
Expand All @@ -18,6 +19,7 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;

/**
Expand Down Expand Up @@ -58,6 +60,8 @@ class Profile extends Model implements HasMedia, Auditable
*/
protected $casts = [
'public' => 'boolean',
'contributors' => 'array',
'authors' => 'array',
];

/**
Expand Down Expand Up @@ -173,64 +177,13 @@ public function hasOrcidManagedPublications()

public function updateORCID()
{
$orc_id = $this->information()->get(array('data'))->toArray()[0]['data']['orc_id'];

if(is_null($orc_id)){
//can't update if we don't know your ID
return false;
}

$orc_url = "https://pub.orcid.org/v2.0/" . $orc_id . "/activities";

$client = new Client();

$res = $client->get($orc_url, [
'headers' => [
'Authorization' => 'Bearer ' . config('ORCID_TOKEN'),
'Accept' => 'application/json'
],
'http_errors' => false, // don't throw exceptions for 4xx,5xx responses
]);

//an error of some sort
if($res->getStatusCode() != 200){
return false;
}

$datum = json_decode($res->getBody()->getContents(), true);

foreach($datum['works']['group'] as $record){
$url = NULL;
foreach($record['external-ids']['external-id'] as $ref){
if($ref['external-id-type'] == "eid"){
$url = "https://www.scopus.com/record/display.uri?origin=resultslist&eid=" . $ref['external-id-value'];
}
else if($ref['external-id-type'] == "doi"){
$url = "http://doi.org/" . $ref['external-id-value'];
}
}
$record = ProfileData::firstOrCreate([
'profile_id' => $this->id,
'type' => 'publications',
'data->title' => $record['work-summary'][0]['title']['title']['value'],
'sort_order' => $record['work-summary'][0]['publication-date']['year']['value'] ?? null,
],[
'data' => [
'url' => $url,
'title' => $record['work-summary'][0]['title']['title']['value'],
'year' => $record['work-summary'][0]['publication-date']['year']['value'] ?? null,
'type' => ucwords(strtolower(str_replace('_', ' ', $record['work-summary'][0]['type']))),
'status' => 'Published'
],
]);
}

Cache::tags(['profile_data'])->flush();

//ran through process successfully
return true;
$publications_manager = app()->make(OrcidPublicationsRepository::class);
$publications_manager->setProfile($this);

return $publications_manager->syncPublications();
}


public function updateDatum($section, $request)
{
$sort_order = count($request->data ?? []) + 1;
Expand Down Expand Up @@ -375,6 +328,21 @@ protected function registerImageThumbnails(Media $media = null, $name, $width, $
// Query Scopes //
//////////////////

/**
* Profiles with orcid sync on
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*
*/
public function scopeWithOrcidSyncOn($query) : Builder {
return $query->whereHas('data', function ($data) {
$data
->where('type', 'information')
->where('data', 'like', '%"orc_id_managed": "1"%');
});
}

/**
* Query scope for public Profiles
*
Expand Down Expand Up @@ -596,6 +564,16 @@ public function getApiUrlAttribute()
return route('api.index', ['person' => $this->slug, 'with_data' => true]);
}

/**
* Get the profile ORCID ID
*/
public function getOrcidAttribute()
{
$orc_id = $this->information()->get(array('data'))->toArray()[0]['data']['orc_id'];

return $orc_id ?? null;
}

///////////////
// Relations //
///////////////
Expand Down
Loading