From 8d33275ff66569175dca749b549dfee346e3f1a4 Mon Sep 17 00:00:00 2001
From: Remi PLANEL <rplanel@pasteur.fr>
Date: Mon, 26 Feb 2024 13:10:37 +0100
Subject: [PATCH] get article from meilisearch + cache
---
components/content/ArticleDoi.vue | 21 ++---
composables/useFetchArticle.ts | 129 ++++++++++--------------------
2 files changed, 50 insertions(+), 100 deletions(-)
diff --git a/components/content/ArticleDoi.vue b/components/content/ArticleDoi.vue
index fa7e1b86..bf4ae550 100644
--- a/components/content/ArticleDoi.vue
+++ b/components/content/ArticleDoi.vue
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { useDisplay } from "vuetify";
-import { useArticlesStore } from '@/stores/articles'
+import type { WikiArticle } from '@/types/articles';
export interface Props {
index?: number;
@@ -19,11 +19,14 @@ const props = withDefaults(defineProps<Props>(), {
isRelevant: false,
});
-const { article } = useFetchArticle(props.doi);
+const article = ref<WikiArticle | undefined>(undefined)
const { mobile } = useDisplay();
const show = ref(false);
-
+onMounted(async () => {
+ const { article: articleOnMounted } = await useFetchArticle(props.doi);
+ article.value = articleOnMounted.value
+})
const articleTitle = computed(() => {
return props?.title ?? article?.value?.title ?? props.doi;
});
@@ -32,16 +35,11 @@ const articleAbstract = computed(() => {
});
</script>
<template>
- <v-list-item :href="article?.href" :id="`ref-${props.doi}`" :target="article?.target" density="compact" color="transparent"
- class="px-1">
+ <v-list-item :href="article?.href" :id="`ref-${props.doi}`" :target="article?.target" density="compact"
+ color="transparent" class="px-1">
<template v-if="!mobile" #prepend>
<v-icon icon="md:star" :color="props.isRelevant ? 'info' : 'transparent'"></v-icon>
</template>
- <!-- <template v-if="!mobile" #append>
- <v-btn v-if="articleAbstract" size="x-small" variant="plain"
- :append-icon="show ? 'mdi-chevron-up' : 'mdi-chevron-down'" class="px-0"
- @click.stop.prevent="show = !show">Abstract</v-btn>
- </template> -->
<v-card flat color="transparent" density="compact" class="my-0 article-ref">
<v-card-item density="compact" class="pa-0">
<v-toolbar class="py-0 d-flex align-start article-toolbar" color="transparent" :height="20">
@@ -51,9 +49,6 @@ const articleAbstract = computed(() => {
:append-icon="show ? 'mdi-chevron-up' : 'mdi-chevron-down'" class="px-1 align-center"
@click.stop.prevent="show = !show">Abstract</v-btn>
</v-toolbar>
- <!-- <v-card-title class="py-0"><span class="font-weight-bold">{{
- articleTitle
- }}</span></v-card-title> -->
<v-card-subtitle class="py-0">
{{ article?.subtitle ?? "no authors" }}</v-card-subtitle>
<v-card-subtitle class="py-0">
diff --git a/composables/useFetchArticle.ts b/composables/useFetchArticle.ts
index 760b25c9..eef82b70 100644
--- a/composables/useFetchArticle.ts
+++ b/composables/useFetchArticle.ts
@@ -1,26 +1,22 @@
-import { useArticlesStore } from '../stores/articles'
-import { ref, computed, watchEffect, toValue } from "vue"
-// import { useFetch } from '#app';
-// import { useFetch } from "nuxt"
-import { type MaybeRef, useFetch } from '#imports'
-import type { CslJson, CrossrefArticle, SrcArticle } from '@/types/articles';
+import { ref, toValue } from "vue"
+import { StorageSerializers } from "@vueuse/core"
+import type { CslJson, WikiArticle } from '@/types/articles';
+import type { SearchParams } from 'meilisearch'
-
-
-
-
-
-export function useFetchArticle(doi: MaybeRef<string> = ref("")) {
- // const article = ref<Article>()
- // const rawArticle = ref<RawArticle>()
- const srcArticle = ref<SrcArticle | null>(null)
- const store = useArticlesStore()
- const pending = ref(false)
+export async function useFetchArticle(doi: string = "") {
const doiBaseUrl = ref(new URL("https://doi.org/"));
- const url = ref(new URL(`/works/${toValue(doi)}`, " https://api.crossref.org/").href);
- const article = ref()
- const zoteroArticles = ref()
-
+ // const article = ref<WikiArticle | undefined>(undefined)
+ const article = useSessionStorage<WikiArticle>(doi, null, { serializer: StorageSerializers.object })
+ const client = useMeiliSearchRef()
+ const index = ref("article")
+ const params = ref<SearchParams>({
+ facets: ["*"],
+ filter: [`DOI='${doi}'`],
+ limit: 1
+ })
+ function getReferenceUrl(doi: string) {
+ return new URL(doi, doiBaseUrl.value).href;
+ }
function toAuthorsString(authors: Array<{ family: string; given: string }>) {
return authors
.map((curr) => {
@@ -28,12 +24,7 @@ export function useFetchArticle(doi: MaybeRef<string> = ref("")) {
})
.join(", ");
}
-
- function getReferenceUrl(doi: string) {
- return new URL(doi, doiBaseUrl.value).href;
- }
-
- function zoteroArticleToArticle(zoteroArticle: CslJson) {
+ function zoteroArticleToArticle(zoteroArticle: CslJson): WikiArticle | undefined {
if (zoteroArticle != undefined) {
const {
DOI,
@@ -56,74 +47,38 @@ export function useFetchArticle(doi: MaybeRef<string> = ref("")) {
target: "_blank",
prependIcon: "mdi-newspaper-variant-outline",
}
- }
+ } else { return undefined }
+ }
+ if (!article.value) {
+ const { data, error } = await useAsyncData(
+ doi,
+ async () => {
+ return await client.index(toValue(index)).search<CslJson>("", toValue(params))
+ },
+ {
+ transform: function (data) {
+ if (data !== undefined && data?.hits.length >= 1) {
+ return zoteroArticleToArticle(data.hits[0])
+ }
+ }
+ }
+ )
- }
- function crossrefToArticle(article: CrossrefArticle): WikiArticle {
- const { title, DOI, type, "container-title": ct, "short-container-title": sct, abstract, author, issued } = article
- // let sanitizedAbstract = abstract
- const sanitizedAbstract = abstract ? /(?:\<jats\:p\>)?(.*)(?:\<\/jats\:p\>)?/.exec(abstract)?.[1] ?? '' : ''
- const sanitizedContainerTitle = sct?.length > 0 ? sct[0] : ct?.length > 0 ? ct[0] : ""
- return {
- title: title?.length > 0 ? title[0] : "",
- DOI,
- abstract: sanitizedAbstract,
- containerTitle: sanitizedContainerTitle,
- subtitle: toAuthorsString(author || []),
- author,
- year: issued?.["date-parts"][0][0] ?? '',
- href: getReferenceUrl(DOI),
- target: "_blank",
- prependIcon: "mdi-newspaper-variant-outline"
+ if (error.value) {
+ throw createError({
+ ...error.value,
+ statusMessage: `Could not fetch article ${doi}`
+ })
}
+ article.value = data.value
}
+ return { article }
+}
- function crossrefToCsl(article: CrossrefArticle): CslJson {
- const { title, DOI, type, "container-title": ct, "short-container-title": sct, abstract, author, issued } = article
- // let sanitizedAbstract = abstract
- const sanitizedAbstract = abstract ? /(?:\<jats\:p\>)?(.*)(?:\<\/jats\:p\>)?/.exec(abstract)?.[1] ?? '' : ''
- const sanitizedContainerTitle = sct?.length > 0 ? sct[0] : ct?.length > 0 ? ct[0] : ""
- return {
- title: title?.length > 0 ? title[0] : "",
- type,
- DOI,
- abstract: sanitizedAbstract,
- author,
- "container-title": sanitizedContainerTitle,
- issued
- }
- }
- if (store.articles.has(toValue(doi))) {
- const cslArticle = store.articles.get(toValue(doi))
- article.value = cslArticle ? zoteroArticleToArticle(cslArticle) : undefined
- }
- else {
- useFetch<RawArticle>(toValue(url), {
- lazy: true, server: false,
- }).then(({ data, pending: pendingUseFetch }) => {
- if (data.value?.message) {
- article.value = crossrefToArticle(data.value.message)
- store.add(crossrefToCsl(data.value.message))
- }
- pending.value = pendingUseFetch.value
- })
- }
- // const fetchCrossRef = () => {
- // useFetch<RawArticle>(toValue(url), {
- // lazy: true, server: false,
- // }).then(({ data, pending: pendingUseFetch }) => {
- // if (data.value?.message) {
- // srcArticle.value = data.value.message
- // }
- // pending.value = pendingUseFetch.value
- // })
- // }
- return { article, pending }
-}
--
GitLab