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

Adds native spelunker support #414

Merged
merged 8 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .codespellrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[codespell]
# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = .git*,*.svg,package-lock.json,*.css,*-min.*,.codespellrc,*.bundle.*,*.map
skip = .git*,*.svg,package-lock.json,*.css,*-min.*,.codespellrc,*.bundle.*,*.map, *.js
check-hidden = true
# ignore-regex =
# ignore-regex =
# some favorite albeit unfortunate variable names
ignore-words-list = te
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ repos:
rev: v2.3.0
hooks:
- id: codespell

8 changes: 7 additions & 1 deletion neuvue_project/neuvue/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""

import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
Expand All @@ -24,6 +25,8 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

SITE_ID = 2

ALLOWED_HOSTS = [
".neuvue.io",
".elasticbeanstalk.com",
Expand Down Expand Up @@ -242,4 +245,7 @@

mimetypes.add_type("application/javascript", ".js", True)

STATIC_NG_FILES = os.listdir(os.path.join(BASE_DIR, "workspace", "static", "workspace"))
STATIC_NG_FILES = os.listdir(
os.path.join(BASE_DIR, "workspace", "static", "workspace")
) + os.listdir(os.path.join(BASE_DIR, "workspace", "static", "spelunker-workspace"))
# STATIC_NG_FILES = os.listdir(os.path.join(BASE_DIR, "workspace", "static", "workspace-spelunker"))
5 changes: 5 additions & 0 deletions neuvue_project/neuvue/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
path("tasks/", TaskView.as_view(), name="tasks"),
path("getting-started/", GettingStartedView.as_view(), name="getting-started"),
path("workspace/<str:namespace>", WorkspaceView.as_view(), name="workspace"),
path(
"spelunker-workspace/<str:namespace>",
WorkspaceView.as_view(),
name="spelunker-workspace",
),
path("admin/", admin.site.urls),
path("__debug__/", include("debug_toolbar.urls")),
path("accounts/", include("allauth.urls")),
Expand Down
Binary file modified neuvue_project/neuvueDB.sqlite3
Binary file not shown.
2 changes: 1 addition & 1 deletion neuvue_project/templates/about.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ <h2 class="pt-5 pb-3"> Collaborators </h2>
<h2 class="pt-5 pb-3"> Contact Us </h2>

<p>
We'd love to hear from you! Reach out at <a class="text-secondary-color-activated" href="mailto:info@neuvue.io">info@neuvue.io</a> with questions or if you are interested in collaborating with us.
We'd love to hear from you! Reach out at <a class="text-secondary-color-activated" href="mailto:info@neuvue.io">info@bossdb.org</a> with questions or if you are interested in collaborating with us.
Copy link
Member

Choose a reason for hiding this comment

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

Very glad you are changing this. Can you change the mailto: link as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

consider it done!

</p>

</div>
Expand Down
43 changes: 35 additions & 8 deletions neuvue_project/templates/inspect.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ <h3 class="text-white mb-3"> Inspect Task </h3>
</div>
</div>
</div>
<script type="text/javascript" src="{% static "workspace/main.bundle.js" %}"></script>
{% if ng_host == "spelunker" %}
<script type="module" src="{% static 'spelunker-workspace/562.1d0cecad9cc0725955f0.js' %}"></script><script type="module" src="{% static 'spelunker-workspace/993.ca23372ceee17151541a.js' %}"></script><script type="module" src="{% static 'spelunker-workspace/367.ad6071abcab399305157.js' %}"></script><script type="module" src="{% static 'spelunker-workspace/main.cb26101b553ed6b82856.js' %}"></script><link href="{% static 'spelunker-workspace/main.cb26101b553ed6b82856.css' %}" rel="stylesheet">
{% else %}
<script type="text/javascript" src="{% static 'workspace/main.bundle.js' %}"></script>
{% endif %}
<script type="text/javascript" src="{% static "js/utils.js" %}"></script>
{% endif%}
<style> .overlay-hidden { display:none; } </style>
Expand All @@ -111,18 +115,41 @@ <h3 class="text-white mb-3"> Inspect Task </h3>
<script type="text/javascript">

function getLink() {
viewer.postJsonState(true, undefined, true, function() {
let url_prefix = "https://neuroglancer.neuvue.io/?json_url="
copyToClipboard(url_prefix.concat(viewer.saver.savedUrl));
});
{% if ng_host == "neuvue" %}
viewer.postJsonState(true, undefined, true, function() {
let url_prefix = "https://neuroglancer.neuvue.io/?json_url="
copyToClipboard(url_prefix.concat(viewer.saver.savedUrl));
});
{% elif ng_host == "spelunker" %}
let url_prefix = "https://spelunker.cave-explorer.org/#!";
copyToClipboard(url_prefix.concat(JSON.stringify(viewer.state.toJSON())));
{% else %}
//pass
{% endif %}
}

// Function to attempt restoring the viewer state
function tryRestoreState() {
if (typeof viewer !== 'undefined') {
// Viewer is defined, attempt to restore the state
try {
var received_data = {{ ng_state|safe }};
viewer.state.restoreState(received_data);
console.log('Viewer state restored successfully.');
} catch (error) {
console.error('Error restoring viewer state:', error);
}
} else {
// Viewer is not yet defined, retry after a short delay
setTimeout(tryRestoreState, 100); // Retry every 100 milliseconds
}
}

// Neuroglancer State Load
$(document).ready(function() {
{% if ng_state %}
const state = {{ ng_state|safe }};
openSideMenu();
viewer.state.restoreState(state);
openSideMenu();
tryRestoreState();
{% endif %}
})

Expand Down
8 changes: 8 additions & 0 deletions neuvue_project/templates/tasks.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@
aria-expanded="false">
<span class="sr-only header-box-text">Toggle Dropdown</span>
</button>
{% if context.ng_host == "spelunker" %}
<span class="header-button header-outline rightBorderRounded" onclick="document.location='{% url 'spelunker-workspace' namespace=namespace %}'">
{% else %}
<span class="header-button header-outline rightBorderRounded" onclick="document.location='{% url 'workspace' namespace=namespace %}'">
{% endif %}
<button class="header-box-text"
onclick="triggerLoadingSpinner('{{namespace}}NgSpinner')">
<div id='{{namespace}}NgSpinner'>
Expand Down Expand Up @@ -77,7 +81,11 @@
</div>
</div>
{% else %}
{% if context.ng_host == "spelunker" %}
<div class="header-sizing header-outline rightBorderRounded" onclick="document.location='{% url 'spelunker-workspace' namespace=namespace %}'">
{% else %}
<div class="header-sizing header-outline rightBorderRounded" onclick="document.location='{% url 'workspace' namespace=namespace %}'">
{% endif %}
<button class="header-box-text"
onclick="triggerLoadingSpinner('{{namespace}}NgSpinner')">
<div id='{{namespace}}NgSpinner'>
Expand Down
74 changes: 63 additions & 11 deletions neuvue_project/templates/workspace.html
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,13 @@
<script src="https://cdn.jsdelivr.net/npm/@yaireo/tagify/dist/tagify.polyfills.min.js"></script>
<script type="text/javascript" src="{% static 'js/browser-interaction-time.umd.js' %}"></script>
<script type="text/javascript" src="{% static 'js/utils.js' %}"></script>

{% if ng_host == "spelunker" %}
<script type="module" src="{% static 'spelunker-workspace/562.1d0cecad9cc0725955f0.js' %}"></script><script type="module" src="{% static 'spelunker-workspace/993.ca23372ceee17151541a.js' %}"></script><script type="module" src="{% static 'spelunker-workspace/367.ad6071abcab399305157.js' %}"></script><script type="module" src="{% static 'spelunker-workspace/main.cb26101b553ed6b82856.js' %}"></script><link href="{% static 'spelunker-workspace/main.cb26101b553ed6b82856.css' %}" rel="stylesheet">
{% else %}
<script type="text/javascript" src="{% static 'workspace/main.bundle.js' %}"></script>
{% endif %}

<script type="text/javascript">

// initialize Tagify on the above input node reference
Expand All @@ -299,14 +305,23 @@
})

