{% extends "base.html" %} {% block content %} {% set box = "border:1px solid var(--border, #ddd); border-radius:6px; padding:0.9rem 1rem; margin:1rem 0;" %}

FAIRDOM-SEEK

Publish to a SEEK instance in two stages: set up its data model from a profile, then push a dataset into that model. Both talk to SEEK over its API, using the URL and key from the Plugins page. You can also export a file and import it through SEEK yourself.

{% if action_error %}
{{ action_error }}
{% endif %} {% if provision_result %}
Model set up{% if provisioned_profile %} ({{ provisioned_profile }}){% endif %}. Created {{ provision_result.created|length }}, reused {{ provision_result.reused|length }}. {% if provision_result.created %}
Created: {{ provision_result.created|join(", ") }}
{% endif %} {% if provision_result.reused %}
Reused: {{ provision_result.reused|join(", ") }}
{% endif %} {% if provision_result.errors %}
Errors: {{ provision_result.errors|join("; ") }}
{% endif %}
{% endif %} {% if sync_result %} {% set not_uploaded = sync_result.skipped|length + sync_result.errors|length %}
Synced {{ sync_result.created_count }} resource{{ "" if sync_result.created_count == 1 else "s" }} to SEEK{% if not_uploaded %}, but {{ not_uploaded }} entit{{ "y was" if not_uploaded == 1 else "ies were" }} not uploaded{% endif %}.
{{ sync_result.investigations|length }} investigation(s), {{ sync_result.studies|length }} study(ies), {{ sync_result.assays|length }} assay(s), {{ sync_result.samples|length }} sample(s).
{% if not_uploaded %}
{{ not_uploaded }} of your entit{{ "y" if not_uploaded == 1 else "ies" }} did not reach SEEK — see below. Your SEEK copy is incomplete.
{% endif %} {# Many nodes of one entity type give the same reason; show each reason once with a count, rather than repeating it per node. #} {% if sync_result.skipped %}
Skipped: {% set counts = {} %} {% for node_id, reason in sync_result.skipped %}{% set _ = counts.update({reason: counts.get(reason, 0) + 1}) %}{% endfor %} {% for reason, n in counts.items() %}{{ reason }}{% if n > 1 %} (×{{ n }}){% endif %}{% if not loop.last %}; {% endif %}{% endfor %}
{% endif %} {% if sync_result.errors %}
Errors: {% set ecounts = {} %} {% for node_id, reason in sync_result.errors %}{% set _ = ecounts.update({reason: ecounts.get(reason, 0) + 1}) %}{% endfor %} {% for reason, n in ecounts.items() %}{{ reason }}{% if n > 1 %} (×{{ n }}){% endif %}{% if not loop.last %}; {% endif %}{% endfor %}
{% endif %}
{% endif %} {% if not api_key_configured %}
Set your SEEK URL and API key on the Plugins page. The buttons below stay disabled until you do, because provisioning and syncing both write to SEEK.
{% endif %} {# ---- 1: set up the model. One profile choice drives both actions here. ---- #}

1 · Set up the model in SEEK

Choose a profile. Set up Sample Types creates it in SEEK as Sample Types, with a Controlled Vocabulary for each field that has a fixed list of allowed values. Safe to run again — it reuses whatever already exists. To sync a dataset later, set up the profile it was built on.

{% include "seek/_project_select.html" %}
{# Repopulate the version list, and keep the Extended Metadata form's hidden profile/version in step, when the profile choice changes. #} {# Browsable projection of the selected profile: what Sample Types and Extended Metadata provisioning will create, before any upload. #}
What this will create in SEEK — browse before uploading
{% include "seek/_preview.html" %}
Extended Metadata — the custom fields on Investigation, Study and Assay. SEEK only lets an administrator add these, and not over the API, so download them as a file and pass it to your admin.
{# Mirrors the profile and version chosen above, so this cannot disagree with what gets provisioned. #} for the profile and version selected above

Then, as a SEEK admin: Server administration → Extended Metadata Types → New, pick the FAIR DS TTL tab, upload the file, and confirm on the preview.

{# ---- 2: sync the data ---- #}

2 · Push a dataset into SEEK

{% if exportable_count %}

Sends the dataset {{ dataset_name or "(unsaved)" }} (built on {{ profile }}) — {% for etype, n in entity_counts %}{{ etype }} ×{{ n }}{% if not loop.last %}, {% endif %}{% endfor %} — into SEEK as Investigations, Studies, Assays and Samples, matched to the Sample Types set up in step 1.

{% include "seek/_project_select.html" %}
{% else %}

No dataset is loaded. Step 1 needs only a profile, but this step acts on an open dataset — load or build one first.

{% endif %}
{# ---- 3: file export ---- #}

3 · Or export a file to import yourself

Download the dataset as FAIR Data Station RDF and import it through SEEK's own Import from FAIR Data Station, which handles the full ISA structure and Extended Metadata in one step. No API key needed.

{% if exportable_count %} Download SEEK ISA RDF (.ttl) → {% else %} {% endif %} {% if seek_url %} · your instance: {% if seek_url.startswith(("http://", "https://")) %}{{ seek_url }}{% else %}{{ seek_url }}{% endif %} {% endif %}

Note: SEEK places entities by where they sit in the ISA hierarchy, not by their name. With the ISA profile (which has no ObservationUnit level) entities below Study import one level high; Investigation and Study round-trip cleanly.

{% endblock %}