Skip to content

Commit bcd7c0f

Browse files
committed
new structure
1 parent 29adc6b commit bcd7c0f

7 files changed

Lines changed: 163 additions & 2443992 deletions

File tree

src/components/Card.svelte

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,27 @@
3030
}
3131
3232
function handleError(event) {
33-
event.target.style.display = 'none'; // Hide image on error
33+
event.target.style.display = 'none';
3434
}
3535
36-
function getImageByNode(node) {
37-
const id = node;
38-
const datum = $entities.find((d) => {
39-
return d['@id'] == id;
40-
});
41-
return datum?.[config.paths.img[0]]?.[0]?.['@id'] || getNestedValue(datum, config.paths.img.join('.'));
36+
import { getItemThumbnail } from '@utils';
37+
38+
const entity = $entities.find((d) => d['@id'] === datum.target);
39+
40+
if (entity?.thumbnail_display_urls?.large) {
41+
imageSrc = entity.thumbnail_display_urls.large;
42+
} else if (entity?.['o:media']?.length) {
43+
for (const ref of entity['o:media']) {
44+
const media = $entities.find((d) => d['@id'] === ref['@id']);
45+
if (media?.thumbnail_display_urls?.large) {
46+
imageSrc = media.thumbnail_display_urls.large;
47+
break;
48+
}
49+
}
4250
}
4351
44-
$: {
45-
imageSrc = getImageByNode(datum.target);
52+
if (!imageSrc) {
53+
imageSrc = '';
4654
}
4755
4856
$: source = datum?.source; //?.split('/').slice(-1)[0];
@@ -104,6 +112,12 @@
104112
{#if datum.title}
105113
<div class="title">{datum.title?.replace(config.url, '') || ''}</div>
106114
{/if}
115+
{:else if datum.target && datum.target.startsWith('http') && !datum.target.includes(config.url)}
116+
<div class="title">
117+
<a href={datum.target} target="_blank" rel="noopener noreferrer">
118+
{datum.title?.replace(config.url, '') || datum.target}
119+
</a>
120+
</div>
107121
{:else}
108122
<div class="title">{datum.title?.replace(config.url, '') || ''}</div>
109123
{/if}

src/components/Graph.svelte

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
.filter((d) => d?.data)
3838
.map((d) => {
3939
return {
40-
img: d.data[config.paths.img?.[0]]?.[0]?.['@id'] || getNestedValue(d.data, config.paths.img.join('.')),
40+
img:
41+
d.data[config.paths.img?.[0]]?.[0]?.['@id'] ||
42+
getNestedValue(d.data, config.paths.img.join('.')),
4143
source: `item_${d.id}`,
4244
target: d.data?.['@id'],
4345
title: d.data?.[config.paths.title] || d.data?.['@id'] || ''
@@ -49,33 +51,35 @@
4951
(graphSteps && $graphSteps?.[0]?.data.length == 0)
5052
) {
5153
$graphSteps[0] = {
52-
data: dataToGraph, // initialStep ?
54+
data: dataToGraph,
5355
new: dataToGraph,
5456
page: 0,
5557
paginate: dataToGraph
5658
};
5759
}
5860
5961
async function loadData(nodes, batchSize) {
60-
const ids = nodes.map((d) => {
61-
// const id = d?.id?.split('/') || d?.target?.split('/');
62-
return d.id || d?.target;
63-
});
64-
65-
const numBatches = Math.ceil(ids.length / batchSize);
62+
const ids = nodes.map((d) => d.id || d.target);
6663
67-
for (let i = 0; i < numBatches; i++) {
68-
const batchIds = ids.slice(i * batchSize, (i + 1) * batchSize);
64+
if (data.links) {
65+
data.links.forEach((link) => {
66+
if (!ids.includes(link.target)) ids.push(link.target);
67+
if (!ids.includes(link.source)) ids.push(link.source);
68+
});
69+
}
6970
70-
// let query = `${config.api}/items?${batchIds.map((id) => `id[]=${id}`).join('&')}`;
71-
// let response = await fetch(query);
72-
// let jsonItems = await response.json();
71+
const uniqueIds = Array.from(new Set(ids));
72+
const numBatches = Math.ceil(uniqueIds.length / batchSize);
7373
74-
let jsonItems = $db.filter((d) => ids.includes(d['@id']) || nodes.includes(d['@id']));
74+
for (let i = 0; i < numBatches; i++) {
75+
const batchIds = uniqueIds.slice(i * batchSize, (i + 1) * batchSize);
7576
77+
let jsonItems = $db.filter((d) => batchIds.includes(d['@id']));
7678
entities.update((items) => {
77-
if (!items.includes(...jsonItems)) {
78-
items.push(...jsonItems);
79+
for (const item of jsonItems) {
80+
if (!items.some((e) => e['@id'] === item['@id'])) {
81+
items.push(item);
82+
}
7983
}
8084
return items;
8185
});
@@ -86,8 +90,6 @@
8690
8791
const getPaginatedData = (index, col) => {
8892
if (col != null) {
89-
// const { scrollTop, scrollHeight, clientHeight } = col;
90-
// if (scrollTop >= 0 && scrollTop + clientHeight >= scrollHeight - 50) {
9193
const page = $graphSteps[index]?.page + 1 || 0;
9294
$graphSteps[index] = {
9395
...$graphSteps[index],
@@ -98,10 +100,8 @@
98100
$graphSteps[index]?.data &&
99101
$graphSteps[index]?.paginate.length != $graphSteps[index]?.data.length
100102
) {
101-
// loading based on the last n items
102103
loadData($graphSteps[index].paginate.slice(-batchSize), batchSize);
103104
}
104-
// }
105105
}
106106
};
107107

src/lib/db.js

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,83 @@ import { writable } from 'svelte/store';
22
import { base } from '$app/paths';
33
import * as config from '@setup';
44

5-
65
export const db = writable([]);
7-
export async function loadDb() {
8-
try {
9-
const url = config.local.includes('http') ? config.local : `${base}/${config.local}`;
10-
console.log(`Fetching URL: ${url}`);
11-
const response = await fetch(url);
12-
13-
if (!response.ok) {
14-
throw new Error(`Failed to load db: ${response.statusText} (Status: ${response.status})`);
15-
}
16-
17-
const contentType = response.headers.get('content-type');
18-
if (!contentType || !contentType.includes('application/json')) {
19-
throw new Error('Response is not valid JSON');
20-
}
21-
22-
const data = await response.json();
23-
db.set(
24-
data.map((d) => ({
25-
...d,
26-
"@id": d["@id"]
27-
?.replace(/\/items\//, '/resources/')
28-
?.replace(/\/media\//, '/resources/')
29-
?.replace(/\/item_sets\//, '/resources/')
30-
}))
31-
);
32-
console.log('Database loaded successfully.');
33-
34-
} catch (error) {
35-
console.error('Error loading db.json:', error);
36-
}
6+
7+
function normalizeOmekaId(url) {
8+
if (!url || !config.api) return url;
9+
10+
try {
11+
const originalUrl = new URL(url);
12+
const configuredApiUrl = new URL(config.api);
13+
14+
const apiIndex = originalUrl.pathname.indexOf('/api');
15+
if (apiIndex === -1) return url;
16+
17+
const pathAfterApi = originalUrl.pathname.slice(apiIndex + 4);
18+
19+
const basePath = configuredApiUrl.pathname.replace(/\/$/, '');
20+
21+
return (
22+
configuredApiUrl.origin +
23+
basePath +
24+
pathAfterApi +
25+
(originalUrl.search || '') +
26+
(originalUrl.hash || '')
27+
);
28+
} catch {
29+
return url;
30+
}
31+
}
32+
33+
function normalizeIdsDeep(obj) {
34+
if (!obj || typeof obj !== 'object') return obj;
35+
36+
for (const key in obj) {
37+
if (key === '@id' && typeof obj[key] === 'string') {
38+
const original = obj[key];
39+
let replaced = original
40+
.replace(/\/items\//, '/resources/')
41+
.replace(/\/media\//, '/resources/')
42+
.replace(/\/item_sets\//, '/resources/');
43+
replaced = normalizeOmekaId(replaced);
44+
45+
// if (original.includes('67423') || replaced.includes('67423')) {
46+
// console.log(`Found specific @id:`, original);
47+
// console.log(`Normalized @id:`, original, '→', replaced);
48+
// }
49+
50+
if (original !== replaced) {
51+
obj[key] = replaced;
52+
}
53+
} else if (typeof obj[key] === 'object') {
54+
normalizeIdsDeep(obj[key]);
55+
}
56+
}
57+
return obj;
3758
}
3859

60+
export async function loadDb() {
61+
try {
62+
const url = config.local.includes('http') ? config.local : `${base}/${config.local}`;
63+
console.log(`Fetching URL: ${url}`);
64+
const response = await fetch(url);
65+
66+
if (!response.ok) {
67+
throw new Error(`Failed to load db: ${response.statusText} (Status: ${response.status})`);
68+
}
69+
70+
const contentType = response.headers.get('content-type');
71+
if (!contentType || !contentType.includes('application/json')) {
72+
throw new Error('Response is not valid JSON');
73+
}
74+
75+
const data = await response.json();
76+
77+
const normalizedData = data.map((item) => normalizeIdsDeep(item));
78+
79+
db.set(normalizedData);
80+
console.log('Database loaded and all @id fields normalized.');
81+
} catch (error) {
82+
console.error('Error loading db.json:', error);
83+
}
84+
}

src/setup.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"title": "ReFa Reader",
33
"api": "https://uclab.fh-potsdam.de/refa-catalog/api",
4-
"local": "db.json",
4+
"local": "db_min.json",
55
"url": "",
66
"paths": {
77
"title": "o:title",

src/utils.js

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,25 @@ export async function extractLinks(markdown, mdData) {
4040
return links;
4141
}
4242

43-
function getItemsFromDbById(ids, mdData, type) {
44-
const baseApiUrl = config.api;
45-
46-
return mdData
47-
.filter((item) => {
48-
const id = item['@id'];
49-
// const idWithoutBaseUrl = id?.split('/').pop();
50-
return ids.find((d) => d == id);
51-
52-
// return ids.includes(id);
53-
})
54-
.map((item) => {
55-
// only for omeka
56-
item['@id'] = item['@id']
57-
?.replace(/\/items\//, '/resources/')
58-
?.replace(/\/media\//, '/resources/')
59-
?.replace(/\/item_sets\//, '/resources/');
60-
return item;
61-
});
43+
// Accepts both full URLs and Omeka IDs
44+
function getItemsFromDbById(ids, db, type) {
45+
return db.filter((item) => {
46+
const itemId = item['@id'];
47+
// Check direct match
48+
if (ids.includes(itemId)) return true;
49+
50+
// Try replacing /media/, /items/, /item_sets/ to /resources/
51+
const normalizedId = itemId
52+
?.replace(/\/items\//, '/resources/')
53+
?.replace(/\/media\//, '/resources/')
54+
?.replace(/\/item_sets\//, '/resources/');
55+
if (ids.includes(normalizedId)) return true;
56+
57+
// Try checking if any provided id matches after normalization
58+
return ids.some(
59+
(id) => id === itemId || id === normalizedId || itemId.endsWith('/' + id) // e.g. just the numeric part
60+
);
61+
});
6262
}
6363

6464
export async function createTriplets(data) {
@@ -135,7 +135,6 @@ export function parseJSONLD(jsonLD, set) {
135135
obj[config.property]?.replace('_', ' ')?.replace(regex, '') ||
136136
parentKey?.replace(regex, '');
137137

138-
139138
const exists = triplets.some(
140139
(triplet) => triplet.source === source && triplet.target === target
141140
);
@@ -176,3 +175,25 @@ export function parseJSONLD(jsonLD, set) {
176175
export function getNestedValue(obj, path) {
177176
return path.split('.').reduce((o, key) => (o || {})[key], obj);
178177
}
178+
export function getItemThumbnail(item, allEntities, preferredSize = 'large') {
179+
if (!item) return null;
180+
181+
// Direct: sometimes image is in the item
182+
if (item.thumbnail_display_urls && item.thumbnail_display_urls[preferredSize]) {
183+
return item.thumbnail_display_urls[preferredSize];
184+
}
185+
186+
// Or in o:media
187+
const mediaArr = item['o:media'];
188+
if (!mediaArr || !mediaArr.length) return null;
189+
190+
for (let mediaRef of mediaArr) {
191+
// Find media entity
192+
let mediaObj = allEntities.find(ent => ent['@id'] === mediaRef['@id'] || ent['o:id'] === mediaRef['o:id']);
193+
if (mediaObj?.thumbnail_display_urls && mediaObj.thumbnail_display_urls[preferredSize]) {
194+
return mediaObj.thumbnail_display_urls[preferredSize];
195+
}
196+
}
197+
198+
return null;
199+
}

0 commit comments

Comments
 (0)