function getLink() {
viewer.postJsonState(true, undefined, true, function() {
let url_prefix = "https://neuroglancer.neuvue.io/?json_url="
copyToClipboard(url_prefix.concat(viewer.saver.savedUrl));
});
{% if ng_host == "neuvue" %}
viewer.postJsonState(true, undefined, true, function() {
let url_prefix = "https://neuroglancer.neuvue.io/?json_url="
copyToClipboard(url_prefix.concat(viewer.saver.savedUrl));
triggerToast("Copied link to clipboard");
});
{% elif ng_host == "spelunker" %}
let url_prefix = "https://spelunker.cave-explorer.org/#!";
copyToClipboard(url_prefix.concat(JSON.stringify(viewer.state.toJSON())));
triggerToast("Copied link to clipboard");
{% else %}
triggerToast("Copy Link unavailable on embedded Neuroglancer task type");
{% endif %}
}

function saveState() {
{% if not ng_url %}
{% if ng_host == "neuvue" %}
viewer.postJsonState(true, undefined, true, function() {
let state = viewer.saver.savedUrl;
let post_body = JSON.stringify({'task_id':"{{task_id}}", 'ng_state' : state});
Expand All @@ -320,7 +335,8 @@
});
});
{% else %}
triggerToast("Autosave disabled for embedded neuroglancer");
triggerToast("State saving disabled for non-native Neuroglancer");
removeLoadingSpinner("save_state", "Save State");
{% endif %}
}

