Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

For staging environment, include dynamic text for dandi download command #1810

Merged
merged 9 commits into from
Sep 20, 2024
10 changes: 9 additions & 1 deletion web/src/components/FileBrowser/FileUploadInstructions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
width="60%"
class="white--text pl-2 py-1 text-left"
>
<div>> dandi download https://dandiarchive.org/dandiset/{{ dandisetIdentifier }}/draft</div>
<div>> {{ downloadCommand }}</div>
<div>> cd {{ dandisetIdentifier }}</div>
<div>> dandi organize &lt;source_folder&gt; -f dry</div>
<div>> dandi organize &lt;source_folder&gt;</div>
Expand Down Expand Up @@ -53,4 +53,12 @@ import { useDandisetStore } from '@/stores/dandiset';

const store = useDandisetStore();
const dandisetIdentifier = computed(() => store.dandiset?.dandiset.identifier);

if (dandisetIdentifier.value === undefined) {
throw new Error('store.dandiset must be defined');
}

const downloadCommand = computed(() => {
return `dandi download ${window.location.origin}/dandiset/${dandisetIdentifier.value}/draft`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not really follow the discussion in the other thread. Could you summarize here whether this logic is correct for the upload instructions? Is there a reason not to use the same logic as in the download dialog below? Specifically, if a user downloads their dandiset via dandi download DANDI:123456/draft, will the subsequent dandi upload command get confused about where to upload?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DANDI CLI should still be OK -- as @yarikoptic mentioned that any short-hand prefix shouldn't break here, as "The dandi-cli is actually "too smart" to support a wide range of URLs and they do not have to be associated with a specific "short named" instance (if something fails -- it is a bug)"

I can test further if you'd like more confirmation

});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good first step since would allow both main and staging one but it is not a generic solution.

The dandi-cli is actually "too smart" to support a wide range of URLs and they do not have to be associated with a specific "short named" instance (if something fails -- it is a bug):

❯ dandi download --help
Usage: dandi download [OPTIONS] [URL]...

  Download files or entire folders from DANDI.

  Accepted resource identifier patterns:
   - DANDI:<dandiset id>[/<version>]
   - https://gui.dandiarchive.org/...
   - https://identifiers.org/DANDI:<dandiset id>[/<version id>] (<version id> cannot be 'draft')
   - https://<server>[/api]/[#/]dandiset/<dandiset id>[/<version>][/files[?location=<path>]]
   - https://*dandiarchive-org.netflify.app/...
   - https://<server>[/api]/dandisets/<dandiset id>[/versions[/<version>]]
   - https://<server>[/api]/assets/<asset id>[/download]
   - https://<server>[/api]/dandisets/<dandiset id>/versions/<version>/assets/<asset id>[/download]
   - https://<server>[/api]/dandisets/<dandiset id>/versions/<version>/assets/?path=<path>
   - https://<server>[/api]/dandisets/<dandiset id>/versions/<version>/assets/?glob=<glob>
   - dandi://<instance name>/<dandiset id>[@<version>][/<path>]
   - https://<server>/...

so here IMHO it just should use local server URL... chatgpt suggested some code like this

computed: {
  baseUrl() {
    const protocol = window.location.protocol;
    const host = window.location.hostname;
    const port = window.location.port ? `:${window.location.port}` : '';
    return `${protocol}//${host}${port}`;
  }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify though -- for example, a user on identifiers.org though does not see the DANDI CLI download command, so I'm curious why not confine it just to the two specific places where it might render.

I do agree that it could be more generic; however, I'd vote to keep it simple and confined in case an unknown edge case exists (e.g. windoww.location.protocol is tied to the browser, not Vue, etc., etc.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a few commits to clean up the PR and change the URL generation to something close to Yarik's ChatGPT code. Check it out and let me know what you think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify though -- for example, a user on identifiers.org though does not see the DANDI CLI download command, so I'm curious why not confine it just to the two specific places where it might render.

sorry, missed the question -- what "two specific places" you have in mind?

here the list of supported URLs organically grown to make it easier for a user to download, e.g. "just cut paste URL from the browser". identifiers.org is indeed niche use case, and would work only for the main instance unless we add some other identifiers to handle in addition to DANDI. Indeed it is not really needed and likely not really used, but if dandi is asked to download it and it knows how to parse it -- why not? ;)

</script>
26 changes: 14 additions & 12 deletions web/src/views/DandisetLandingView/DownloadDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,16 @@ import { computed, ref } from 'vue';
import { useDandisetStore } from '@/stores/dandiset';
import CopyText from '@/components/CopyText.vue';

function formatDownloadCommand(identifier: string, version: string): string {
if (version === 'draft') {
return `dandi download https://dandiarchive.org/dandiset/${identifier}/draft`;
}
if (!version) {
return `dandi download DANDI:${identifier}`;
}
return `dandi download DANDI:${identifier}/${version}`;
function downloadCommand(identifier: string, version: string): string {
// Use the special 'DANDI:' url prefix if appropriate.
const generalUrl = `${window.location.origin}/dandiset/${identifier}`;
const dandiUrl = `DANDI:${identifier}`;
const url = window.location.origin == 'https://dandiarchive.org' ? dandiUrl : generalUrl;

// Prepare a url suffix to specify a specific version (or not).
const versionPath = version ? `/${version}` : '';

return `dandi download ${url}${versionPath}`;
waxlamp marked this conversation as resolved.
Show resolved Hide resolved
}

const store = useDandisetStore();
Expand All @@ -147,19 +149,19 @@ const availableVersions = computed(
);

const defaultDownloadText = computed(
() => (identifier.value ? formatDownloadCommand(identifier.value, currentVersion.value) : ''),
() => (identifier.value ? downloadCommand(identifier.value, currentVersion.value) : ''),
);

const customDownloadText = computed(() => {
if (!identifier.value) {
return '';
}
if (selectedDownloadOption.value === 'draft') {
return formatDownloadCommand(identifier.value, 'draft');
return downloadCommand(identifier.value, 'draft');
} if (selectedDownloadOption.value === 'latest') {
return formatDownloadCommand(identifier.value, '');
return downloadCommand(identifier.value, '');
} if (selectedDownloadOption.value === 'other') {
return formatDownloadCommand(
return downloadCommand(
identifier.value,
availableVersions.value[selectedVersion.value].version,
);
Expand Down