diff --git a/src/assets/catalogExample1.json b/src/assets/catalogExample1.json new file mode 100644 index 0000000000000000000000000000000000000000..64f794888956157a184d0773fa6ea4002a2f6325 --- /dev/null +++ b/src/assets/catalogExample1.json @@ -0,0 +1,769 @@ +{ + "@id": "https://data.driihm.fr/catalogs/driihm2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "creator": [ + { + "@id": "https://data.driihm.fr/agents/driihm", + "@type": ["http://xmlns.com/foaf/0.1/Agent", "http://xmlns.com/foaf/0.1/Organization"], + "mbox": "mailto:driihm-admin@services.cnrs.fr", + "name": [ + { + "@language": "en", + "@value": "InterDisciplinary Research Facility on Human-Environment Interactions" + }, + { + "@language": "fr", + "@value": "Laboratoire d'Excellence Dispositif de Recherche Interdisciplinaire sur les Interactions Hommes-Milieux" + } + ] + } + ], + "description": { + "fr": "Catalogue de métadonnées du LabEx DRIIHM", + "en": "DRIIHM LabEx Metadata catalogue" + }, + "http://purl.org/dc/terms/hasPart": [ + { + "@id": "https://data.driihm.fr/catalogs/geodriihm" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-bmp" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-fes" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-hv" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-lc" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-lm" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-oya" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-pdb" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-vr" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-est" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-nun" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-pbe" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-pic" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-tes" + } + ], + "identifier": "2af4cfa0-9f3b-11ee-a39a-d6e96336453a", + "http://purl.org/dc/terms/issued": { + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + "@value": "2023-01-30T00:00:00Z" + }, + "language": [ + { + "@id": "http://publications.europa.eu/resource/authority/language/POR", + "prefLabel": { + "tr": "portekizce", + "en": "Portuguese", + "lv": "portugāļu valoda", + "mt": "Portugiż", + "nl": "Portugees", + "lt": "portugalų kalba", + "ga": "Portaingéilis", + "hsb": "portugalšćina", + "sv": "portugisiska", + "is": "portúgalska", + "el": "πορτογαλικά", + "pl": "portugalski", + "sh": "portugalski", + "hr": "portugalski", + "cs": "portugalština", + "mk": "португалски", + "sr": "португалски", + "bg": "португалски", + "sl": "portugalščina", + "hu": "portugál", + "es": "portugués", + "uk": "португальська мова", + "et": "portugali keel", + "sk": "portugalčina", + "ro": "portugheză", + "de": "Portugiesisch", + "fi": "portugali", + "it": "portoghese", + "ru": "португальский", + "da": "portugisisk", + "no": "portugisisk", + "pt": "português", + "fr": "portugais" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/language/ENG", + "prefLabel": { + "tr": "ıngilizce", + "ru": "английский", + "hr": "engleski", + "sh": "engleski", + "da": "engelsk", + "no": "engelsk", + "pt": "inglês", + "de": "Englisch", + "uk": "англійська мова", + "mt": "Ingliż", + "sl": "angleščina", + "ro": "engleză", + "et": "inglise keel", + "cs": "angličtina", + "sk": "angličtina", + "sr": "енглески", + "nl": "Engels", + "mk": "англиски", + "sv": "engelska", + "ga": "Béarla", + "fi": "englanti", + "lt": "anglų kalba", + "hsb": "jendźelšćina", + "hu": "angol", + "en": "English", + "fr": "anglais", + "es": "inglés", + "is": "enska", + "it": "inglese", + "bg": "английски", + "el": "αγγλικά", + "pl": "angielski", + "lv": "angļu valoda" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/language/FRA", + "prefLabel": { + "bg": "френски", + "sk": "francúzština", + "hsb": "francošćina", + "es": "francés", + "lv": "franču valoda", + "no": "fransk", + "da": "fransk", + "lt": "prancūzų kalba", + "pt": "francês", + "cs": "francouzština", + "tr": "fransızca", + "ru": "французский", + "hr": "francuski", + "pl": "francuski", + "sh": "francuski", + "nl": "Frans", + "fi": "ranska", + "ro": "franceză", + "sl": "francoščina", + "ga": "Fraincis", + "fr": "français", + "is": "franska", + "sv": "franska", + "de": "Französisch", + "el": "γαλλικά", + "mt": "Franċiż", + "sr": "француски", + "mk": "француски", + "uk": "французька мова", + "hu": "francia", + "it": "francese", + "et": "prantsuse keel", + "en": "French" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/language/SPA", + "prefLabel": { + "el": "ισπανικά", + "sk": "španielčina", + "pl": "hiszpański", + "pt": "espanhol", + "bg": "испански", + "tr": "ıspanyolca", + "no": "spansk", + "da": "spansk", + "sh": "španski", + "es": "español", + "ga": "Spáinnis", + "fr": "espagnol", + "sl": "španščina", + "uk": "іспанська мова", + "ro": "spaniolă", + "mt": "Spanjol", + "de": "Spanisch", + "ru": "испанский", + "cs": "španělština", + "nl": "Spaans", + "sv": "spanska", + "lv": "spāņu valoda", + "fi": "espanja", + "it": "spagnolo", + "hu": "spanyol", + "mk": "шпански", + "sr": "шпански", + "hsb": "španišćina", + "lt": "ispanų kalba", + "hr": "španjolski", + "en": "Spanish", + "is": "spænska", + "et": "hispaania keel" + } + } + ], + "http://purl.org/dc/terms/modified": { + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + "@value": "2023-01-30T00:00:00Z" + }, + "publisher": "https://data.driihm.fr/agents/driihm", + "spatial": [ + { + "@id": "http://publications.europa.eu/resource/authority/country/CAN", + "prefLabel": { + "uk": "Канада", + "ru": "Канада", + "bg": "Канада", + "no": "Canada", + "da": "Canada", + "en": "Canada", + "it": "Canada", + "ro": "Canada", + "fr": "Canada", + "nl": "Canada", + "et": "Kanada", + "pl": "Kanada", + "hr": "Kanada", + "hu": "Kanada", + "sl": "Kanada", + "lb": "Kanada", + "de": "Kanada", + "sk": "Kanada", + "is": "Kanada", + "fi": "Kanada", + "sv": "Kanada", + "cs": "Kanada", + "tr": "Kanada", + "lt": "Kanada", + "ca": "Canadà", + "pt": "Canadá", + "es": "Canadá", + "lv": "Kanāda", + "zh": "加拿大", + "el": "Καναδάς", + "ar": "كندا", + "mt": "Il-Kanada", + "ga": "Ceanada", + "ja": "カナダ" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/country/CHL", + "prefLabel": { + "uk": "Чилі", + "lv": "Čīle", + "hu": "Chile", + "en": "Chile", + "ro": "Chile", + "sv": "Chile", + "lb": "Chile", + "de": "Chile", + "no": "Chile", + "es": "Chile", + "fi": "Chile", + "pt": "Chile", + "cs": "Chile", + "is": "Chile", + "da": "Chile", + "pl": "Chile", + "it": "Cile", + "ca": "Xile", + "zh": "智利", + "tr": "Şili", + "sl": "Čile", + "hr": "Čile", + "sk": "Čile", + "el": "Χιλή", + "mt": "Iċ-Ċilì", + "bg": "Чили", + "ru": "Чили", + "lt": "Čilė", + "ja": "チリ", + "ga": "An tSile", + "ar": "شيلي", + "et": "Tšiili", + "nl": "Chili", + "fr": "Chili" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/country/FRA", + "prefLabel": { + "ga": "An Fhrainc", + "el": "Γαλλία", + "cs": "Francie", + "ja": "フランス", + "tr": "Fransa", + "uk": "Франція", + "bs": "Francuska", + "hr": "Francuska", + "ar": "فرنسا", + "bg": "Фpaнция", + "sq": "Franca", + "lt": "Prancūzija", + "hu": "Franciaország", + "eu": "Frantzia", + "pl": "Francja", + "en": "France", + "fr": "France", + "no": "Frankrike", + "sv": "Frankrike", + "fi": "Ranska", + "lb": "Frankräich", + "mk": "Франција", + "ru": "Франция", + "be": "Францыя", + "sl": "Francija", + "lv": "Francija", + "rm": "Frantscha", + "fo": "Frakland", + "ro": "Franţa", + "zh": "法国", + "da": "Frankrig", + "de": "Frankreich", + "sk": "Francúzsko", + "gl": "Francia", + "es": "Francia", + "it": "Francia", + "ca": "França", + "pt": "França", + "nl": "Frankrijk", + "mt": "Franza", + "is": "Frakkland", + "sr": "Француска", + "et": "Prantsusmaa" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/country/PRT", + "prefLabel": { + "eu": "Portugal", + "de": "Portugal", + "bs": "Portugal", + "fr": "Portugal", + "es": "Portugal", + "no": "Portugal", + "gl": "Portugal", + "rm": "Portugal", + "lb": "Portugal", + "ca": "Portugal", + "et": "Portugal", + "da": "Portugal", + "pt": "Portugal", + "hr": "Portugal", + "fo": "Portugal", + "en": "Portugal", + "sv": "Portugal", + "nl": "Portugal", + "be": "Партугалія", + "sk": "Portugalsko", + "cs": "Portugalsko", + "ru": "Португалия", + "ar": "البرتغال", + "lt": "Portugalija", + "ga": "An Phortaingéil", + "ja": "ポルトガル", + "bg": "Пopтyгaлия", + "mk": "Португалија", + "lv": "Portugāle", + "it": "Portogallo", + "hu": "Portugália", + "sq": "Portugalia", + "ro": "Portugalia", + "pl": "Portugalia", + "sl": "Portugalska", + "zh": "葡萄牙", + "is": "Portúgal", + "mt": "Il-Portugall", + "uk": "Португалія", + "tr": "Portekiz", + "fi": "Portugali", + "el": "Πορτογαλία", + "sr": "Португал" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/country/SEN", + "prefLabel": { + "el": "Σενεγάλη", + "hu": "Szenegál", + "lv": "Senegāla", + "fr": "Sénégal", + "ar": "السنغال", + "mt": "Is-Senegal", + "uk": "Сенегал", + "bg": "Сенегал", + "ru": "Сенегал", + "zh": "塞内加尔", + "lt": "Senegalas", + "is": "Senegal", + "it": "Senegal", + "sv": "Senegal", + "es": "Senegal", + "ro": "Senegal", + "nl": "Senegal", + "et": "Senegal", + "cs": "Senegal", + "pt": "Senegal", + "da": "Senegal", + "lb": "Senegal", + "hr": "Senegal", + "sk": "Senegal", + "en": "Senegal", + "sl": "Senegal", + "no": "Senegal", + "tr": "Senegal", + "fi": "Senegal", + "de": "Senegal", + "ca": "Senegal", + "pl": "Senegal", + "ga": "An tSeineagáil", + "ja": "セネガル" + } + }, + { + "@id": "http://publications.europa.eu/resource/authority/country/USA", + "prefLabel": { + "ro": "Statele Unite", + "lt": "Jungtinės Valstijos", + "sl": "Združene države", + "lv": "ASV", + "ga": "Na Stáit Aontaithe", + "de": "Vereinigte Staaten", + "no": "USA", + "el": "Ηνωμένες Πολιτείες", + "fi": "Yhdysvallat", + "hu": "Egyesült Államok", + "fr": "États-Unis", + "da": "Forenede Stater", + "mt": "L-Istati Uniti", + "tr": "Birleşik Devletler", + "lb": "Vereenegt Staaten", + "es": "Estados Unidos", + "pt": "Estados Unidos", + "sk": "Spojené štáty", + "ar": "الولايات المتحدة الأمريكية", + "sv": "Förenta staterna", + "it": "Stati Uniti", + "uk": "США", + "en": "United States", + "ca": "Estats Units", + "is": "Bandaríkin", + "zh": "美国", + "ja": "アメリカ合衆国", + "cs": "Spojené státy", + "pl": "Stany Zjednoczone", + "et": "Ameerika Ühendriigid", + "hr": "Sjedinjene Države", + "bg": "Съединени щати", + "nl": "Verenigde Staten", + "ru": "Соединённые Штаты" + } + } + ], + "http://purl.org/dc/terms/subject": [ + { + "@id": "http://data.europa.eu/bkc/018.03.00.0250" + }, + { + "@id": "http://www.eionet.europa.eu/gemet/concept/5003" + }, + { + "@id": "http://data.europa.eu/bkc/018.04.00.0550" + }, + { + "@id": "http://www.eionet.europa.eu/gemet/concept/15134" + }, + { + "@id": "http://www.eionet.europa.eu/gemet/concept/2470" + }, + { + "@language": "fr", + "@value": "Interactions Hommes-Milieux" + }, + { + "@language": "fr", + "@value": "Écologie globale" + }, + { + "@language": "fr", + "@value": "Socio-écosystème" + }, + { + "@language": "fr", + "@value": "Retro-observation" + }, + { + "@language": "fr", + "@value": "Complexité" + } + ], + "title": { + "fr": "LabEx DRIIHM 2", + "en": "DRIIHM LabEx 2" + }, + "http://purl.org/dc/terms/type": { + "@id": "http://purl.org/dc/dcmitype/Collection" + }, + "http://www.w3.org/ns/dcat#catalog": [ + { + "@id": "https://data.driihm.fr/catalogs/ohm-bmp2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-fes2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-hv2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-lc2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-lm2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-oya2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-pdb2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-vr2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-est2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-nun2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-pbe2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-pic2" + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-tes2" + } + ], + "http://www.w3.org/ns/dcat#dataset": { + "@id": "urn:testdataset:nakala:6d79f9ed-e10e-4eea-8bfe-93c0bdf370dc" + }, + "http://xmlns.com/foaf/0.1/homepage": { + "@id": "https://www.driihm.fr" + }, + "temporal": [], + "status": "__vue_devtool_undefined__", + "contactPoint": [], + "theme": [], + "type": "__vue_devtool_undefined__", + "catalogs": [ + { + "@id": "https://data.driihm.fr/catalogs/ohm-bmp2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Provence Coalfield OHM Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHM Bassin minier de Provence" + }, + "identifier": "2af4d004-9f3b-11ee-a39a-d6e96336453a", + "title": { + "en": "Provence Coalfield OHM", + "fr": "OHM Bassin minier de Provence" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-fes2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "fr": "Catalogue de métadonnées de l'OHM Fessenheim", + "en": "Fessenheim OHM Metadata catalogue" + }, + "identifier": "2af4d018-9f3b-11ee-a39a-d6e96336453a", + "title": { + "en": "Fessenheim OHM", + "fr": "OHM Fessenheim" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-hv2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Pyrénées - Haut Vicdessos/Hautes Vallées des Gaves OHM Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHM Pyrénées - Haut Vicdessos/Hautes Vallées des Gaves" + }, + "identifier": "2af4d022-9f3b-11ee-a39a-d6e96336453a", + "title": { + "fr": "OHM Pyrénées - Haut Vicdessos/Hautes Vallées des Gaves", + "en": "Pyrénées - Haut Vicdessos/Hautes Vallées des Gaves" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-lc2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "fr": "Catalogue de métadonnées de l'OHM Littoral Caraïbe", + "en": "Caribbean coast OHM Metadata catalogue" + }, + "identifier": "2af4d02c-9f3b-11ee-a39a-d6e96336453a", + "title": { + "fr": "OHM Littoral Caraïbe", + "en": "Caribbean coast OHM" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-lm2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "French Mediterranean coastal zone OHM Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHM Littoral méditerranéen" + }, + "identifier": "2af4d040-9f3b-11ee-a39a-d6e96336453a", + "title": { + "fr": "OHM Littoral méditerranéen", + "en": "French Mediterranean coastal zone OHM" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-oya2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "fr": "Catalogue de métadonnées de l'OHM Oyapock", + "en": "Oyapock OHM Metadata catalogue" + }, + "identifier": "2af4d04a-9f3b-11ee-a39a-d6e96336453a", + "title": { + "en": "Oyapock OHM", + "fr": "OHM Oyapock" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-pdb2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Bitche County OHM Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHM Pays de Bitche" + }, + "identifier": "2af4d054-9f3b-11ee-a39a-d6e96336453a", + "title": { + "en": "Bitche County OHM", + "fr": "OHM Pays de Bitche" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohm-vr2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Rhone Valley OHM Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHM Vallée du Rhône" + }, + "identifier": "2af4d05e-9f3b-11ee-a39a-d6e96336453a", + "title": { + "fr": "OHM Vallée du Rhône", + "en": "Rhone Valley OHM" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-est2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Estarreja OHMi Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHMi Estarreja" + }, + "identifier": "2af4d072-9f3b-11ee-a39a-d6e96336453a", + "title": { + "fr": "OHMi Estarreja", + "en": "Estarreja OHMi" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-nun2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Nunavik OHMi Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHMi Nunavik" + }, + "identifier": "2af4d07c-9f3b-11ee-a39a-d6e96336453a", + "title": { + "fr": "OHMi Nunavik", + "en": "Nunavik OHMi" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-pbe2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Patagonia - Bahia Exploradores OHMi Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHMi Patagonia - Bahia Exploradores" + }, + "identifier": "2af4d086-9f3b-11ee-a39a-d6e96336453a", + "title": { + "en": "Patagonia - Bahia Exploradores OHMi", + "fr": "OHMi Patagonia - Bahia Exploradores" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-pic2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "fr": "Catalogue de métadonnées de l'OHMi Pima County", + "en": "Pima County OHMi Metadata catalogue" + }, + "identifier": "2af4d090-9f3b-11ee-a39a-d6e96336453a", + "title": { + "fr": "OHMi Pima County", + "en": "Pima County OHMi" + } + }, + { + "@id": "https://data.driihm.fr/catalogs/ohmi-tes2", + "@type": "http://www.w3.org/ns/dcat#Catalog", + "description": { + "en": "Tessekere OHMi Metadata catalogue", + "fr": "Catalogue de métadonnées de l'OHMi Tessékéré" + }, + "identifier": "2af4d09a-9f3b-11ee-a39a-d6e96336453a", + "title": { + "en": "Tessekere OHMi", + "fr": "OHMi Tessékéré" + } + } + ], + "datasets": [ + { + "@id": "urn:testdataset:nakala:6d79f9ed-e10e-4eea-8bfe-93c0bdf370dc", + "@type": "http://www.w3.org/ns/dcat#Dataset", + "description": { + "fr": "Base de données collectée dans le cadre du stage de master 2 Lorraine Coché \"Inventaire et structuration des données d'observation des mammifères marins autour de la Guadeloupe\" en 2020 (Master Écosystèmes marins tropicaux de l'Université des Antilles). Cette base de données a été constituée dans un esprit de science participative. Elle centralise et harmonise les données d'observation collectées par l'équipe du Sanctuaire Agoa (Aire Marine Protégée), l'OMMAG (Observatoire des Mammifères Marins de l'Archipel Guadeloupéen), BREACH Antilles, et les sociétés de whale-watching Cétacés Caraïbes, Guadeloupe Evasion Découverte et Aventures Marines.", + "en": "Database collected as part of Lorraine Coché's Master 2 course entitled \"Inventory and structuring of marine mammal observation data around Guadeloupe\" in 2020 (Master's degree in Tropical Marine Ecosystems at the University of the West Indies). This database has been set up in the spirit of participatory science. It centralises and harmonises the observation data collected by the Agoa Sanctuary team (Marine Protected Area), OMMAG (Observatoire des Mammifères Marins de l'Archipel Guadeloupéen), BREACH Antilles, and the whale-watching companies Cétacés Caraïbes, Guadeloupe Evasion Découverte and Aventures Marines." + }, + "identifier": "test-6d79f9ed-e10e-4eea-8bfe-93c0bdf370dc", + "title": { + "fr": "KAKILA, Base de données d'observation des mammifères marins autour de l'archipel de la Guadeloupe dans le sanctuaire AGOA - Antilles françaises", + "en": "KAKILA, Marine mammal observation database around the Guadeloupe archipelago in the AGOA sanctuary - French West Indies" + } + } + ] +} diff --git a/src/components/OcTreeCommunity/OcTreeCommunity.vue b/src/components/OcTreeCommunity/OcTreeCommunity.vue index ad90862e8e0ceb73324e185d297c07aaeaf7ef77..a1180b71b9d4c075161bc7f44d0b8e5d2c5990d7 100644 --- a/src/components/OcTreeCommunity/OcTreeCommunity.vue +++ b/src/components/OcTreeCommunity/OcTreeCommunity.vue @@ -1,12 +1,22 @@ <template> <div :class="`p-4 bg-${color}-300 h-full`"> - <h3 v-if="title" class="font-title text-4xl uppercase font-bold text-white mb-4 h-12"> - {{ title }} - </h3> + <RouterLink + v-if="title" + :to="{ + name: 'community', + params: { lang: locale, community: community?.name } + }" + > + <h3 class="font-title text-4xl uppercase font-bold text-white mb-4 h-12"> + {{ title }} + </h3> + </RouterLink> + <h4 v-if="loading && formattedTreeNodes.length == 0" class="font-bold text-xl mt-4"> <i class="animate-spin fas fa-spinner" /> {{ t('loading') }} </h4> + <Tree id="tree" :class="`bg-${color}-300`" @@ -19,7 +29,7 @@ @node-collapse="$emit('nodeCollapse', $event)" @node-unselect="$emit('nodeUnselect', $event)" loadingMode="icon" - class="pl-0 overflow-auto" + class="p-0 overflow-auto" style="height: calc(100% - 3rem)" > <template #nodeicon="{ node }"> @@ -48,7 +58,7 @@ import { ResourceType, type2ResourceType } from '@/helpers/resourceType' import { getResourceVisibility } from '@/helpers/resourceVisibility' import OcVisibilityIcon from '../OcVisibilityIcon/OcVisibilityIcon.vue' -const { t } = useI18n() +const { t, locale } = useI18n() const { translateValue } = useTranslateValue() const props = defineProps({ diff --git a/src/components/descriptors/OcDescriptorCatalog/OcDescriptorCatalog.stories.ts b/src/components/descriptors/OcDescriptorCatalog/OcDescriptorCatalog.stories.ts new file mode 100644 index 0000000000000000000000000000000000000000..b06d7a8d768fd658d091fee29a0fcdb10ddfa46c --- /dev/null +++ b/src/components/descriptors/OcDescriptorCatalog/OcDescriptorCatalog.stories.ts @@ -0,0 +1,27 @@ +import type { Meta, StoryObj } from '@storybook/vue3' + +import OcDescriptorCatalog from './OcDescriptorCatalog.vue' +import catalogExample from '@/assets/catalogExample1.json' + +const meta: Meta<typeof OcDescriptorCatalog> = { + component: OcDescriptorCatalog +} + +export default meta +type Story = StoryObj<typeof OcDescriptorCatalog> + +export const Default: Story = { + render: (args) => ({ + components: { OcDescriptorCatalog }, + setup() { + return { args } + }, + template: '<OcDescriptorCatalog v-bind="args" />' + }), + args: { + catalog: catalogExample, + community: { + name: 'example' + } + } +} diff --git a/src/components/descriptors/OcDescriptorCatalog/OcDescriptorCatalog.vue b/src/components/descriptors/OcDescriptorCatalog/OcDescriptorCatalog.vue new file mode 100644 index 0000000000000000000000000000000000000000..b8566a621106c6dd702dffe83d905e822de16b19 --- /dev/null +++ b/src/components/descriptors/OcDescriptorCatalog/OcDescriptorCatalog.vue @@ -0,0 +1,92 @@ +<template> + <Accordion :value="[]" multiple> + <AccordionPanel value="0" v-if="props.catalog.catalogs"> + <AccordionHeader> + <h2 class="text-xl font-medium mb-2 text-black"> + {{ translateValue(catalogMetadata.catalogs.label) }} + <span v-if="props.catalog.catalogs">({{ props.catalog.catalogs?.length }})</span> + </h2> + </AccordionHeader> + <AccordionContent> + <div + class="mb-4 border bg-white rounded-md p-2" + v-for="catalog in props.catalog.catalogs" + :key="catalog['@id']" + > + <oc-link + :to="{ + name: 'community.resource', + params: { + identifier: catalog.identifier, + community: props.community?.name, + resource: 'catalog' + } + }" + > + <h3 class="mb-2 text-md font-medium hover:text-black"> + {{ translateValue(catalog.title) }} + <i class="fa-solid fa-arrow-up-right-from-square mr-1"></i> + </h3> + </oc-link> + <p class="mb-2"> + {{ translateValue(catalog.description) }} + </p> + </div> + </AccordionContent> + </AccordionPanel> + <AccordionPanel value="1" v-if="props.catalog.datasets"> + <AccordionHeader> + <h2 class="text-xl font-medium mb-2 text-black"> + {{ translateValue(catalogMetadata.datasets.label) }} + <span v-if="props.catalog.datasets">({{ props.catalog.datasets?.length }})</span> + </h2> + </AccordionHeader> + <AccordionContent> + <div + class="mb-4 border bg-white rounded-md p-2" + v-for="dataset in props.catalog.datasets" + :key="dataset['@id']" + > + <oc-link + :to="{ + name: 'community.resource', + params: { + identifier: dataset.identifier, + community: props.community?.name, + resource: 'dataset' + } + }" + > + <h3 class="mb-2 text-md font-medium hover:text-black"> + {{ translateValue(dataset.title) }} + <i class="fa-solid fa-arrow-up-right-from-square mr-1"></i> + </h3> + </oc-link> + <p class="mb-2"> + {{ translateValue(dataset.description) }} + </p> + </div> + </AccordionContent> + </AccordionPanel> + </Accordion> +</template> + +<script setup lang="ts"> +import type { OcCatalog, OcCommunity } from '@/declarations' +import { useTranslateValue } from '@/composables/useTranslateValue' +import OcLink from '@/components/OcLink.vue' + +import Accordion from 'primevue/accordion' +import AccordionHeader from 'primevue/accordionheader' +import AccordionPanel from 'primevue/accordionpanel' +import AccordionContent from 'primevue/accordioncontent' + +import { catalogMetadata } from '@/modelMetadata/catalog' + +const { translateValue } = useTranslateValue('parent') + +const props = defineProps<{ + catalog: OcCatalog + community: OcCommunity +}>() +</script> diff --git a/src/components/descriptors/OcDescriptorResource/OcDescriptorResource.vue b/src/components/descriptors/OcDescriptorResource/OcDescriptorResource.vue index 5cac6dd749dc15a47029fac8e292ec8402712a0f..d9e4622395b028b6269a2ad24a54650101b28849 100644 --- a/src/components/descriptors/OcDescriptorResource/OcDescriptorResource.vue +++ b/src/components/descriptors/OcDescriptorResource/OcDescriptorResource.vue @@ -51,7 +51,7 @@ <div class="shadow-md bg-slate-100 rounded border my-8 p-8 relative" v-else> <header class="flex-1"> <div class="flex justify-between gap-2"> - <h1 class="font-bold text-3xl"> + <h1 class="font-semibold text-3xl"> {{ translateValue(props.resource?.title) || t('descriptors.default.noTitle') }} </h1> <div class="flex flex-col gap-2"> @@ -86,27 +86,23 @@ </header> <div class="flex gap-4 w-full max-w-full"> <main style="width: calc(100% - 20rem)"> - <section class="my-4"> + <section class="my-4" v-if="props.resource?.type"> <h2 class="text-xl font-medium mb-2"> {{ globalTranslateValue(datasetMetadata.type.label) }} </h2> <span class="rounded bg-slate-200 border border-slate-500 text-black font-medium px-2 py-1" - v-if="props.resource?.type" > {{ translateValue(props.resource?.type.prefLabel) }} </span> - <div v-else> - {{ t('descriptors.default.noType') }} - </div> </section> - <section class="my-4"> + <section class="my-4" v-if="props.resource?.theme?.length > 0"> <h2 class="text-xl font-medium mb-2"> {{ globalTranslateValue(datasetMetadata.theme.label) }} - <span v-if="props.resource?.theme"> ({{ props.resource?.theme.length }}) </span> + <span> ({{ props.resource?.theme.length }}) </span> </h2> - <div class="flex gap-2 flex-wrap" v-if="props.resource?.theme"> + <div class="flex gap-2 flex-wrap"> <div class="rounded bg-primary text-white font-medium px-2 py-1" v-for="theme in props.resource?.theme" @@ -115,12 +111,9 @@ {{ translateValue(theme.prefLabel) }} </div> </div> - <div v-else> - {{ t('descriptors.default.noTheme') }} - </div> </section> - <section class="my-4"> + <section class="my-4" v-if="Array.isArray(props.resource?.keyword?.[locale])"> <h2 class="text-xl font-medium mb-2"> {{ globalTranslateValue(datasetMetadata.keyword.label) }} <span @@ -168,7 +161,7 @@ <aside class="w-[20rem]"> <section class="bg-white rounded p-2 flex flex-col gap-2 mb-4" - v-if="props.resource?.spatial" + v-if="props.resource?.spatial?.length > 0" > <h2 class="text-xl font-medium"> {{ globalTranslateValue(datasetMetadata.spatial.label) }} @@ -188,19 +181,19 @@ </div> </section> - <section class="bg-white rounded p-2 mb-4" v-if="props.resource?.temporal"> + <section class="bg-white rounded p-2 mb-4" v-if="props.resource?.temporal?.length > 0"> <h2 class="text-xl font-medium mb-2"> {{ globalTranslateValue(datasetMetadata.temporal.label) }} </h2> <ul> <li v-for="(temporal, key) in props.resource.temporal" :key="key"> - {{ temporal[0].toLocaleDateString(locale) }} - - {{ temporal[1].toLocaleDateString(locale) }} + {{ temporal?.[0]?.toLocaleDateString(locale) }} - + {{ temporal?.[1]?.toLocaleDateString(locale) }} </li> </ul> </section> - <section class="bg-white rounded p-2 mb-4" v-if="props.resource?.creator"> + <section class="bg-white rounded p-2 mb-4" v-if="props.resource?.creator?.length > 0"> <h2 class="text-xl font-medium mb-2"> {{ globalTranslateValue(datasetMetadata.creator.label) }} <span> ({{ props.resource.creator.length }}) </span> @@ -214,7 +207,10 @@ </ul> </section> - <section class="bg-white rounded p-2 mb-4" v-if="props.resource?.contactPoint"> + <section + class="bg-white rounded p-2 mb-4" + v-if="props.resource?.contactPoint?.length > 0" + > <h2 class="text-xl font-medium mb-2"> {{ globalTranslateValue(datasetMetadata.contactPoint.label) }} <span> ({{ props.resource.contactPoint.length }}) </span> diff --git a/src/declarations.ts b/src/declarations.ts index ec5bd930e6fa65d239b0b4128e4b3fcd2f99e2fc..f6c0500dab878b8463625cc2c189184adbe5ee30 100644 --- a/src/declarations.ts +++ b/src/declarations.ts @@ -52,20 +52,6 @@ export type OcResource = { graph?: string[] } -export type OcCatalog = OcResource & { - contactPoint?: Array<OcPerson | OcOrganization> - publisher?: OcOrganization - theme?: OcConcept[] - issued?: Date - temporal?: [Date, Date][] - spatial?: Array<OcConcept | OcGeometry> - parentCatalog?: OcCatalog | OcCatalogSummary -} - -export type OcCatalogSummary = OcResource & { - catalog?: string[] -} - export type OcDataset = OcResource & { otherIdentifier?: OcAdmsIdentifier[] creator?: Array<OcPerson | OcOrganization> @@ -109,6 +95,22 @@ export type OcDatasetSummary = OcResource & { loadingDistributionSummary?: boolean } +export type OcCatalog = OcDataset & { + contactPoint?: Array<OcPerson | OcOrganization> + publisher?: OcOrganization + theme?: OcConcept[] + issued?: Date + temporal?: [Date, Date][] + spatial?: Array<OcConcept | OcGeometry> + parentCatalog?: OcCatalog | OcCatalogSummary + datasets: OcDataset[] + catalogs: OcCatalog[] +} + +export type OcCatalogSummary = OcResource & { + catalog?: string[] +} + export type OcDistribution = OcResource & { license?: OcConceptScheme type?: OcConceptScheme @@ -156,22 +158,22 @@ export type OcConceptScheme = { } export type OcGeometry = { - '@id'?: string, - geometry: Array<{ '@type': string, '@value': string }> + '@id'?: string + geometry: Array<{ '@type': string; '@value': string }> } export type OcOrganization = { '@id': string '@type': string | string[] - identifier?: string, - name: LocalizedProperty<string>, + identifier?: string + name: LocalizedProperty<string> mbox?: string | string[] } export type OcPerson = { - '@id': string, - '@type': string | string[], - identifier?: string, + '@id': string + '@type': string | string[] + identifier?: string familyName?: string givenName?: string firstName?: string @@ -184,7 +186,7 @@ export type OcPerson = { } export type OcMemberInfos = { - '@id': string, + '@id': string loginID: string hasPrivateGraph: string hasSystemGraph: string @@ -237,9 +239,14 @@ export type OcFieldMetadata = { export type OcModelMetadata<T> = Record<keyof T, OcFieldMetadata> export type OcLocale = { - "@id": string + '@id': string prefLabel: LocalizedProperty<string> code: string } -export type OcRole = typeof roleVisitor | typeof roleCommunityMember | typeof roleCommunityManager | typeof rolePlatformManager | typeof roleCatalogManager \ No newline at end of file +export type OcRole = + | typeof roleVisitor + | typeof roleCommunityMember + | typeof roleCommunityManager + | typeof rolePlatformManager + | typeof roleCatalogManager diff --git a/src/modelMetadata/catalog.ts b/src/modelMetadata/catalog.ts index 2fe4dbbd64312a637310cbf5eca76b587da75e63..bc0dddf86856e1c5bb759aa17175d2a9fcd890f0 100644 --- a/src/modelMetadata/catalog.ts +++ b/src/modelMetadata/catalog.ts @@ -1,163 +1,78 @@ import type { OcCatalog, OcModelMetadata } from '@/declarations' +import { datasetMetadata } from './dataset' export const catalogMetadata: OcModelMetadata<OcCatalog> = { - '@id': { - label: '@id' - }, - '@type': { - label: '@type' - }, + ...datasetMetadata, identifier: { - label: { - en: 'Identifier', - fr: 'Identifiant' - }, - required: true, - propertyUri: 'http://purl.org/dc/terms/identifier', - dereferencement: 'https://ec-jrc.github.io/dcat-ap-jrc/#catalogue-identifier', - desc: { - en: 'A unique identifier of the resource being described or catalogued.', - fr: 'Un identifiant unique de la ressource décrite ou cataloguée.' - }, - fair: ['f'], - comment: { - en: 'The identifier is a text string which is assigned to the resource to provide an unambiguous reference within a particular context.', - fr: "L'identifiant est une chaîne de texte attribuée à la ressource pour fournir une référence non ambiguë dans un contexte particulier." - } + ...datasetMetadata.identifier, + dereferencement: 'https://ec-jrc.github.io/dcat-ap-jrc/#catalogue-identifier' }, title: { - propertyUri: 'http://purl.org/dc/terms/title', + ...datasetMetadata.title, dereferencement: 'https://ec-jrc.github.io/dcat-ap-jrc/#catalogue-title', - label: { - en: 'Title', - fr: 'Titre' - }, - required: true, desc: { en: 'The name / title of the catalogue', fr: 'Le nom / titre du catalogue' - }, - fair: ['f', 'r'], - context: { - '@container': '@language' } }, description: { - propertyUri: 'http://purl.org/dc/terms/description', + ...datasetMetadata.description, dereferencement: 'https://ec-jrc.github.io/dcat-ap-jrc/#catalogue-description', - label: { - en: 'Description', - fr: 'Description' - }, - required: true, desc: { en: 'The description of the catalogue', fr: 'La description du catalogue' - }, - fair: ['f', 'r'], - context: { - '@container': '@language' } }, contactPoint: { - propertyUri: 'http://www.w3.org/ns/dcat#contactPoint', + ...datasetMetadata.contactPoint, dereferencement: 'https://ec-jrc.github.io/dcat-ap-jrc/#catalogue-contact-point', - label: { - en: 'Contact Point', - fr: 'Point de contact' - }, - required: true, desc: { en: 'The contact point for the catalogue', fr: 'Le point de contact du catalogue' - }, - fair: ['f', 'a', 'r'] + } }, publisher: { - propertyUri: 'http://purl.org/dc/terms/publisher', + ...datasetMetadata.publisher, dereferencement: 'https://ec-jrc.github.io/dcat-ap-jrc/#catalogue-publisher', - label: { - en: 'Publisher', - fr: 'Éditeur' - }, - required: true, desc: { en: 'The organisation responsible for the publication of the catalogue', fr: "L'organisation responsable de la publication du catalogue" - }, - fair: ['f', 'a'] + } }, theme: { - propertyUri: 'https://www.w3.org/ns/dcat#theme', + ...datasetMetadata.theme, dereferencement: 'https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Catalogue.themes', - label: { - en: 'Theme', - fr: 'Thème' - }, required: false, desc: { en: 'A main category of the catalogue. A catalogue can have multiple themes.', fr: 'Catégorie principale du catalogue. Un catalogue peut avoir plusieurs thèmes.' - }, - fair: ['f', 'i'], - vocabularies: ['http://publications.europa.eu/resource/authority/data-theme'], - context: { - '@container': '@set' } }, issued: { - label: { - en: 'Publication date (release date)', - fr: 'Date de publication' - }, + ...datasetMetadata.issued, required: false, desc: { en: 'Date of formal issuance (e.g., publication) of the catalogue.', fr: "Date d'émission officielle (par exemple, publication) de la ressource." }, - propertyUri: 'http://purl.org/dc/terms/issued', - dereferencement: 'https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Catalogue.releasedate', - fair: ['a'], - comment: { - en: 'This property SHOULD be set using the first known date of issuance.', - fr: "Cette propriété DEVRAIT être définie en utilisant la première date d'émission connue." - } + dereferencement: 'https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Catalogue.releasedate' }, temporal: { - label: { - en: 'Temporal coverage', - fr: 'Couverture temporelle' - }, - required: false, + ...datasetMetadata.temporal, desc: { en: 'The temporal period that the catalogue and its contents covers.', fr: 'Couverture temporelle du catalogue et de ses contenus.' }, - propertyUri: 'http://purl.org/dc/terms/temporal', - dereferencement: 'https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Catalogue.temporalcoverage', - fair: ['f'], - comment: { - en: 'The temporal coverage of a catalogue may be encoded as an instance of dcterms:PeriodOfTime, or may be indicated using an IRI reference (link) to a resource describing a time period or interval.', - fr: "La couverture temporelle d'un catalogue peut être encodée comme une instance de dcterms:PeriodOfTime, ou peut être indiquée en utilisant une référence IRI (lien) vers une ressource décrivant une période ou un intervalle de temps." - } + dereferencement: 'https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Catalogue.temporalcoverage' }, spatial: { - label: { - en: 'Spatial/geographical coverage', - fr: 'Couverture spatiale' - }, - required: false, + ...datasetMetadata.spatial, desc: { en: 'The geographical area covered by the catalogue and its contents.', fr: 'Emprise spatiale/géographique du catalogue et de ses contenus.' }, - propertyUri: 'http://purl.org/dc/terms/spatial', - dereferencement: 'https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Catalogue.geographicalcoverage', - fair: ['f'], - vocabularies: [ - 'http://publications.europa.eu/resource/authority/continent', - 'http://publications.europa.eu/resource/authority/country' - ], + dereferencement: + 'https://semiceu.github.io/DCAT-AP/releases/3.0.0/#Catalogue.geographicalcoverage', comment: { en: 'The spatial coverage of a catalogue may be encoded as an instance of dcterms:Location, or may be indicated using an IRI reference (link) to a resource describing a location. It is recommended that links are to entries in a well maintained gazetteer such as Geonames.', fr: "La couverture spatiale d'un catalogue peut être encodée comme une instance de dcterms:Location, ou peut être indiquée en utilisant une référence IRI (lien) vers une ressource décrivant une localisation. Il est recommandé que les liens renvoient à des entrées d'un répertoire toponymique bien tenu, tel que Geonames." @@ -171,8 +86,20 @@ export const catalogMetadata: OcModelMetadata<OcCatalog> = { required: true, desc: { en: 'The parent catalogue of this catalogue', - fr: "Le catalogue parent de ce catalogue" + fr: 'Le catalogue parent de ce catalogue' }, fair: ['f'] + }, + catalogs: { + label: { + en: 'Sub catalogs', + fr: 'Sous catalogues' + } + }, + datasets: { + label: { + en: 'Datasets', + fr: 'Jeux de données' + } } } diff --git a/src/modelMetadata/dataset.ts b/src/modelMetadata/dataset.ts index 8c4831831d716ed4f0d043c1f364e9034d4c0734..617598a621570333cf69ca63b750ac73a9a12fdc 100644 --- a/src/modelMetadata/dataset.ts +++ b/src/modelMetadata/dataset.ts @@ -1,65 +1,20 @@ import type { OcDataset, OcModelMetadata } from '@/declarations' +import { resourceMetadata } from './resource' export const datasetMetadata: OcModelMetadata<OcDataset> = { - '@id': { - label: '@id' - }, - '@type': { - label: '@type' - }, - identifier: { - label: { - en: 'Identifier', - fr: 'Identifiant' - }, - required: true, - propertyUri: 'http://purl.org/dc/terms/identifier', - dereferencement: 'https://www.w3.org/TR/vocab-dcat-3/#Property:resource_identifier', - desc: { - en: 'A unique identifier of the resource being described or catalogued.', - fr: 'Un identifiant unique de la ressource décrite ou cataloguée.' - }, - fair: ['f'], - comment: { - en: 'The identifier is a text string which is assigned to the resource to provide an unambiguous reference within a particular context.', - fr: "L'identifiant est une chaîne de texte attribuée à la ressource pour fournir une référence non ambiguë dans un contexte particulier." - } - // context: { - // "@type": "http://www.w3.org/2000/01/rdf-schema#Literal" - // } - }, + ...resourceMetadata, title: { - propertyUri: 'http://purl.org/dc/terms/title', - dereferencement: 'https://www.w3.org/TR/vocab-dcat-3/#Property:resource_title', - label: { - en: 'Title', - fr: 'Titre' - }, - required: true, + ...resourceMetadata.title, desc: { en: 'The name / title of the dataset', fr: 'Le nom / titre du jeu de données' - }, - fair: ['f', 'r'], - context: { - '@container': '@language' } }, description: { - propertyUri: 'http://purl.org/dc/terms/description', - dereferencement: 'https://www.w3.org/TR/vocab-dcat-3/#Property:resource_description', - label: { - en: 'Description', - fr: 'Description' - }, - required: true, + ...resourceMetadata.description, desc: { en: 'The description of the dataset', fr: 'La description du jeu de données' - }, - fair: ['f', 'r'], - context: { - '@container': '@language' } }, contactPoint: { @@ -85,8 +40,8 @@ export const datasetMetadata: OcModelMetadata<OcDataset> = { }, required: true, desc: { - en: 'The organisation responsible for the publication of the catalogue', - fr: "L'organisation responsable de la publication du catalogue" + en: 'The organisation responsible for the publication of the dataset', + fr: "L'organisation responsable de la publication du jeu de données" }, fair: ['f', 'a'] }, @@ -114,8 +69,8 @@ export const datasetMetadata: OcModelMetadata<OcDataset> = { }, required: true, desc: { - en: 'A main category of the resource. A resource can have multiple themes.', - fr: 'Catégorie principale de la ressource. Une ressource peut avoir plusieurs thèmes.' + en: 'A main category of the dataset. A dataset can have multiple themes.', + fr: 'Catégorie principale du jeu de donnée. Un jeu de données peut avoir plusieurs thèmes.' }, fair: ['f', 'i'], vocabularies: ['http://publications.europa.eu/resource/authority/data-theme'], @@ -377,10 +332,11 @@ export const datasetMetadata: OcModelMetadata<OcDataset> = { required: true, desc: { en: 'The catalogue where the dataset is documented.', - fr: "Le catalogue dans lequel est référencé le jeu de données" + fr: 'Le catalogue dans lequel est référencé le jeu de données' }, fair: ['f'] }, + distribution: { label: { en: 'Distribution', @@ -404,6 +360,13 @@ export const datasetMetadata: OcModelMetadata<OcDataset> = { }, fair: ['f', 'i'] }, + provenance: { + propertyUri: 'http://purl.org/dc/terms/provenance', + label: { + en: 'Provenance', + fr: 'Provenance' + } + }, inputData: { propertyUri: 'http://purl.org/dc/terms/source', label: { diff --git a/src/modelMetadata/resource.ts b/src/modelMetadata/resource.ts new file mode 100644 index 0000000000000000000000000000000000000000..429b40e013057e7f75a3accb43b1cdc483657571 --- /dev/null +++ b/src/modelMetadata/resource.ts @@ -0,0 +1,65 @@ +import type { OcResource, OcModelMetadata } from '@/declarations' + +export const resourceMetadata: OcModelMetadata<OcResource> = { + '@id': { + label: '@id' + }, + '@type': { + label: '@type' + }, + identifier: { + label: { + en: 'Identifier', + fr: 'Identifiant' + }, + required: true, + propertyUri: 'http://purl.org/dc/terms/identifier', + dereferencement: 'https://www.w3.org/TR/vocab-dcat-3/#Property:resource_identifier', + desc: { + en: 'A unique identifier of the resource being described or catalogued.', + fr: 'Un identifiant unique de la ressource décrite ou cataloguée.' + }, + fair: ['f'], + comment: { + en: 'The identifier is a text string which is assigned to the resource to provide an unambiguous reference within a particular context.', + fr: "L'identifiant est une chaîne de texte attribuée à la ressource pour fournir une référence non ambiguë dans un contexte particulier." + } + // context: { + // "@type": "http://www.w3.org/2000/01/rdf-schema#Literal" + // } + }, + title: { + propertyUri: 'http://purl.org/dc/terms/title', + dereferencement: 'https://www.w3.org/TR/vocab-dcat-3/#Property:resource_title', + label: { + en: 'Title', + fr: 'Titre' + }, + required: true, + desc: { + en: 'The name / title of resource', + fr: 'Le nom / titre de la ressource' + }, + fair: ['f', 'r'], + context: { + '@container': '@language' + } + }, + description: { + propertyUri: 'http://purl.org/dc/terms/description', + dereferencement: 'https://www.w3.org/TR/vocab-dcat-3/#Property:resource_description', + label: { + en: 'Description', + fr: 'Description' + }, + required: true, + desc: { + en: 'The description of resource', + fr: 'La description de la ressource' + }, + fair: ['f', 'r'], + context: { + '@container': '@language' + } + } +} diff --git a/src/pages/community/[community]/[resource]/[identifier].vue b/src/pages/community/[community]/[resource]/[identifier].vue index eb0aa609de7d95b6d4c15d1b437ac4f1d8f0f41f..def8c70a2edc7d30653501614cf94a3762c31622 100644 --- a/src/pages/community/[community]/[resource]/[identifier].vue +++ b/src/pages/community/[community]/[resource]/[identifier].vue @@ -1,9 +1,18 @@ <template> - <OcDescriptorResource :loading="loading" :resource="currentResource" :community="community"> + <OcDescriptorResource + :loading="loading" + :resource="currentResource as OcResource" + :community="community" + > <template v-if="currentResource"> <OcDescriptorDataset v-if="resourceType === 'dataset'" - :dataset="currentResource" + :dataset="currentResource as OcDataset" + :community="community" + /> + <OcDescriptorCatalog + v-if="resourceType === 'catalog'" + :catalog="currentResource as OcCatalog" :community="community" /> </template> @@ -15,12 +24,14 @@ import { useAccountData } from '@/dataLoaders/account' import { computed, onMounted, watch, ref } from 'vue' import { useRoute } from 'vue-router' import { useAccountStore } from '@/stores/account' -import { getDataset } from '@/sparql/datasets' -import type { OcDataset } from '@/declarations' +import type { OcDataset, OcCatalog, OcResource } from '@/declarations' import OcDescriptorResource from '@/components/descriptors/OcDescriptorResource/OcDescriptorResource.vue' import OcDescriptorDataset from '@/components/descriptors/OcDescriptorDataset/OcDescriptorDataset.vue' +import OcDescriptorCatalog from '@/components/descriptors/OcDescriptorCatalog/OcDescriptorCatalog.vue' import { getResource } from '@/sparql/resource' +import { getDataset } from '@/sparql/datasets' +import { getCatalog } from '@/sparql/catalog' import { useCommunityData } from '@/dataLoaders/community' import { useTreeStore } from '@/stores/tree' @@ -41,7 +52,7 @@ const resourceType = computed(() => route.params.resource as string) const loading = ref(false) const error = ref<Error | null>(null) -const currentResource = ref<OcDataset | null>() +const currentResource = ref<OcDataset | OcCatalog | null>() async function loadResource() { loading.value = true @@ -52,6 +63,8 @@ async function loadResource() { currentResource.value = await getDataset(identifier.value, accountStore.auth) break case 'catalog': + currentResource.value = await getCatalog(identifier.value, accountStore.auth) + break case 'service': case 'distribution': currentResource.value = await getResource(identifier.value, accountStore.auth) @@ -65,7 +78,7 @@ async function loadResource() { onMounted(() => { loadResource() - if (treeStore.state.selectedNodeKey === undefined){ + if (treeStore.state.selectedNodeKey === undefined) { // Si on arrive sur la page de ressource sans avoir selectionné un noeud // c'est qu'on arrive par l'URL et on doit charger et ouvrir l'arbre treeStore.fetchTreeForResource(identifier.value, resourceType.value) diff --git a/src/pages/community/[community]/index.vue b/src/pages/community/[community]/index.vue index abc67bdb4d7e75cdc92e340ba20bff1a431613c9..fda5b9c81bab6cd0eea8a3b3fed10b9c9ed48bdb 100644 --- a/src/pages/community/[community]/index.vue +++ b/src/pages/community/[community]/index.vue @@ -1,35 +1,42 @@ <template> <div class="p-4 pl-8 w-11/12 md:w-10/12 xl:w-9/12 m-auto"> - <h1 :class="`font-title text-4xl uppercase font-bold mb-4 text-[--p-primary-color]`"> + + <h1 :class="`font-title text-4xl uppercase font-bold text-[--p-primary-color]`"> {{ title }} </h1> - <h3 class="text-2xl mb-2">{{ t('community.homepage.presentationLabel') }}</h3> - <div class="flex flex-row gap-8"> - <p class="text-justify w-full">{{ abstract }}</p> - <img - v-if="logoUrl" - :src="logoUrl" - alt="" - class="max-w-[200px] max-h-[200px] w-fit h-fit object-cover" - /> - </div> - <IconField class="w-full mt-8 mb-4"> - <InputText - id="search" - fluid - size="large" - :placeholder="t('community.homepage.searchBarPlaceholder')" - /> - <InputIcon class="fa-solid fa-magnifying-glass" /> - </IconField> + <section class="mt-8"> + <h3 class="text-2xl mb-2">{{ t('community.homepage.presentationLabel') }}</h3> + <div class="flex flex-row gap-8"> + <p class="text-justify w-full">{{ abstract }}</p> + <img + v-if="logoUrl" + :src="logoUrl" + alt="" + class="max-w-[200px] max-h-[200px] w-fit h-fit object-cover" + /> + </div> + </section> + + <section class="mt-8"> - <h3 class="text-xl font-bold"> - {{ t('community.homepage.searchBarLegend') }} - </h3> - <hr class="mt-4" /> + <h3 class="text-2xl"> + {{ t('community.homepage.searchBarLegend') }} + </h3> - <div class="flex justify-center"> + <IconField class="w-full mt-4 mb-4"> + <InputText + id="search" + fluid + size="large" + :placeholder="t('community.homepage.searchBarPlaceholder')" + /> + <InputIcon class="fa-solid fa-magnifying-glass" /> + </IconField> + + </section> + + <section class="flex justify-center mt-8" v-if="!account.isAuthenticated || !isMemberOfCurrentCommunity"> <Button v-if="!account.isAuthenticated" :as="OcLink" @@ -48,16 +55,19 @@ > {{ t('community.homepage.joinButtonLabel', { communityName: title }) }} </Button> - </div> - - <h3 v-if="!!account.isAuthenticated && isMemberOfCurrentCommunity" class="text-2xl mt-8 mb-8"> - {{ t('community.homepage.yourServices') }} - </h3> - <OcHomeCommunityActionList - class="mt-8" - :color="color" - :is-authenticated="account.isAuthenticated" - ></OcHomeCommunityActionList> + </section> + + <section class="mt-8"> + <h3 class="text-2xl"> + {{ t('community.homepage.yourServices') }} + </h3> + <OcHomeCommunityActionList + class="mt-4" + :color="color" + :is-authenticated="account.isAuthenticated" + /> + + </section> </div> </template> diff --git a/src/pages/index.vue b/src/pages/index.vue index 0a27689e6a8bca1346a530df14847a7b4652c2a0..f0fbc364d67cf6e40b2840effb8e950304ec0a49 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -22,7 +22,7 @@ </header> <div class="w-11/12 md:w-10/12 lg:w-9/12 xl:w-7/12 mx-auto mb-10"> - <p class="font-bold mb-4 text-center text-xl">{{ t('communities') }}</p> + <h2 class="font-medium mb-4 text-center text-2xl">{{ t('communities') }}</h2> <div class="flex justify-center flex-wrap gap-4"> <OcCardCommunity v-for="(community, index) in communityStore.state.data" diff --git a/src/sparql/catalog.ts b/src/sparql/catalog.ts index 0f1ae00616a6794e05b7317e9019205e84178e2b..1a1e68e7e8a9e94efbb5ee2f5d46edd40f659027 100644 --- a/src/sparql/catalog.ts +++ b/src/sparql/catalog.ts @@ -1,6 +1,25 @@ -import type { Credentials, OcCatalog, OcCatalogSummary, OcOrganization, OcPerson } from "@/declarations" -import { executeSparqlConstruct, executeSparqlInsert, formatDate, formatLocalizedProperty } from "./sparql" -import { resourceContext } from "./resource" +import { + executeSparqlConstruct, + executeSparqlInsert, + formatDate, + formatLocalizedProperty +} from './sparql' +import { resourceContext } from './resource' +import { datasetContext } from './datasets' +import type { + Credentials, + OcCatalog, + OcConcept, + OcDataset, + OcPerson, + OcCatalogSummary, + OcOrganization +} from '@/declarations' +import { conceptContext } from './vocabularies' + +const catalogGetContext = { + ...datasetContext +} export const queryCatalog = async (query: string, locale: string, auth?: Credentials) => { return executeSparqlConstruct<OcCatalog>( @@ -56,9 +75,9 @@ export const getCatalogSummary = async (identifier: string, auth?: Credentials) context: { ...resourceContext, catalog: { - "@id": "http://www.w3.org/ns/dcat#catalog", - "@type": "@id", - "@container": "@set" + '@id': 'http://www.w3.org/ns/dcat#catalog', + '@type': '@id', + '@container': '@set' } } } @@ -91,9 +110,9 @@ export const getCatalogSummaryFromUri = async (uri: string, auth?: Credentials) context: { ...resourceContext, catalog: { - "@id": "http://www.w3.org/ns/dcat#catalog", - "@type": "@id", - "@container": "@set" + '@id': 'http://www.w3.org/ns/dcat#catalog', + '@type': '@id', + '@container': '@set' } } } @@ -128,9 +147,9 @@ export const getCatalogSummaryFromParentUri = async (uri: string, auth?: Credent context: { ...resourceContext, catalog: { - "@id": "http://www.w3.org/ns/dcat#catalog", - "@type": "@id", - "@container": "@set" + '@id': 'http://www.w3.org/ns/dcat#catalog', + '@type': '@id', + '@container': '@set' } } } @@ -158,9 +177,9 @@ export const getCatalogSummaryFromChildUri = async (uri: string, auth?: Credenti context: { ...resourceContext, catalog: { - "@id": "http://www.w3.org/ns/dcat#catalog", - "@type": "@id", - "@container": "@set" + '@id': 'http://www.w3.org/ns/dcat#catalog', + '@type': '@id', + '@container': '@set' } } } @@ -179,7 +198,7 @@ export async function insertCatalog( let insertQuery = ` INSERT INTO <${graph}> { - <${parentCatalog['@id']}> dcat:catalog <${catalog["@id"]}>. + <${parentCatalog['@id']}> dcat:catalog <${catalog['@id']}>. ` insertQuery += buildCatalogTriples(catalog, profile) insertQuery += `}` @@ -190,10 +209,12 @@ export async function insertCatalog( } function buildCatalogTriples(catalog: OcCatalog, profile: OcPerson) { - const contactPoint = (catalog.contactPoint as (OcPerson | OcOrganization)[])?.map((item => `<${item['@id']}>`)).join(',') + const contactPoint = (catalog.contactPoint as (OcPerson | OcOrganization)[]) + ?.map((item) => `<${item['@id']}>`) + .join(',') let triples = ` - <${catalog["@id"]}> a dcat:Catalog; + <${catalog['@id']}> a dcat:Catalog; dct:identifier "${catalog.identifier}"; dct:title ${formatLocalizedProperty(catalog.title)}; dct:description ${formatLocalizedProperty(catalog.description)}; @@ -207,7 +228,7 @@ function buildCatalogTriples(catalog: OcCatalog, profile: OcPerson) { ]` if (catalog.theme) { - const theme = catalog.theme?.map((item => `<${item['@id']}>`)).join(',') + const theme = catalog.theme?.map((item) => `<${item['@id']}>`).join(',') triples += `; dcat:theme ${theme}` } @@ -216,7 +237,7 @@ function buildCatalogTriples(catalog: OcCatalog, profile: OcPerson) { dct:issued ${formatDate(catalog.issued as Date)}` } if (catalog.temporal && catalog.temporal.length) { - const temporal = (catalog.temporal as [Date, Date][]).map(value => { + const temporal = (catalog.temporal as [Date, Date][]).map((value) => { return `[ a dct:PeriodOfTime; dcat:startDate ${formatDate(value[0])}; @@ -228,7 +249,7 @@ function buildCatalogTriples(catalog: OcCatalog, profile: OcPerson) { dct:temporal ${temporal.join(',')}` } if (catalog.spatial && catalog.spatial.length) { - const spatial = catalog.spatial?.map((item => `<${item['@id']}>`)).join(',') + const spatial = catalog.spatial?.map((item) => `<${item['@id']}>`).join(',') triples += `; dct:spatial ${spatial}` } @@ -238,3 +259,268 @@ function buildCatalogTriples(catalog: OcCatalog, profile: OcPerson) { return triples } +/** + * Retrieve a catalog, + * feeded with creators, sub catalogs, datasets, spatial, temporals and themes. + * + * @param identifier Local identifier for Virtuoso + * @param auth Credentials to use if provided + * @returns The catalog retrieved + */ +export async function getCatalog(identifier: string, auth?: Credentials): Promise<OcCatalog> { + /** + * First, get the @id of the catalog + * to better filtering requests. + */ + const catalogIdResponse = await executeSparqlConstruct<OcCatalog>( + ` + CONSTRUCT { + ?catalog ?p ?o. + } + WHERE { + ?catalog a dcat:Catalog; + ?p ?o. + ?catalog dct:identifier ?identifier. + FILTER (str(?identifier) = "${identifier}"). + } + `, + { + auth, + context: catalogGetContext + } + ) + const catalog = catalogIdResponse?.[0] + const catalogUri = catalog['@id'] + + /** + * Temporal + */ + const catalogTemporalPromise = executeSparqlConstruct<{ startDate: Date; endDate: Date }>( + ` + CONSTRUCT { + ?temporal ?p ?o. + } + WHERE { + <${catalogUri}> dct:temporal ?temporal. + OPTIONAL { + ?temporal ?p ?o. + } + } + `, + { + auth, + context: { + startDate: { + '@id': 'http://www.w3.org/ns/dcat#startDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime' + }, + endDate: { + '@id': 'http://www.w3.org/ns/dcat#endDate', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime' + } + } + } + ) + + /** + * Creators + */ + const catalogCreatorPromise = executeSparqlConstruct<OcPerson>( + ` + CONSTRUCT { + ?creator ?p1 ?o1. + } + WHERE { + <${catalogUri}> dct:creator ?creator. + ?creator ?p1 ?o1. + FILTER (?p1 IN (rdf:type, foaf:familyName, foaf:name, foaf:givenName, foaf:firstName, foaf:lastName, foaf:mbox)) + } + `, + { + auth + } + ) + + /** + * Contact points + */ + const catalogContactPointPromise = executeSparqlConstruct<OcPerson>( + ` + CONSTRUCT { + ?contactPoint ?p1 ?o1. + } + WHERE { + <${catalogUri}> dcat:contactPoint ?contactPoint. + ?contactPoint ?p1 ?o1. + FILTER (?p1 IN (rdf:type, foaf:familyName, foaf:name, foaf:givenName, foaf:firstName, foaf:lastName, foaf:mbox)) + } + `, + { + auth + } + ) + + /** + * Spatial + */ + const catalogSpatialPromise = executeSparqlConstruct<OcConcept>( + ` + CONSTRUCT { + ?spatial skos:prefLabel ?o. + } + WHERE { + <${catalogUri}> dct:spatial ?spatial. + ?spatial skos:prefLabel ?o. + } + `, + { + auth, + context: conceptContext + } + ) + + /** + * Theme + */ + const catalogThemePromise = executeSparqlConstruct<OcConcept>( + ` + CONSTRUCT { + ?theme skos:prefLabel ?o. + } + WHERE { + <${catalogUri}> dcat:theme ?theme. + ?theme skos:prefLabel ?o. + } + `, + { + auth, + context: conceptContext + } + ) + + /** + * language + */ + const catalogLanguagePromise = executeSparqlConstruct<OcConcept>( + ` + CONSTRUCT { + ?language skos:prefLabel ?o1. + } + WHERE { + <${catalogUri}> dct:language ?language. + ?language skos:prefLabel ?o1. + } + `, + { + auth, + context: conceptContext + } + ) + + /** + * status + */ + const catalogStatusPromise = executeSparqlConstruct<OcConcept>( + ` + CONSTRUCT { + ?status skos:prefLabel ?o1. + } + WHERE { + <${catalogUri}> adms:status ?status. + ?status skos:prefLabel ?o1. + } + `, + { + auth, + context: conceptContext + } + ) + + /** + * Type + */ + const catalogTypePromise = executeSparqlConstruct<OcConcept>( + ` + CONSTRUCT { + ?type skos:prefLabel ?o. + } + WHERE { + <${catalogUri}> dct:type ?type. + ?type skos:prefLabel ?o. + } + `, + { + auth, + context: conceptContext + } + ) + + /** + * Sub catalogs + */ + const catalogSubsPromise = executeSparqlConstruct<OcCatalog>( + ` + CONSTRUCT { + ?subcatalog ?p ?o. + } + WHERE { + <${catalogUri}> dcat:catalog ?subcatalog. + VALUES ?p { dct:identifier rdf:type dct:title dct:description } + ?subcatalog ?p ?o. + } + `, + { + auth, + context: resourceContext + } + ) + + /** + * Datasets + */ + const catalogDatasetsPromise = executeSparqlConstruct<OcDataset>( + ` + CONSTRUCT { + ?dataset ?p ?o. + } + WHERE { + <${catalogUri}> dcat:dataset ?dataset. + VALUES ?p { dct:identifier rdf:type dct:title dct:description } + ?dataset ?p ?o. + } + `, + { + auth, + context: resourceContext + } + ) + + const catalogResponse = await Promise.all([ + catalogTemporalPromise, // 0 + catalogStatusPromise, // 1 + catalogCreatorPromise, // 2 + catalogContactPointPromise, // 3 + catalogThemePromise, // 4 + catalogSpatialPromise, // 5 + catalogLanguagePromise, // 6 + catalogTypePromise, // 7 + catalogSubsPromise, // 8 + catalogDatasetsPromise // 9 + ]) + + catalog.temporal = catalogResponse[0].map<[Date, Date]>((temporal) => [ + temporal.startDate, + temporal.endDate + ]) + catalog.status = catalogResponse[1][0] + catalog.creator = catalogResponse[2] + catalog.contactPoint = catalogResponse[3] + catalog.theme = catalogResponse[4] + catalog.spatial = catalogResponse[5] + catalog.language = catalogResponse[6] + catalog.type = catalogResponse[7][0] + catalog.catalogs = catalogResponse[8] + catalog.datasets = catalogResponse[9] + + return catalog +} diff --git a/src/sparql/datasets.ts b/src/sparql/datasets.ts index fecf89f3874340b47d688ffb9d10814c49c4c7c6..522ba168cf8602eb542c7e8bcbb81ba73c3118e3 100644 --- a/src/sparql/datasets.ts +++ b/src/sparql/datasets.ts @@ -21,11 +21,7 @@ import { import type { ContextDefinition } from 'jsonld' import { resourceContext } from './resource' -export async function insertDataset( - dataset: OcDataset, - profile: OcPerson, - auth?: Credentials -) { +export async function insertDataset(dataset: OcDataset, profile: OcPerson, auth?: Credentials) { dataset.identifier ||= crypto.randomUUID() dataset['@id'] ||= `${import.meta.env.VITE_OC_URI_BASE_URL}/datasets/${dataset.identifier}` @@ -38,7 +34,7 @@ export async function insertDataset( let insertQuery = ` INSERT INTO <${dataset.graph[0]}> { - <${dataset.catalog['@id']}> dcat:dataset <${dataset["@id"]}>. + <${dataset.catalog['@id']}> dcat:dataset <${dataset['@id']}>. ` insertQuery += buildDatasetTriples(dataset, profile) insertQuery += `}` @@ -48,11 +44,7 @@ export async function insertDataset( return dataset } -export async function updateDataset( - dataset: OcDataset, - profile: OcPerson, - auth?: Credentials -) { +export async function updateDataset(dataset: OcDataset, profile: OcPerson, auth?: Credentials) { // An update consiste of a concomitant delete // and insert. // We first delete all triples handled by our @@ -104,22 +96,26 @@ export async function updateDataset( WITH <${dataset.graph[0]}> INSERT { ${buildDatasetTriples(dataset, profile)} }; - ` - , { auth } + `, + { auth } ) return dataset } function buildDatasetTriples(dataset: OcDataset, profile: OcPerson) { - const theme = dataset.theme?.map((item => `<${item['@id']}>`)).join(',') - const creator = (dataset.creator as (OcPerson | OcOrganization)[])?.map((item => `<${item['@id']}>`)).join(',') - const language = (dataset.language as OcConcept[])?.map((item => `<${item['@id']}>`)).join(',') - const contactPoint = (dataset.contactPoint as (OcPerson | OcOrganization)[])?.map((item => `<${item['@id']}>`)).join(',') + const theme = dataset.theme?.map((item) => `<${item['@id']}>`).join(',') + const creator = (dataset.creator as (OcPerson | OcOrganization)[]) + ?.map((item) => `<${item['@id']}>`) + .join(',') + const language = (dataset.language as OcConcept[])?.map((item) => `<${item['@id']}>`).join(',') + const contactPoint = (dataset.contactPoint as (OcPerson | OcOrganization)[]) + ?.map((item) => `<${item['@id']}>`) + .join(',') let triples = ` - <${dataset["@id"]}> a dcat:Dataset; - dct:type <${dataset.type?.["@id"]}>; + <${dataset['@id']}> a dcat:Dataset; + dct:type <${dataset.type?.['@id']}>; dct:identifier "${dataset.identifier}"; dct:title ${formatLocalizedProperty(dataset.title)}; dcat:theme ${theme}; @@ -138,12 +134,9 @@ function buildDatasetTriples(dataset: OcDataset, profile: OcPerson) { ]` if (dataset.keyword && dataset.keyword.length) { - const keywords: string = formatLocalizedProperty<string[]>( - dataset.keyword, - (item, locale) => { - return item.map(item => defaultLocalizedPropFormatter(item, locale)).join(', ') - } - ) + const keywords: string = formatLocalizedProperty<string[]>(dataset.keyword, (item, locale) => { + return item.map((item) => defaultLocalizedPropFormatter(item, locale)).join(', ') + }) triples += `; dcat:keyword ${keywords}` } @@ -156,7 +149,7 @@ function buildDatasetTriples(dataset: OcDataset, profile: OcPerson) { dct:modified ${formatDate(dataset.modified as Date)}` } if (dataset.temporal && dataset.temporal.length) { - const temporal = (dataset.temporal as [Date, Date][]).map(value => { + const temporal = (dataset.temporal as [Date, Date][]).map((value) => { return `[ a dct:PeriodOfTime; dcat:startDate ${formatDate(value[0])}; @@ -168,7 +161,7 @@ function buildDatasetTriples(dataset: OcDataset, profile: OcPerson) { dct:temporal ${temporal.join(',')}` } if (dataset.spatial && dataset.spatial.length) { - const spatial = dataset.spatial?.map((item => `<${item['@id']}>`)).join(',') + const spatial = dataset.spatial?.map((item) => `<${item['@id']}>`).join(',') triples += `; dct:spatial ${spatial}` } @@ -195,12 +188,12 @@ function buildDatasetTriples(dataset: OcDataset, profile: OcPerson) { return triples } -const datasetContext: ContextDefinition = { +export const datasetContext: ContextDefinition = { ...resourceContext, keyword: { '@id': 'http://www.w3.org/ns/dcat#keyword', '@container': ['@language', '@set'] - }, + } } /** @@ -241,7 +234,7 @@ export async function getDataset(identifier: string, auth?: Credentials): Promis /** * Temporal */ - const datasetTemporalPromise = executeSparqlConstruct<{ startDate: string, endDate: string }>( + const datasetTemporalPromise = executeSparqlConstruct<{ startDate: Date; endDate: Date }>( ` CONSTRUCT { ?temporal ?p ?o. @@ -258,13 +251,12 @@ export async function getDataset(identifier: string, auth?: Credentials): Promis context: { startDate: { '@id': 'http://www.w3.org/ns/dcat#startDate', - '@type': 'http://www.w3.org/2001/XMLSchema#date', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime' }, endDate: { '@id': 'http://www.w3.org/ns/dcat#endDate', - '@type': 'http://www.w3.org/2001/XMLSchema#date', + '@type': 'http://www.w3.org/2001/XMLSchema#dateTime' } - } } ) @@ -380,7 +372,7 @@ export async function getDataset(identifier: string, auth?: Credentials): Promis auth, context: { geometry: { - '@id': "http://www.w3.org/ns/locn#geometry", + '@id': 'http://www.w3.org/ns/locn#geometry', '@container': '@set' } } @@ -648,7 +640,10 @@ export async function getDataset(identifier: string, auth?: Credentials): Promis datasetSpatialGeometryPromise // 14 ]) - dataset.temporal = datasetResponse[0].map<[Date, Date]>(temporal => [new Date(Date.parse(temporal.startDate)), new Date(Date.parse(temporal.endDate))]) + dataset.temporal = datasetResponse[0].map<[Date, Date]>((temporal) => [ + temporal.startDate, + temporal.endDate + ]) dataset.status = datasetResponse[1][0] dataset.creator = datasetResponse[2] dataset.contactPoint = datasetResponse[3] @@ -662,15 +657,19 @@ export async function getDataset(identifier: string, auth?: Credentials): Promis dataset.accrualPeriodicity = datasetResponse[11][0] dataset.license = datasetResponse[12][0] dataset.conformsTo = datasetResponse[13][0] + dataset.spatial = datasetResponse[14] // Format Dates - dataset.issued = dataset.issued ? new Date(Date.parse(dataset.issued)) : dataset.issued - dataset.modified = dataset.modified ? new Date(Date.parse(dataset.modified)) : dataset.modified + // dataset.issued = dataset.issued ? new Date(Date.parse(dataset.issued)) : dataset.issued + // dataset.modified = dataset.modified ? new Date(Date.parse(dataset.modified)) : dataset.modified return dataset } -export async function getUserDatasetSummaries(userUri: string, auth?: Credentials): Promise<OcDatasetSummary[]> { +export async function getUserDatasetSummaries( + userUri: string, + auth?: Credentials +): Promise<OcDatasetSummary[]> { return await executeSparqlConstruct<OcDatasetSummary>( ` CONSTRUCT { @@ -693,12 +692,12 @@ export async function getUserDatasetSummaries(userUri: string, auth?: Credential context: { ...datasetContext, distribution: { - "@id": "http://www.w3.org/ns/dcat#distribution", - "@type": "@id", - "@container": "@set" + '@id': 'http://www.w3.org/ns/dcat#distribution', + '@type': '@id', + '@container': '@set' } }, - auth, + auth } ) } diff --git a/src/sparql/resource.ts b/src/sparql/resource.ts index c039f511e6719c403e1c2de82acf861ce81a1369..2714fd5e47c95b97fdbb71fd507c4f53f484d24f 100644 --- a/src/sparql/resource.ts +++ b/src/sparql/resource.ts @@ -1,7 +1,4 @@ -import { - type OcResource, - type Credentials, -} from '@/declarations' +import { type OcResource, type Credentials } from '@/declarations' import { executeSparqlConstruct } from './sparql' import type { ContextDefinition } from 'jsonld' @@ -27,85 +24,88 @@ export const resourceContext: ContextDefinition = { } } +/** + * @deprecated + */ const resourceFullContext: ContextDefinition = { ...resourceContext, - "language": { - "@id": "http://purl.org/dc/terms/language", - "@type": "@id" + language: { + '@id': 'http://purl.org/dc/terms/language', + '@type': '@id' }, - "contactPoint": { - "@id": "http://www.w3.org/ns/dcat#contactPoint", - "@type": "@id" + contactPoint: { + '@id': 'http://www.w3.org/ns/dcat#contactPoint', + '@type': '@id' }, - "status": { - "@id": "http://www.w3.org/ns/adms#status", - "@type": "@id" + status: { + '@id': 'http://www.w3.org/ns/adms#status', + '@type': '@id' }, - "spatial": { - "@id": "http://purl.org/dc/terms/spatial", - "@type": "@id" + spatial: { + '@id': 'http://purl.org/dc/terms/spatial', + '@type': '@id' }, - "landingPage": { - "@id": "http://www.w3.org/ns/dcat#landingPage", - "@type": "@id" + landingPage: { + '@id': 'http://www.w3.org/ns/dcat#landingPage', + '@type': '@id' }, - "bibliographicCitation": { - "@id": "http://purl.org/dc/terms/bibliographicCitation" + bibliographicCitation: { + '@id': 'http://purl.org/dc/terms/bibliographicCitation' }, - "theme": { - "@id": "http://www.w3.org/ns/dcat#theme", - "@type": "@id" + theme: { + '@id': 'http://www.w3.org/ns/dcat#theme', + '@type': '@id' }, - "publisher": { - "@id": "http://purl.org/dc/terms/publisher", - "@type": "@id" + publisher: { + '@id': 'http://purl.org/dc/terms/publisher', + '@type': '@id' }, - "sameAs": { - "@id": "http://www.w3.org/2002/07/owl#sameAs", - "@type": "@id" + sameAs: { + '@id': 'http://www.w3.org/2002/07/owl#sameAs', + '@type': '@id' }, - "isReferencedBy": { - "@id": "http://purl.org/dc/terms/isReferencedBy", - "@type": "@id" + isReferencedBy: { + '@id': 'http://purl.org/dc/terms/isReferencedBy', + '@type': '@id' }, - "creator": { - "@id": "http://purl.org/dc/terms/creator", - "@type": "@id" + creator: { + '@id': 'http://purl.org/dc/terms/creator', + '@type': '@id' }, - "modified": { - "@id": "http://purl.org/dc/terms/modified", - "@type": "http://www.w3.org/2001/XMLSchema#date" + modified: { + '@id': 'http://purl.org/dc/terms/modified', + '@type': 'http://www.w3.org/2001/XMLSchema#date' }, - "provenance": { - "@id": "http://purl.org/dc/terms/provenance", - "@type": "@id" + provenance: { + '@id': 'http://purl.org/dc/terms/provenance', + '@type': '@id' }, - "temporal": { - "@id": "http://purl.org/dc/terms/temporal", - "@type": "@id" + temporal: { + '@id': 'http://purl.org/dc/terms/temporal', + '@type': '@id' }, - "creator@string": { - "@id": "http://purl.org/dc/elements/1.1/creator" + 'creator@string': { + '@id': 'http://purl.org/dc/elements/1.1/creator' }, - "accrualPeriodicity": { - "@id": "http://purl.org/dc/terms/accrualPeriodicity", - "@type": "@id" + accrualPeriodicity: { + '@id': 'http://purl.org/dc/terms/accrualPeriodicity', + '@type': '@id' }, - "issued": { - "@id": "http://purl.org/dc/terms/issued", - "@type": "http://www.w3.org/2001/XMLSchema#date" + issued: { + '@id': 'http://purl.org/dc/terms/issued', + '@type': 'http://www.w3.org/2001/XMLSchema#date' }, - "keyword": { - "@id": "http://www.w3.org/ns/dcat#keyword", - "@container": "@language" + keyword: { + '@id': 'http://www.w3.org/ns/dcat#keyword', + '@container': '@language' }, - "distribution": { - "@id": "http://www.w3.org/ns/dcat#distribution", - "@type": "@id" + distribution: { + '@id': 'http://www.w3.org/ns/dcat#distribution', + '@type': '@id' }, - "isPartOf": { - "@id": "http://purl.org/dc/terms/isPartOf", - "@type": "@id" + isPartOf: { + '@id': 'http://purl.org/dc/terms/isPartOf', + '@type': '@id' } } @@ -140,7 +140,7 @@ export async function getResource(identifier: string, auth?: Credentials): Promi `, { auth, - context: resourceFullContext + context: resourceContext } ) diff --git a/src/sparql/sparql.ts b/src/sparql/sparql.ts index c554f8e58534247f1fcc41e9efdb5cff186df336..9426382a9266f6f0afbc09bf07defda4ff90a0b6 100644 --- a/src/sparql/sparql.ts +++ b/src/sparql/sparql.ts @@ -1,7 +1,7 @@ -import type { Credentials, LocalizedProperty } from "@/declarations"; -import { httpFetch } from "@/helpers/http"; -import { compact, frame, type ContextDefinition, type NodeObject } from "jsonld"; -import type { Frame } from "jsonld/jsonld-spec"; +import type { Credentials, LocalizedProperty } from '@/declarations' +import { httpFetch } from '@/helpers/http' +import { compact, frame, type ContextDefinition, type NodeObject } from 'jsonld' +import type { Frame } from 'jsonld/jsonld-spec' /** * Execute a CONTRUCT SparQL query and return @@ -14,8 +14,8 @@ import type { Frame } from "jsonld/jsonld-spec"; export async function executeSparqlConstruct<T>( constructQuery: string, options?: { - auth?: Credentials, - context?: ContextDefinition, + auth?: Credentials + context?: ContextDefinition frame?: Frame } ): Promise<T[]> { @@ -24,8 +24,8 @@ export async function executeSparqlConstruct<T>( { method: 'POST', headers: { - 'Content-Type': "application/sparql-update", - "Accept": "application/ld+json" + 'Content-Type': 'application/sparql-update', + Accept: 'application/ld+json' }, body: constructQuery, auth: options?.auth @@ -53,7 +53,7 @@ export async function executeSparqlConstruct<T>( // The lines below are here to normalize this. const graph: T[] = [] if (compacted['@graph'] === undefined) { - const { ['@context']: _, ...data } = compacted; + const { ['@context']: _, ...data } = compacted graph.push(data as T) } else { graph.push(...(compacted['@graph'] as Iterable<T>)) @@ -61,7 +61,9 @@ export async function executeSparqlConstruct<T>( // Casting Dates const dateProp = Object.entries(context) - .filter(([_, prop]: [string, any]) => prop['@type'] === 'http://www.w3.org/2001/XMLSchema#dateTime') + .filter( + ([_, prop]: [string, any]) => prop['@type'] === 'http://www.w3.org/2001/XMLSchema#dateTime' + ) .map(([key, _]: [string, any]) => key) if (dateProp.length) { @@ -80,14 +82,16 @@ export async function executeSparqlConstruct<T>( function normalizeIdentifier(object: NodeObject) { const normalize = (identifier: any) => { if (Array.isArray(identifier)) { - console.warn("Object as several identifier : ", identifier) + console.warn('Object as several identifier : ', identifier) identifier = identifier[0] } - return typeof identifier === 'string' ? identifier : ((identifier as NodeObject)?.['@value'] as string) + return typeof identifier === 'string' + ? identifier + : ((identifier as NodeObject)?.['@value'] as string) } // Normalizing identifiers - if (object['@graph'] === undefined || object['@graph'] === null && object.identifier) { + if (object['@graph'] === undefined || (object['@graph'] === null && object.identifier)) { // Unique value object.identifier = normalize(object.identifier) } else if (Array.isArray(object['@graph'])) { @@ -107,7 +111,7 @@ function normalizeIdentifier(object: NodeObject) { export async function executeSparqlSelect<T>( selectQuery: string, options?: { - auth?: Credentials, + auth?: Credentials } ): Promise<T[]> { const response = await httpFetch( @@ -115,8 +119,8 @@ export async function executeSparqlSelect<T>( { method: 'POST', headers: { - 'Content-Type': "application/sparql-update", - "Accept": "application/json" + 'Content-Type': 'application/sparql-update', + Accept: 'application/json' }, body: selectQuery, auth: options?.auth @@ -133,29 +137,28 @@ export async function executeSparqlSelect<T>( export async function executeSparqlInsert( insertQuery: string, options?: { - auth?: Credentials, - asVisitor?: boolean, + auth?: Credentials + asVisitor?: boolean } ) { if (options?.auth === undefined && options?.asVisitor === undefined) { - throw new Error('Insert SparQL query should be launched with an authentification or as the visitor user.') - } - - return await httpFetch( - `${import.meta.env.VITE_OC_BASE_URL}/sparql-auth`, - { - method: 'POST', - headers: { - 'Content-Type': "application/sparql-update", - "Accept": "application/sparql-results+xml" - }, - body: insertQuery, - auth: options?.auth ?? { - email: import.meta.env.VITE_VISITOR_USER, - password: import.meta.env.VITE_VISITOR_PWD, - } + throw new Error( + 'Insert SparQL query should be launched with an authentification or as the visitor user.' + ) + } + + return await httpFetch(`${import.meta.env.VITE_OC_BASE_URL}/sparql-auth`, { + method: 'POST', + headers: { + 'Content-Type': 'application/sparql-update', + Accept: 'application/sparql-results+xml' + }, + body: insertQuery, + auth: options?.auth ?? { + email: import.meta.env.VITE_VISITOR_USER, + password: import.meta.env.VITE_VISITOR_PWD } - ) + }) } /** @@ -165,30 +168,27 @@ export async function executeSparqlInsert( export async function executeSparqlUpdate( insertQuery: string, options: { - auth?: Credentials, + auth?: Credentials } ) { if (options?.auth === undefined) { throw new Error('Update SparQL query should be launched with an authentification.') } - return await httpFetch( - `${import.meta.env.VITE_OC_BASE_URL}/sparql-auth`, - { - method: 'POST', - headers: { - 'Content-Type': "application/sparql-update", - "Accept": "application/sparql-results+xml" - }, - body: insertQuery, - auth: options.auth - } - ) + return await httpFetch(`${import.meta.env.VITE_OC_BASE_URL}/sparql-auth`, { + method: 'POST', + headers: { + 'Content-Type': 'application/sparql-update', + Accept: 'application/sparql-results+xml' + }, + body: insertQuery, + auth: options.auth + }) } export function escapeString(value?: string) { // eslint-disable-next-line no-useless-escape - return value?.replace(/[\""]/g, '\\"').replace(/\n/g, "\\n") + return value?.replace(/[\""]/g, '\\"').replace(/\n/g, '\\n') } export function formatDate(value: Date | undefined): string | undefined { diff --git a/src/sparql/vocabularies.ts b/src/sparql/vocabularies.ts index f701c0e147e8f64ff8dcd7c973d5e1af49abfeaa..2390646363e2d04915791b7b62b45a709e8d93e7 100644 --- a/src/sparql/vocabularies.ts +++ b/src/sparql/vocabularies.ts @@ -1,34 +1,34 @@ -import type { Credentials, OcConcept, OcConceptScheme, OcLocale } from "@/declarations" -import { executeSparqlConstruct } from "./sparql" -import type { ContextDefinition } from "jsonld" +import type { Credentials, OcConcept, OcConceptScheme, OcLocale } from '@/declarations' +import { executeSparqlConstruct } from './sparql' +import type { ContextDefinition } from 'jsonld' -const conceptContext: ContextDefinition = { - "identifier": { - "@id": "http://purl.org/dc/terms/identifier", - "@type": "http://www.w3.org/2000/01/rdf-schema#Literal" +export const conceptContext: ContextDefinition = { + identifier: { + '@id': 'http://purl.org/dc/terms/identifier', + '@type': 'http://www.w3.org/2000/01/rdf-schema#Literal' }, - "prefLabel": { - "@id": "http://www.w3.org/2004/02/skos/core#prefLabel", - "@container": "@language" + prefLabel: { + '@id': 'http://www.w3.org/2004/02/skos/core#prefLabel', + '@container': '@language' }, - "narrower": { - "@id": "http://www.w3.org/2004/02/skos/core#narrower" + narrower: { + '@id': 'http://www.w3.org/2004/02/skos/core#narrower' } } const schemeContext: ContextDefinition = { - "identifier": { - "@id": "http://purl.org/dc/terms/identifier", - "@type": "http://www.w3.org/2000/01/rdf-schema#Literal" + identifier: { + '@id': 'http://purl.org/dc/terms/identifier', + '@type': 'http://www.w3.org/2000/01/rdf-schema#Literal' }, - "prefLabel": { - "@id": "http://www.w3.org/2004/02/skos/core#prefLabel", - "@container": "@language" + prefLabel: { + '@id': 'http://www.w3.org/2004/02/skos/core#prefLabel', + '@container': '@language' } } export const getVocabulariesList = (schemes: string[], auth?: Credentials) => { - const promiseList = schemes.map(scheme => getVocabularyList(scheme, auth)) + const promiseList = schemes.map((scheme) => getVocabularyList(scheme, auth)) return Promise.all(promiseList).then((values) => { return ([] as OcConcept[]).concat(...values) }) @@ -46,19 +46,29 @@ export const getVocabularyList = (scheme: string, auth?: Credentials) => { `, { auth: auth, - context: conceptContext, + context: conceptContext } ) } -export const queryVocabulariesList = (schemes: string[], query: string, locale: string, auth?: Credentials) => { - const promiseList = schemes.map(scheme => queryVocabulary(scheme, query, locale, auth)) +export const queryVocabulariesList = ( + schemes: string[], + query: string, + locale: string, + auth?: Credentials +) => { + const promiseList = schemes.map((scheme) => queryVocabulary(scheme, query, locale, auth)) return Promise.all(promiseList).then((values) => { return ([] as OcConcept[]).concat(...values) }) } -export const queryVocabulary = (scheme: string, query: string, locale: string, auth?: Credentials) => { +export const queryVocabulary = ( + scheme: string, + query: string, + locale: string, + auth?: Credentials +) => { return executeSparqlConstruct<OcConcept>( ` CONSTRUCT {?s skos:prefLabel ?label.} @@ -72,7 +82,7 @@ export const queryVocabulary = (scheme: string, query: string, locale: string, a `, { auth: auth, - context: conceptContext, + context: conceptContext } ) } @@ -96,7 +106,7 @@ export const getConceptChildren = (scheme: string, parentTerm?: string, auth?: C `, { auth: auth, - context: conceptContext, + context: conceptContext } ) } @@ -119,7 +129,7 @@ export const getVocabularyRootConcepts = async (scheme: string, auth?: Credentia `, { auth: auth, - context: conceptContext, + context: conceptContext } ) @@ -144,7 +154,7 @@ export const getVocabulariesInformations = async (schemes: string[]) => { } `, { - context: schemeContext, + context: schemeContext } ) } @@ -169,18 +179,18 @@ export const getLocaleList = () => { `, { context: { - "identifier": { - "@id": "http://purl.org/dc/terms/identifier", - "@type": "http://www.w3.org/2000/01/rdf-schema#Literal" + identifier: { + '@id': 'http://purl.org/dc/terms/identifier', + '@type': 'http://www.w3.org/2000/01/rdf-schema#Literal' }, - "prefLabel": { - "@id": "http://www.w3.org/2004/02/skos/core#prefLabel", - "@container": "@language" + prefLabel: { + '@id': 'http://www.w3.org/2004/02/skos/core#prefLabel', + '@container': '@language' }, - "code": { - "@id": "http://publications.europa.eu/ontology/authority/legacy-code", - }, - }, + code: { + '@id': 'http://publications.europa.eu/ontology/authority/legacy-code' + } + } } ) } @@ -201,19 +211,19 @@ export const getLicenseList = () => { `, { context: { - "identifier": { - "@id": "http://purl.org/dc/terms/identifier", - "@type": "http://www.w3.org/2000/01/rdf-schema#Literal" + identifier: { + '@id': 'http://purl.org/dc/terms/identifier', + '@type': 'http://www.w3.org/2000/01/rdf-schema#Literal' }, - "prefLabel": { - "@id": "http://www.w3.org/2004/02/skos/core#prefLabel", - "@container": "@language" + prefLabel: { + '@id': 'http://www.w3.org/2004/02/skos/core#prefLabel', + '@container': '@language' }, - "altLabel": { - "@id": "http://www.w3.org/2004/02/skos/core#altLabel", - "@container": "@language" - }, - }, + altLabel: { + '@id': 'http://www.w3.org/2004/02/skos/core#altLabel', + '@container': '@language' + } + } } ) }