Expand Down Expand Up @@ -373,6 +389,16 @@
}
}

function getOperationIdsFromSpelunker(){
for (let i = 0; i < viewer.layerManager.managedLayers.length; i++) {
const layer = viewer.layerManager.managedLayers[i].layer_;
if (layer.type === 'segmentation') {
const operationIds = layer.graphConnection.value.operationIds.toString();
return operationIds.split(',').map(Number);
}
}
}

function submitForm(value, form='#mainForm') {
window.removeEventListener('beforeunload', exitAlert);
let duration = Math.round(browserTimer.getTimeInMilliseconds()/1000);
Expand All @@ -383,8 +409,16 @@
$('<input>').attr('type', 'hidden').attr('name', 'duration').attr('value', duration).appendTo(form);
$(form).submit();

{% else %}
{% elif ng_host == "spelunker" %}
updateTrackedOperations(getOperationIdsFromSpelunker())
let state = JSON.stringify(viewer.state.toJSON());
$('<input>').attr('type', 'hidden').attr('name', 'ngState').attr('value', state).appendTo(form);
$('<input>').attr('type', 'hidden').attr('name', 'button').attr('value', value).appendTo(form);
$('<input>').attr('type', 'hidden').attr('name', 'duration').attr('value', duration).appendTo(form);

$(form).submit();

{% else %}
updateTrackedOperations(operation_ids)
viewer.postJsonState(true, undefined, true, function() {
let state = viewer.saver.savedUrl;
Expand Down Expand Up @@ -453,6 +487,23 @@
selected_button.classList.toggle("active");
}

// Function to attempt restoring the viewer state
function tryRestoreState() {
if (typeof viewer !== 'undefined') {
// Viewer is defined, attempt to restore the state
try {
var received_data = {{ ng_state|safe }};
viewer.state.restoreState(received_data);
console.log('Viewer state restored successfully.');
} catch (error) {
console.error('Error restoring viewer state:', error);
}
} else {
// Viewer is not yet defined, retry after a short delay
setTimeout(tryRestoreState, 100); // Retry every 100 milliseconds
}
}

// Button Submission
$(document).ready(function() {

Expand All @@ -468,13 +519,14 @@

// Specific to built-in NG
{% if not ng_url%}
var received_data = {{ ng_state|safe }};
viewer.state.restoreState(received_data);


tryRestoreState()
// Track operation IDs every three seconds
window.setInterval(function() {
{% if ng_host == "spelunker" %}
updateTrackedOperations(getOperationIdsFromSpelunker());
{% else %}
updateTrackedOperations(operation_ids);
{% endif %}
}, 3000);
{% endif %}

Expand Down
8 changes: 5 additions & 3 deletions neuvue_project/workspace/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# import the logging library
import logging

import warnings
logging.basicConfig(level=logging.DEBUG)
# Get an instance of a logger
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -111,8 +111,10 @@ def create_stats_table(pending_tasks, closed_tasks):
for namespace, namespace_df in df.groupby("namespace"):
n_tasks = len(namespace_df)
m = namespace_df[status].min()
average_event_time = (m + (namespace_df[status] - m)).mean().to_pydatetime()
changelog.append((average_event_time, n_tasks, status, namespace))
with warnings.catch_warnings():
warnings.simplefilter("ignore")
average_event_time = (m + (namespace_df[status] - m)).mean().to_pydatetime()
changelog.append((average_event_time, n_tasks, status, namespace))
Copy link
Member

Choose a reason for hiding this comment

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

Why are we ignoring warnings here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

a previous team member hannah gooden reported annoying warnings here
#382


# Sort changelogs by datetime
daily_changelog_items = sorted(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.11 on 2024-10-21 20:47

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("workspace", "0021_alter_namespace_ng_host"),
]

operations = [
migrations.AlterField(
model_name="namespace",
name="ng_host",
field=models.CharField(
choices=[
("neuvue", "NeuVue Legacy"),
("spelunker", "Spelunker Native"),
("https://neuroglancer.bossdb.io", "neuroglancer.bossdb.io"),
(
"https://spelunker.cave-explorer.org/",
"spelunker.cave-explorer.org",
),
],
default="neuvue",
max_length=100,
),
),
]
8 changes: 5 additions & 3 deletions neuvue_project/workspace/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ class NeuroglancerHost(models.TextChoices):
spelunker.cave-explorer.org -> embedded neuroglancer developed by AIBS.
"""

NEUVUE = "neuvue", _("NeuVue Built-in")
NEUVUE = "neuvue", _("NeuVue Legacy")
SPELUNKER = "spelunker", _("Spelunker Native")
BOSSDB = "https://neuroglancer.bossdb.io", _("neuroglancer.bossdb.io")
# CLIO = "https://clio-ng.janelia.org", _("clio-ng.janelia.org")
SPELUNKER = "https://spelunker.cave-explorer.org/", _("spelunker.cave-explorer.org")
SPELUNKER_URL = "https://spelunker.cave-explorer.org/", _(
"spelunker.cave-explorer.org"
)


class ForcedChoiceButtonGroup(models.Model):
Expand Down
14 changes: 12 additions & 2 deletions neuvue_project/workspace/neuroglancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@
ChainedStateBuilder,
)

from .models import Namespace, NeuroglancerLinkType, PcgChoices, ImageChoices
from .models import (
Namespace,
NeuroglancerLinkType,
PcgChoices,
ImageChoices,
NeuroglancerHost,
)


logging.basicConfig(level=logging.DEBUG)
Expand Down Expand Up @@ -273,7 +279,10 @@ def construct_proofreading_state(task_df, points, return_as="json"):


def construct_url_from_existing(state: str, ng_host: str):
return ng_host + "/#!" + state
if ng_host in [NeuroglancerHost.SPELUNKER, NeuroglancerHost.SPELUNKER_URL]:
return ng_host + "/#!middleauth+" + state
else:
return ng_host + "/#!" + state


@backoff.on_exception(backoff.expo, Exception, max_tries=3)
Expand All @@ -285,6 +294,7 @@ def get_from_state_server(url: str):
Returns:
(str): JSON String
"""
url = url.replace("middleauth+", "")
headers = {
"content-type": "application/json",
"Authorization": f"Bearer {os.environ['CAVECLIENT_TOKEN']}",
Expand Down
Loading
Loading