Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.

Commit 6a4945f

Browse files
authored
Merge pull request #40 from sdsc-ordes/chore/error-handeling
Chore/error handeling and add synth chemicals on result sparql query
2 parents 9cfc5c6 + 03235eb commit 6a4945f

16 files changed

Lines changed: 273 additions & 260 deletions

docs/queries/result.rq

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,31 @@ PREFIX cat: <http://example.org/catplus/ontology/>
33
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
44
PREFIX schema: <https://schema.org/>
55

6-
SELECT ?contentUrl ?campaignName ?reactionType ?reactionName ?chemicalName ?casNumber ?smiles
6+
SELECT
7+
?contentUrl
8+
?reactionType
9+
?chemicalName
710
WHERE {
8-
# STEP 2: The outer query takes each ?s from the subquery result
9-
# and finds all the associated details for it.
10-
?s cat:hasBatch ?batch ;
11-
cat:hasChemical ?chemical ;
12-
schema:name ?campaignName ;
13-
schema:contentUrl ?contentUrl .
14-
?batch cat:reactionType ?reactionType ;
15-
cat:reactionName ?reactionName .
16-
?chemical allores:AFR_0002295 ?smiles ;
17-
allores:AFR_0002292 ?chemicalName ;
18-
cat:casNumber ?casNumber .
19-
20-
# --- Subquery Starts Here ---
21-
# STEP 1: This inner query runs first. Its only job is to find the
22-
# unique campaigns (?s) that match the filter, and then apply pagination.
23-
{
24-
SELECT DISTINCT ?s WHERE {
25-
?s rdf:type cat:Campaign ;
26-
cat:hasBatch ?batch ;
27-
cat:hasChemical ?chemical ;
28-
schema:contentUrl ?contentUrl .
29-
?batch cat:reactionType ?reactionType ;
30-
cat:reactionName ?reactionName .
31-
?chemical allores:AFR_0002295 ?smiles .
32-
# The FILTER must be inside the subquery to apply before the LIMIT.
33-
FILTER (?reactionType = 'N-methylation' || ?reactionName = 'Caffeine synthesis' || ?smiles = 'CO')
11+
?s schema:contentUrl ?contentUrl .
12+
?s cat:hasBatch ?batch .
13+
{?s cat:hasChemical ?chemical . ?s a cat:Campaign}
14+
UNION {?action cat:hasBatch ?batch . ?action cat:hasSample* ?sample . ?sample cat:hasChemical ?chemical .}
15+
?batch cat:reactionType ?reactionType .
16+
?chemical allores:AFR_0002292 ?chemicalName .
17+
{
18+
SELECT DISTINCT ?s WHERE {
19+
?batch cat:reactionType ?reactionType .
20+
?s rdf:type cat:Campaign .
21+
?s cat:hasBatch ?batch .
22+
{?s cat:hasChemical ?chemical . ?s a cat:Campaign}
23+
UNION {?action cat:hasBatch ?batch . ?action cat:hasSample* ?sample . ?sample cat:hasChemical ?chemical .}
24+
?s schema:contentUrl ?contentUrl .
25+
FILTER (?reactionType IN ('N-methylation'))
26+
}
27+
ORDER BY ASC(?contentUrl)
28+
LIMIT 3
29+
OFFSET 0
3430
}
35-
ORDER BY ASC(?contentUrl)
36-
LIMIT 3
37-
OFFSET 0
38-
}
39-
#--- Subquery Ends Here ---
4031
}
41-
GROUP BY ?contentUrl ?campaignName ?reactionType ?reactionName ?chemicalName ?casNumber ?smiles
42-
# This sorts the final results that are returned to the user.
32+
GROUP BY ?contentUrl ?reactionType ?chemicalName
4333
ORDER BY ASC(?contentUrl)

src/error.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<h1>Error occured</h1>
2+
<p>Code %sveltekit.status%</p>
3+
<p>%sveltekit.error.message%</p>

src/lib/components/Campaign.svelte

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,4 @@
9292
<div class="p-6 text-center text-surface-500">
9393
<p>Preparing details for {activeCampaign}...</p>
9494
</div>
95-
{:else}
96-
<div class="flex items-center justify-center h-full text-surface-500">
97-
<p class="text-lg">Please select an item from the sidebar to view its details.</p>
98-
</div>
9995
{/if}

src/lib/components/DisplayQleverResults.svelte

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
results,
1515
resultsTotal,
1616
tableHeaders,
17-
handlePageChange,
1817
} = $props();
1918
2019
const headers = ["Campaign"].concat(Object.values(tableHeaders));
@@ -62,6 +61,13 @@
6261
}
6362
}
6463
64+
async function handlePageChange(e: CustomEvent<{ page: number }>) {
65+
const searchParams = new URLSearchParams(page.url.search);
66+
searchParams.set('page', e.page);
67+
68+
await goto(`?${searchParams.toString()}`, {invalidateAll: true});
69+
}
70+
6571
$effect(() => {
6672
// This effect runs whenever the `results` array changes
6773
if (results && results.length > 0) {

src/lib/components/DisplayS3Results.svelte

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
<script lang="ts" generics="ResultItemType extends ResultItemBase">
2-
import IconArrowLeft from '@lucide/svelte/icons/arrow-left';
3-
import IconArrowRight from '@lucide/svelte/icons/arrow-right';
4-
import IconEllipsis from '@lucide/svelte/icons/ellipsis';
5-
import IconFirst from '@lucide/svelte/icons/chevrons-left';
6-
import IconLast from '@lucide/svelte/icons/chevron-right';
72
import Campaign from '$lib/components/Campaign.svelte';
83
import { publicConfig } from '$lib/config';
94
import type { S3FileInfo } from '$lib/server/s3';
@@ -17,6 +12,7 @@
1712
// get props from data loader
1813
let {
1914
results,
15+
resultsTotal,
2016
tableHeaders
2117
} = $props();
2218

src/lib/components/Header.svelte

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,24 @@
11
<script lang="ts">
2-
import { AppBar } from '@skeletonlabs/skeleton-svelte';
3-
import { base } from '$app/paths';
4-
import { page } from '$app/stores'; // <-- Import the page store
5-
6-
// Import all your icons
7-
import Paperclip from '@lucide/svelte/icons/paperclip';
8-
import Calendar from '@lucide/svelte/icons/calendar';
9-
import Home from '@lucide/svelte/icons/home';
10-
import Database from '@lucide/svelte/icons/database';
11-
import CircleUser from '@lucide/svelte/icons/circle-user';
12-
import Search from '@lucide/svelte/icons/search';
13-
14-
// --- Define your navigation items in an array ---
15-
const navItems = [
16-
{ href: `${base}/`, label: 'Home', icon: Home },
17-
{ href: `${base}/search`, label: 'Search', icon: Search },
18-
{ href: `${base}/data`, label: 'Data', icon: Database }
19-
];
2+
import { AppBar } from '@skeletonlabs/skeleton-svelte';
3+
import { base } from '$app/paths';
4+
import Paperclip from '@lucide/svelte/icons/paperclip';
5+
import Calendar from '@lucide/svelte/icons/calendar';
6+
import Home from '@lucide/svelte/icons/home';
7+
import Database from '@lucide/svelte/icons/database';
8+
import CircleUser from '@lucide/svelte/icons/circle-user';
9+
import Search from '@lucide/svelte/icons/search';
2010
</script>
2111

2212
<AppBar classes="flex items-left">
23-
{#snippet lead()}
24-
{#each navItems as item}
25-
<a
26-
href={item.href}
27-
class="flex items-center mx-4 gap-x-2 transition-colors hover:text-primary-500"
28-
class:text-primary-500={$page.url.pathname === item.href}
29-
>
30-
<svelte:component this={item.icon} size={20} />
31-
<span>{item.label}</span>
32-
</a>
33-
{/each}
34-
{/snippet}
35-
{#snippet trail()}
36-
<Paperclip size={20} />
37-
<Calendar size={20} />
38-
<CircleUser size={20} />
39-
{/snippet}
40-
<span>CAT+ Chemboard</span>
41-
</AppBar>
13+
{#snippet lead()}
14+
<a href="{base}/" class="flex items-center mx-4 gap-x-2"><Home size={20} />Home</a>
15+
<a href="{base}/search" class="flex items-center mx-4 gap-x-2"><Search size={20} />Search</a>
16+
<a href="{base}/data" class="flex items-center mx-4 gap-x-2"><Database size={20} />Data</a>
17+
{/snippet}
18+
{#snippet trail()}
19+
<Paperclip size={20} />
20+
<Calendar size={20} />
21+
<CircleUser size={20} />
22+
{/snippet}
23+
<span>CAT+ Chemboard</span>
24+
</AppBar>

src/lib/components/ResultsHeaderData.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,9 @@
66

77
<h1 class="mb-4 p-4 text-xl font-bold text-surface-800-200">
88
Results ({resultsTotal})
9-
</h1>
9+
</h1>
10+
{#if !resultsTotal}
11+
<p>
12+
No results have been found for this prefix: please try another search.
13+
</p>
14+
{/if}

src/lib/components/ResultsHeaderSearch.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
const qleverUrl = `${publicConfig.PUBLIC_QLEVER_UI_URL}?query=${encodedQuery}`;
1616
1717
let showQuery = $state(false);
18-
$inspect("initialFilters", initialFilers);
19-
$inspect("resultColumns", resultColumns);
2018
</script>
2119

2220
<h1 class="mb-4 p-4 text-xl font-bold text-surface-800-200">

src/lib/components/S3SearchForm.svelte

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
} = $props();
88
</script>
99

10-
<div class="bg-secondary-50-800 space-y-4 rounded p-4">
10+
<div class="space-y-4 rounded p-4">
1111
<h1 class="mb-4 text-2xl font-bold text-surface-800-200">Campaign Data</h1>
12-
<form method="POST" action="?/filter" class="card space-y-4 p-4">
12+
<form method="POST" action="?/filter" class="space-y-4 p-4">
1313
<label class="label">
1414
<span>Filter Campaigns by Prefix:</span>
1515
<input name="prefix" class="input" type="string" value={prefix} required />
@@ -20,12 +20,14 @@
2020
</button>
2121
</div>
2222
</form>
23-
<ul>
24-
<li>All Campaigns start with `batch`</li>
25-
<li>Next comes the year as `YYYY`</li>
26-
<li>the month as `MM`</li>
27-
<li>the day `DD`</li>
28-
<li>a number `NN`</li>
29-
</ul>
30-
<p>Example `batch/2024/05/16/22`</p>
23+
<div class="text-sm">
24+
<ul class="list-disc list-inside">
25+
<li>All Campaigns start with `batch`</li>
26+
<li>Next comes the year as `YYYY`</li>
27+
<li>the month as `MM`</li>
28+
<li>the day `DD`</li>
29+
<li>a number `NN`</li>
30+
</ul>
31+
<p>Example `batch/2024/05/16/22`</p>
32+
</div>
3133
</div>

src/lib/server/s3.ts

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ export async function listFilesInBucket(prefix: string): Promise<S3FileInfo[]> {
140140
}
141141
});
142142
const response = await s3Client.send(command);
143-
const files = response.Contents?.map((item) => item.Key || '').filter(Boolean) as string[] || [];
144143
const fileInfoList: S3FileInfo[] = (response.Contents || [])
145144
.filter(s3Object => s3Object.Key)
146145
.map(s3Object => {
@@ -197,54 +196,48 @@ export async function findLeafPrefixes(
197196
// Start with a list containing only the initial prefix
198197
let prefixesToExplore = [startPrefix];
199198

200-
try {
201-
// We determine the current depth by counting slashes in the startPrefix
202-
const startDepth = (startPrefix.match(/\//g) || []).length;
203-
logger.info({startPrefix, startDepth}, "prefix search")
204-
205-
// Loop from the current depth until we reach the target depth
206-
for (let currentDepth = startDepth; currentDepth < targetDepth; currentDepth++) {
207-
logger.info(`Discovering prefixes at depth ${currentDepth + 1}...`);
208-
209-
// Fetch all sub-prefixes for the current level in parallel
210-
const promises = prefixesToExplore.map(prefix => {
211-
const command = new ListObjectsV2Command({
212-
Bucket: AppServerConfig.S3.S3_BUCKET_NAME,
213-
Prefix: prefix,
214-
Delimiter: '/',
215-
});
216-
return s3Client.send(command);
217-
});
199+
// Determine the current depth by counting slashes in the startPrefix
200+
const startDepth = (startPrefix.match(/\//g) || []).length;
201+
logger.debug({startPrefix, startDepth}, "prefix search")
218202

219-
const responses = await Promise.all(promises);
203+
// Loop from the current depth until we reach the target depth
204+
for (let currentDepth = startDepth; currentDepth < targetDepth; currentDepth++) {
205+
logger.debug(`Discovering prefixes at depth ${currentDepth + 1}...`);
220206

221-
// Collect all the newly found "subfolders"
222-
const nextLevelPrefixes = responses.flatMap(
223-
response => response.CommonPrefixes?.map(p => p.Prefix!).filter(Boolean) || []
224-
);
207+
// Fetch all sub-prefixes for the current level in parallel
208+
const promises = prefixesToExplore.map(prefix => {
209+
const command = new ListObjectsV2Command({
210+
Bucket: AppServerConfig.S3.S3_BUCKET_NAME,
211+
Prefix: prefix,
212+
Delimiter: '/',
213+
});
214+
return s3Client.send(command);
215+
});
225216

226-
if (nextLevelPrefixes.length === 0) {
227-
logger.warn(`No further prefixes found at depth ${currentDepth + 1}. Stopping.`);
228-
prefixesToExplore = []; // Stop the loop
229-
break;
230-
}
217+
const responses = await Promise.all(promises);
231218

232-
prefixesToExplore = nextLevelPrefixes;
233-
logger.info({prefixesToExplore}, "prefixes in between");
219+
// Collect all the newly found "subfolders"
220+
const nextLevelPrefixes = responses.flatMap(
221+
response => response.CommonPrefixes?.map(p => p.Prefix!).filter(Boolean) || []
222+
);
223+
224+
if (nextLevelPrefixes.length === 0) {
225+
logger.warn(`No further prefixes found at depth ${currentDepth + 1}. Stopping.`);
226+
prefixesToExplore = []; // Stop the loop
227+
break;
234228
}
235229

236-
// The loop is finished, prefixesToExplore now holds our campaign-level prefixes
237-
const finalPrefixes = prefixesToExplore.sort();
230+
prefixesToExplore = nextLevelPrefixes;
231+
logger.debug({prefixesToExplore}, "prefixes in between");
232+
}
238233

239-
return {
240-
prefixes: finalPrefixes,
241-
count: finalPrefixes.length,
242-
};
234+
// The loop is finished, prefixesToExplore now holds our campaign-level prefixes
235+
const finalPrefixes = prefixesToExplore.sort();
243236

244-
} catch (error: any) {
245-
logger.error(`S3 Util: Error in findLeafPrefixes for prefix ${startPrefix}:`, error);
246-
throw error;
247-
}
237+
return {
238+
prefixes: finalPrefixes,
239+
count: finalPrefixes.length,
240+
};
248241
}
249242

250243
/**

0 commit comments

Comments
 (0)