Skip to content
Open
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
85 changes: 71 additions & 14 deletions lenses/templates/lenses/lens_list_query.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,84 @@
</div>

<script nonce="{{request.csp_nonce}}">
// Ids of every lens matching the current query, across all pages.
// null = "select all" is not active (only the visible checkboxes count).
var allPagesIds = null;
var toggleBtn = document.getElementById("toggle_all");

var setVisibleChecked = function(checked) {
var checkboxes = document.getElementsByName('ids');
for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = checked; }
};

var clearAllPages = function() {
allPagesIds = null;
toggleBtn.setAttribute('value', 'select');
toggleBtn.textContent = 'Toggle select all';
};

var toggle = function(e) {
checkboxes = document.getElementsByName('ids');
var new_status = e.currentTarget.getAttribute('value');
if (new_status == "select") {
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = true;
}
e.currentTarget.setAttribute('value', 'unselect');
var btn = e.currentTarget;
if (btn.getAttribute('value') == "select") {
// Activate "select all": fetch every matching id across all pages.
var form = document.getElementById('lens-query');
btn.disabled = true;
fetch("{% url 'lenses:lens-query-all-ids' %}", {
method: 'POST',
body: new FormData(form),
headers: {'X-Requested-With': 'XMLHttpRequest'}
})
.then(function(r) { if (!r.ok) { throw new Error('bad status'); } return r.json(); })
.then(function(d) {
allPagesIds = d.ids || [];
setVisibleChecked(true);
btn.setAttribute('value', 'unselect');
btn.textContent = 'Unselect all (' + allPagesIds.length + ' across all pages)';
btn.disabled = false;
})
.catch(function() {
btn.disabled = false;
alert('Could not select all results. Please try again.');
});
} else {
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].checked = false;
}
e.currentTarget.setAttribute('value', 'select');
setVisibleChecked(false);
clearAllPages();
}
}
document.getElementById("toggle_all").addEventListener("click", toggle, true);
};
toggleBtn.addEventListener("click", toggle, true);

// If the user hand-edits any checkbox, drop "select all" mode so the
// selection reflects exactly what is checked.
document.getElementById("exe_summary").addEventListener("change", function(e) {
if (allPagesIds !== null && e.target && e.target.name === 'ids') {
clearAllPages();
}
}, false);

// Before submitting, if "select all" is active, add a hidden ids input for
// every off-page lens so the action operates on the whole result set.
var injectAllPageIds = function() {
var form = document.getElementById('ids-form');
var prev = form.querySelectorAll('input.all-page-id');
for (var i = 0; i < prev.length; i++) { prev[i].remove(); }
if (allPagesIds === null) { return; }
var visible = {};
var boxes = document.querySelectorAll('#exe_summary input[name="ids"]');
for (var j = 0; j < boxes.length; j++) { visible[boxes[j].value] = true; }
for (var k = 0; k < allPagesIds.length; k++) {
var id = String(allPagesIds[k]);
if (!visible[id]) {
var inp = document.createElement('input');
inp.type = 'hidden'; inp.name = 'ids'; inp.value = id; inp.className = 'all-page-id';
form.appendChild(inp);
}
}
};
document.getElementById("ids-form").addEventListener("submit", injectAllPageIds, false);

