Skip to content

Commit

Permalink
start integrating OpenAlex and other remote data
Browse files Browse the repository at this point in the history
  • Loading branch information
pjc09h committed Jun 7, 2024
1 parent 9a7201d commit 4b13f26
Show file tree
Hide file tree
Showing 52 changed files with 1,339 additions and 381 deletions.
74 changes: 74 additions & 0 deletions app/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class App

public Users $user;

# cache settings
private string $cachePrefix = "app:";
private string $cacheDuration = "1 hour";
private string $cacheAlgorithm = "sha3-512";


/**
* __functions
Expand Down Expand Up @@ -397,4 +402,73 @@ public function error(int|string $error = 400): void
# end all execution
exit;
}


/**
* resolveUriById
*
* @param int|string $id
* @param ?string
*/
public function resolveUriById(int|string $id): ?string
{
$cacheKey = hash($this->cacheAlgorithm, $this->cachePrefix . __FUNCTION__ . json_encode(func_get_args()));
$cacheHit = $this->cache->get($cacheKey);

if ($cacheHit) {
return $cacheHit;
}

# determine the column to search
$column = $this->dbNew->determineId($id);
$extractedId = $this->dbNew->extractId($id);
$fullId = $this->dbNew->fullId($id);

# determine the uri's to use
$baseUri = match ($this->executionContext) {
"api" => "/api",
"cli" => "",
"web" => "",
default => throw new Exception("bad executionContext value"),
};

# [database table => url path]
$tables = [
"collages" => "{$baseUri}/collages/{$extractedId}",
"conversations_messages" => "{$baseUri}/conversations/{$extractedId}",
"conversations_threads" => "{$baseUri}/conversations/{$extractedId}",
"creators" => "{$baseUri}/creators/{$extractedId}",
"literature" => "{$baseUri}/literature/{$extractedId}",
"organizations" => "{$baseUri}/organizations/{$extractedId}",
"publications" => "{$baseUri}/publications/{$extractedId}",
"requests" => "{$baseUri}/requests/{$extractedId}",
"roles_permissions" => "{$baseUri}/roles/{$extractedId}",
"site_log" => "{$baseUri}/log/{$extractedId}",
"tags" => "{$baseUri}/tags/{$extractedId}",
"torrents" => "{$baseUri}/torrents/{$extractedId}",
"torrents_group" => "{$baseUri}/torrents/{$extractedId}",
"users" => "{$baseUri}/users/{$extractedId}",
"wiki_articles" => "{$baseUri}/wiki/{$extractedId}",
];

foreach ($tables as $table => $redirect) {
# does the column exist?
$query = "show columns from {$table} like '{$column}'";
$good = $this->dbNew->single($query, []);

if (!$good) {
continue;
}

$query = "select id from {$table} where {$column} = ?";
$row = $this->dbNew->single($query, [$fullId]);

if (!$row) {
continue;
}

$this->cache->set($cacheKey, $redirect, $this->cacheDuration);
return $redirect;
}
}
} # class
35 changes: 35 additions & 0 deletions app/Autocomplete.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);


/**
* Gazelle\Autocomplete
*/

namespace Gazelle;

class Autocomplete
{
# guzzle client
private \GuzzleHttp\Client $client;
private string $baseUri = "";

# cache settings
private string $cachePrefix = "autocomplete:";
private string $cacheDuration = "1 hour";
private string $cacheAlgorithm = "sha3-512";


/**
* __construct
*/
public function __construct()
{
# https://docs.guzzlephp.org/en/stable/quickstart.html
$this->client = new \GuzzleHttp\Client([
#"base_uri" => $this->baseUri,
"timeout" => 2.0,
]);
}
} # class
68 changes: 68 additions & 0 deletions app/Crossref.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,72 @@ public function __construct()
"timeout" => 2.0,
]);
}


