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