var min_checked = function(e) {
var checked_boxes = document.querySelectorAll('#exe_summary input[type="checkbox"]:checked');
if (checked_boxes.length == 0) {
if (checked_boxes.length == 0 && allPagesIds === null) {
//alert('You must select at least one lens!');
e.preventDefault();
}
Expand Down
5 changes: 5 additions & 0 deletions lenses/templates/lenses/lens_query.html
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,11 @@ <h1 class="jb-heading-1">Query Lenses</h1>
<img src="{% static 'icons/arrow-down-circle-fill.svg' %}">
Export .json
</button>

<button type="submit" form="ids-form" id="visualise" class="jb-submit-button-1" formaction="{% url 'sled_visualise:lens-visualise' %}" formtarget="_blank">
<img src="{% static 'icons/eye-fill.svg' %}">
Visualise
</button>
</div>

</div>
Expand Down
1 change: 1 addition & 0 deletions lenses/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
path('',TemplateView.as_view(template_name='lenses/lens_index.html'), name='lens-index'),
path('export/',views.ExportToCSV.as_view(),name='export-csv'),
path('query/',views.LensQueryView.as_view(),name='lens-query'),
path('query-all-ids/',views.query_all_ids,name='lens-query-all-ids'),
path('add/',views.LensAddView.as_view(),name='lens-add'),
path('update/',views.LensUpdateView.as_view(),name='lens-update'),
path('update-modal/<int:pk>',views.LensUpdateModalView.as_view(),name='lens-update-modal'),
Expand Down
29 changes: 25 additions & 4 deletions lenses/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -973,8 +973,6 @@ def get(self, request, *args, **kwargs):
return TemplateResponse(request,'simple_message.html',context={'message':'You are accessing this page in an unauthorized way.'})




# View for lens queries
@method_decorator(login_required,name='dispatch')
class LensQueryView(TemplateView):
Expand Down Expand Up @@ -1054,9 +1052,32 @@ def post(self, request, *args, **kwargs):
context = self.my_response(merged_request,request.user)

return self.render_to_response(context)




@login_required
def query_all_ids(request):
"""Return the ids of every accessible lens matching the posted query form,
across all pages. Used by the 'select all' button on the query page so that
actions can operate on the entire result set rather than the current page."""
if request.method != 'POST':
return JsonResponse({'ids': [], 'count': 0, 'error': 'POST required'}, status=400)
user = request.user
data = request.POST
lens_form = forms.LensQueryForm(data, prefix="lens")
redshift_form = forms.RedshiftQueryForm(data, prefix="redshift")
imaging_form = forms.ImagingQueryForm(data, prefix="imaging")
spectrum_form = forms.SpectrumQueryForm(data, prefix="spectrum")
catalogue_form = forms.CatalogueQueryForm(data, prefix="catalogue")
management_form = forms.ManagementQueryForm(data, prefix="management", user=user)
all_forms = [lens_form, redshift_form, imaging_form, spectrum_form, catalogue_form, management_form]
if not all(f.is_valid() for f in all_forms):
return JsonResponse({'ids': [], 'count': 0, 'error': 'Invalid query parameters'}, status=400)
qset = query_utils.combined_query(lens_form.cleaned_data, redshift_form.cleaned_data, imaging_form.cleaned_data,
spectrum_form.cleaned_data, catalogue_form.cleaned_data, management_form.cleaned_data, user)
ids = list(qset.values_list('id', flat=True).distinct())
return JsonResponse({'ids': ids, 'count': len(ids)})


#=============================================================================================================================
### END: Non-modal views
#=============================================================================================================================
Expand Down
1 change: 1 addition & 0 deletions mysite/settings_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
'sled_limits',
'sled_guide',
'sled_lens_models',
'sled_visualise',
'lenses.apps.LensesConfig',
'sled_groups.apps.GroupsConfig',
'sled_home.apps.HomeConfig',
Expand Down
1 change: 1 addition & 0 deletions mysite/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
path('captcha/', include('captcha.urls')),
path('sled_guide/', include('sled_guide.urls'),name='sled_guide'),
path('sled_lens_models/', include('sled_lens_models.urls'), name='sled_lens_models'),
path('sled_visualise/', include('sled_visualise.urls'), name='sled_visualise'),
# path('sled_core/', include('sled_core.urls'), name='sled_core'),
]

Expand Down
5 changes: 5 additions & 0 deletions run_dev_server/settings_development.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@

CELERY_BROKER_URL = 'redis://redis:6379'
CELERY_RESULT_BACKEND = 'redis://redis:6379'

# django-axes: relax login lockout for local development
AXES_COOLOFF_TIME = 0.25 # auto-unlock after 15 minutes
AXES_FAILURE_LIMIT = 10 # allow more attempts before locking
AXES_RESET_ON_SUCCESS = True # clear the counter on a successful login
Empty file added sled_visualise/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions sled_visualise/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# No models to register.
6 changes: 6 additions & 0 deletions sled_visualise/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class SledVisualiseConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'sled_visualise'
Empty file.
3 changes: 3 additions & 0 deletions sled_visualise/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# The visualise app has no models of its own; it reads from the lenses app.
Loading