diff --git a/README.md b/README.md index 54c17671..9c599485 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,6 @@ COCONUT infrastructure code is licensed under the MIT license - see the [LICENSE ### COCONUT 2.0 - Venkata Chandrasekhar, Kohulan Rajan, Sri Ram Sagar Kanakam, Nisha Sharma, Viktor Weißenborn, Jonas Schaub, Christoph Steinbeck, COCONUT 2.0: a comprehensive overhaul and curation of the collection of open natural products database, Nucleic Acids Research, 2024;, gkae1063, https://doi.org/10.1093/nar/gkae1063 - -https://doi.org/10.1093/nar/gkae1063 ### COCONUT (Legacy) - Sorokina, M., Merseburger, P., Rajan, K. et al. (2021). COCONUT online: COlleCtion of Open Natural prodUcTs database. *Journal of Cheminformatics*, 13, 2. diff --git a/app/Actions/Coconut/AssignDOI.php b/app/Actions/Coconut/AssignDOI.php new file mode 100644 index 00000000..e074baf7 --- /dev/null +++ b/app/Actions/Coconut/AssignDOI.php @@ -0,0 +1,50 @@ +doiService = $doiService; + } + + /** + * Archive the given model. + * + * @param mixed $model + * @return void + */ + public function assign($model) + { + $collection = null; + if ($model instanceof Collection) { + $collection = $model; + } + if ($collection) { + $collectionIdentifier = $collection->identifier ? $collection->identifier : null; + if ($collectionIdentifier == null) { + $collectionTicker = Ticker::whereType('collection')->first(); + $collectionIdentifier = $collectionTicker->index + 1; + $collectionTicker->index = $collectionIdentifier; + $collectionTicker->save(); + + $collection->identifier = $collectionIdentifier; + $collection->save(); + } + $collection->fresh()->generateDOI($this->doiService); + echo $collection->identifier."\r\n"; + } + } +} diff --git a/app/Console/Commands/AssignCollectionsIdentifiers.php b/app/Console/Commands/AssignCollectionsIdentifiers.php index d2148355..2f53919b 100644 --- a/app/Console/Commands/AssignCollectionsIdentifiers.php +++ b/app/Console/Commands/AssignCollectionsIdentifiers.php @@ -50,7 +50,9 @@ public function handle() public function generateIdentifier($index) { - return 'CNPC'.str_pad($index, 4, '0', STR_PAD_LEFT); + $prefix = (env('APP_ENV') === 'production') ? 'CNPC' : 'CNPC_DEV'; + + return $prefix.str_pad($index, 6, '0', STR_PAD_LEFT); } public function fetchLastIndex() diff --git a/app/Console/Commands/AssignDOIs.php b/app/Console/Commands/AssignDOIs.php new file mode 100644 index 00000000..ac47e4b4 --- /dev/null +++ b/app/Console/Commands/AssignDOIs.php @@ -0,0 +1,45 @@ +get(); + + foreach ($collections as $collection) { + $collectionDOI = $collection->doi ? $collection->doi : null; + $assigner->assign($collection); + } + }); + } +} diff --git a/app/Models/Collection.php b/app/Models/Collection.php index 5b4aca08..44a04795 100644 --- a/app/Models/Collection.php +++ b/app/Models/Collection.php @@ -15,6 +15,7 @@ class Collection extends Model implements Auditable, HasMedia { + use HasDOI; use HasFactory; use HasTags; use InteractsWithMedia; @@ -41,6 +42,7 @@ protected static function booted() 'image', 'status', 'release_date', + 'datacite_schema', ]; /** diff --git a/app/Models/HasDOI.php b/app/Models/HasDOI.php new file mode 100644 index 00000000..d8522324 --- /dev/null +++ b/app/Models/HasDOI.php @@ -0,0 +1,114 @@ +getIdentifier($this, 'identifier'); + if ($this->doi == null) { + $url = 'https://coconut.naturalproducts.net/collections/'.$identifier; + $attributes = $this->getMetadata(); + $attributes['url'] = $url; + $doiResponse = $doiService->createDOI($identifier, $attributes); + $this->doi = $doiResponse['data']['id']; + $this->datacite_schema = $doiResponse; + $this->save(); + } + + } + + } + + public function getIdentifier($model, $key) + { + return $model->getAttributes()[$key]; + } + + public function getMetadata() + { + $title = $this->title; + $creators = [ + ['name' => 'COCONUT', + 'nameType' => 'Organizational', ], + ]; + $description = [ + 'description' => $this->description, + 'descriptionType' => 'Other', + ]; + $relatedIdentifiers = [ + [ + 'relatedIdentifier' => $this->url, + 'relatedIdentifierType' => 'URL', + 'relationType' => 'References', + ], + ]; + $dates = [ + [ + 'date' => $this->created_at, + 'dateType' => 'Available', + ], + [ + 'date' => $this->created_at, + 'dateType' => 'Submitted', + ], + [ + 'date' => $this->updated_at, + 'dateType' => 'Updated', + ], + ]; + + $rights = [ + [ + 'rights' => 'Creative Commons Attribution 4.0 International', + 'rightsUri' => 'https://creativecommons.org/licenses/by/4.0/legalcode', + 'rightsIdentifier' => 'CC-BY-4.0', + 'rightsIdentifierScheme' => 'SPDX', + 'schemeUri' => 'https://spdx.org/licenses/', + ], + ]; + $publicationYear = explode('-', $this->created_at)[0]; + $subjects = [ + ['subject' => 'Natural Product', + 'subjectScheme' => 'NCI Thesaurus OBO Edition', + 'schemeURI' => 'http://purl.obolibrary.org/obo/ncit/releases/2022-08-19/ncit.owl', + 'valueURI' => 'http://purl.obolibrary.org/obo/NCIT_C66892', + 'classificationCode' => 'NCIT:C66892', + ], + ]; + $attributes = [ + 'creators' => $creators, + 'titles' => [ + [ + 'title' => $title, + ], + ], + 'dates' => $dates, + 'language' => 'en', + 'rightsList' => $rights, + 'descriptions' => [$description], + 'relatedIdentifiers' => $relatedIdentifiers, + 'resourceType' => 'Collection', + 'resourceTypeGeneral' => 'Collection', + 'publicationYear' => $publicationYear, + 'subjects' => $subjects, + 'types' => [ + 'ris' => 'DATA', + 'bibtex' => 'misc', + 'schemaOrg' => 'Collection', + 'resourceType' => 'Collection', + 'resourceTypeGeneral' => 'Collection', + ], + 'isActive' => true, + 'event' => 'publish', + 'state' => 'findable', + 'schemaVersion' => 'http://datacite.org/schema/kernel-4', + + ]; + + return $attributes; + } +} diff --git a/app/Providers/DOIServiceProvider.php b/app/Providers/DOIServiceProvider.php new file mode 100644 index 00000000..39d981db --- /dev/null +++ b/app/Providers/DOIServiceProvider.php @@ -0,0 +1,24 @@ +app->bind(DOIService::class, function ($app) { + return match (config('doi.default')) { + 'datacite' => new DataCite + }; + }); + } +} diff --git a/app/Services/DOI/DOIService.php b/app/Services/DOI/DOIService.php new file mode 100644 index 00000000..27a37b7f --- /dev/null +++ b/app/Services/DOI/DOIService.php @@ -0,0 +1,18 @@ +client = new Client([ + 'base_uri' => Config::get('doi.'.Config::get('doi.default').'.endpoint'), + 'auth' => [Config::get('doi.'.Config::get('doi.default').'.username'), Config::get('doi.'.Config::get('doi.default').'.secret')], + 'headers' => [ + 'Accept' => 'application/vnd.api+json', + ], + ]); + $this->prefix = Config::get('doi.'.Config::get('doi.default').'.prefix'); + } + + /** + * Returns a list of DOIs + */ + public function getDOIs() + { + $prefix = Config::get('doi.'.Config::get('doi.default').'.prefix'); + $response = $this->client->get('/dois?prefix='.$prefix); + + return $response->getBody(); + } + + public function getDOI($doi) + { + $response = $this->client->get('/dois/'.urlencode($doi)); + + return $response->getBody(); + } + + public function createDOI($identifier, $metadata = []) + { + $doi = $this->prefix.'/'.Config::get('app.name').'.'.$identifier; + $url = 'https://coconut.naturalproducts.net/collections/'.$identifier; + $suffix = Config::get('app.name').'.'.$identifier; + $attributes = [ + 'doi' => $this->prefix.'/'.Config::get('app.name').'.'.$identifier, + 'prefix' => $this->prefix, + 'suffix' => Config::get('app.name').'.'.$identifier, + 'publisher' => Config::get('app.name'), + 'publicationYear' => now()->format('Y'), + 'language' => 'en', + ]; + + foreach ($metadata as $key => $value) { + $attributes[$key] = $value; + } + + $body = [ + 'data' => [ + 'type' => 'dois', + 'attributes' => $attributes, + ], + ]; + + $response = $this->client->post('/dois', + [RequestOptions::JSON => $body] + ); + + $stream = $response->getBody(); + $contents = $stream->getContents(); + + return json_decode($contents, true); + } + + /** + * Update DataCite metadata based on DOI + * + * @param string $doi + * @param array $metadata + * @return array $contents + */ + public function updateDOI($doi, $metadata = []) + { + foreach ($metadata as $key => $value) { + $attributes[$key] = $value; + } + + $body = [ + 'data' => [ + 'type' => 'dois', + 'attributes' => $attributes, + ], + ]; + + $response = $this->client->put('/dois/'.urlencode($doi), + [RequestOptions::JSON => $body] + ); + + $stream = $response->getBody(); + $contents = $stream->getContents(); + $contents = json_decode($contents, true); + + return $contents; + } + + public function deleteDOI($doi) + { + $response = $this->client->delete('/dois/'.urlencode($doi)); + + return $response->getBody(); + } + + public function getDOIActivity($doi) + { + $response = $this->client->get('/dois/'.urlencode($doi).'/activities'); + + return $response->getBody(); + } +} diff --git a/config/app.php b/config/app.php index 0968d310..3acfe4a8 100644 --- a/config/app.php +++ b/config/app.php @@ -177,6 +177,8 @@ Spatie\Permission\PermissionServiceProvider::class, OwenIt\Auditing\AuditingServiceProvider::class, App\Providers\RestDocumentationServiceProvider::class, + App\Providers\DOIServiceProvider::class, + ])->toArray(), /* diff --git a/config/doi.php b/config/doi.php new file mode 100644 index 00000000..4855e6f3 --- /dev/null +++ b/config/doi.php @@ -0,0 +1,36 @@ + env('DOI_PROVIDER', 'datacite'), + + /* + |-------------------------------------------------------------------------- + | DataCite Options + |-------------------------------------------------------------------------- + | + | Here you may specify the configuration options that should be used when + | passwords are hashed using the Argon algorithm. These will allow you + | to control the amount of time it takes to hash the given password. + | + */ + + 'datacite' => [ + 'username' => env('DATACITE_USERNAME'), + 'secret' => env('DATACITE_SECRET'), + 'prefix' => env('DATACITE_PREFIX'), + 'endpoint' => env('DATACITE_ENDPOINT', 'api.test.datacite.org'), + 'contributor_types' => env('DATACITE_CONTRIBUTOR_TYPE', ['ContactPerson', 'DataCollector', 'DataCurator', 'DataManager', 'Distributor', 'Editor', 'HostingInstitution', 'Producer', 'ProjectLeader', 'ProjectManager', 'ProjectMember', 'RegistrationAgency', 'RegistrationAuthority', 'RelatedPerson', 'Researcher', 'ResearchGroup', 'RightsHolder', 'Sponsor', 'Supervisor', 'WorkPackageLeader', 'Other']), + ], + +]; diff --git a/resources/views/livewire/search.blade.php b/resources/views/livewire/search.blade.php index 7f65c618..56e8783e 100644 --- a/resources/views/livewire/search.blade.php +++ b/resources/views/livewire/search.blade.php @@ -13,7 +13,14 @@ $slug = Str::slug($collection->title); $currentYearMonth = now()->format('Y-m'); @endphp - + + @if ($collection->doi) + DOI: + {{ $collection->doi }} +
+ @endif + Download Collection (SDF)