/**
* request
*
* @param string $item, e.g., "journals"
* @param string $id
* @return array
*/
private function request(string $item, string $id): array
{
$app = App::go();

$cacheKey = hash($this->cacheAlgorithm, $this->cachePrefix . __FUNCTION__ . json_encode(func_get_args()));
$cacheHit = $app->cache->get($cacheKey);

if ($cacheHit) {
return $cacheHit;
}

$prefix = match ($item) {
"journals" => "issn",
"works" => "doi",
default => throw new Exception("not implemented"),
};

# https://api.crossref.org/journals/0028-4793
$response = $this->client->get("{$item}/{$id}", ["query" => ["mailto" => $app->env->crossrefEmail]]);

$statusCode = $response->getStatusCode();
if ($statusCode !== 200) {
throw new Exception("http status code {$statusCode}");
}

# decode the api output to an array
$body = json_decode($response->getBody()->getContents() ?? "[]", true);

$app->cache->set($cacheKey, $body, $this->cacheDuration);
return $body;
}


/**
* journals
*
* @param int|string $issn
* @return array
*
* @see https://api.crossref.org/swagger-ui/index.html#/Journals/get_journals__issn_
*/
public function journals($issn): array
{
return $this->request("journals", $issn);
}


/**
* works
*
* @param int|string $doi
* @return array
*
* @see https://api.crossref.org/swagger-ui/index.html#/Works/get_works__doi_
*/
public function works($doi): array
{
return $this->request("works", $doi);
}
} # class
64 changes: 51 additions & 13 deletions app/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,52 +296,53 @@ public function slug(?string $string): string
* Used for finding stuff by id, uuid, or slug.
*
* @param int|string $id
* @param bool $insteadExtractId returns the unique part of the id instead of the column name
* @return string
*/
public function determineId(int|string $id): string
public function determineId(int|string $id, bool $insteadExtractId = false): string
{
$app = App::go();

# cast to string
$id = strval($id);

# openAlex
$good = preg_match("/{$app->env->regexOpenAlex}/", $id);
$good = preg_match("/{$app->env->regexOpenAlex}/", $id, $matches);
if ($good) {
return "openAlexId";
return (!$insteadExtractId ? "openAlexId" : $matches[0]);
}

# doi
$good = preg_match("/{$app->env->regexDoi}/i", $id);
$good = preg_match("/{$app->env->regexDoi}/i", $id, $matches);
if ($good) {
return "doi";
return (!$insteadExtractId ? "doi" : $matches[0]);
}

# orcid
$good = preg_match("/{$app->env->regexOrcid}/i", $id);
$good = preg_match("/{$app->env->regexOrcid}/i", $id, $matches);
if ($good) {
return "orcid";
return (!$insteadExtractId ? "orcid" : $matches[0]);
}

# issn
$good = preg_match("/{$app->env->regexIssn}/", $id);
$good = preg_match("/{$app->env->regexIssn}/", $id, $matches);
if ($good) {
return "issn";
return (!$insteadExtractId ? "issn" : $matches[0]);
}

# rorId
$good = preg_match("/{$app->env->regexRor}/", $id);
$good = preg_match("/{$app->env->regexRor}/", $id, $matches);
if ($good) {
return "rorId";
return (!$insteadExtractId ? "rorId" : $matches[0]);
}

# normal numeric id
if (is_int($id) || is_numeric($id)) {
return "id";
return (!$insteadExtractId ? "id" : $id);
}

# default slug
return "slug";
return (!$insteadExtractId ? "slug" : $id);

/*
# https://ihateregex.io/expr/uuid/
Expand All @@ -357,6 +358,43 @@ public function determineId(int|string $id): string
}


/**
* extractId
*
* Extract the unique part of an id.
*
* @param int|string $id
* @return string
*/
public function extractId(int|string $id): string
{
return $this->determineId($id, true);
}


/**
* fullId
*
* Get the full id from a unique part.
*
* @param int|string $id
* @return string
*/
public function fullId(int|string $id): string
{
$column = $this->determineId($id);
$uniqueId = $this->extractId($id);

return match ($column) {
"openAlexId" => "https://openalex.org/{$uniqueId}",
"doi" => "https://doi.org/{$uniqueId}",
"orcid" => "https://orcid.org/{$uniqueId}",
"rorId" => "https://ror.org/{$uniqueId}",
default => strval($id),
};
}


/**
* translateBinary
*
Expand Down
Loading

0 comments on commit 4b13f26

Please sign in to comment.