From f3618edd66ea696526973410a5bc507de04356f5 Mon Sep 17 00:00:00 2001 From: Tanner Prestegard <tanner.prestegard@ligo.org> Date: Fri, 5 Oct 2018 15:12:11 -0500 Subject: [PATCH] Fixes and updates to search app * Consolidated *all* of the search stuff into the search app (previously the flexigrid stuff was in events/superevents) * Completely removed the old events-only search and latest views, templates, etc. * Removed some old unit tests which tested the events-only search --- docs/admin_docs/source/miscellaneous.rst | 2 +- gracedb/events/tests/test_perms.py | 42 ---- gracedb/events/urls.py | 5 - gracedb/events/view_utils.py | 130 +---------- gracedb/events/views.py | 261 +--------------------- gracedb/search/utils.py | 227 ++++++++++++++++++- gracedb/search/views.py | 23 +- gracedb/superevents/search_flex.py | 109 --------- gracedb/templates/gracedb/cbc_report.html | 2 +- gracedb/templates/gracedb/event_list.html | 137 ------------ gracedb/templates/gracedb/query.html | 73 ------ gracedb/templates/search/query.html | 56 +++-- 12 files changed, 261 insertions(+), 806 deletions(-) delete mode 100644 gracedb/superevents/search_flex.py delete mode 100644 gracedb/templates/gracedb/event_list.html delete mode 100644 gracedb/templates/gracedb/query.html diff --git a/docs/admin_docs/source/miscellaneous.rst b/docs/admin_docs/source/miscellaneous.rst index 77b81cfac..d38babc08 100644 --- a/docs/admin_docs/source/miscellaneous.rst +++ b/docs/admin_docs/source/miscellaneous.rst @@ -107,7 +107,7 @@ A good starting point is to search the GraceDB server code for "L1" to see where Specifics (assume X1 is the IFO code): -1. Add X1OPS, X1OK, X1NO labels, update ``gracedb/templates/gracedb/event_detail_script.js`` with description, and update ``gracedb/templates/gracedb/query_help_frag.html`` +1. Add X1OPS, X1OK, X1NO labels, update ``gracedb/templates/gracedb/event_detail_script.js`` with description, and update ``gracedb/templates/search/query_help_frag.html`` 2. Add to instruments in ``gracedb/events/buildVOEvent.py`` 3. Update ifoList in ``gracedb/events/query.py`` 4. Add entry to ``CONTROL_ROOM_IPS`` in ``gracedb/config/settings/base.py`` diff --git a/gracedb/events/tests/test_perms.py b/gracedb/events/tests/test_perms.py index 9e3f40ff9..8aca82db2 100644 --- a/gracedb/events/tests/test_perms.py +++ b/gracedb/events/tests/test_perms.py @@ -274,38 +274,6 @@ class TestPerms(TestCase): else: self.assertEqual(response.status_code, 403) - def test_internal_search(self): - """Test search by LIGO users""" - query = '{group} {search}'.format(group=TEST_NAMES['group'], - search=TEST_NAMES['search']) - res = self.search_helper(query, self.internal_user) - - # You should get all three events. - self.assertEqual(res['records'], 3) - - def test_lvem_search(self): - """Test search by LV-EM users""" - query = '{group} {search}'.format(group=TEST_NAMES['group'], - search=TEST_NAMES['search']) - res = self.search_helper(query, self.lvem_user) - - # You should get two events ... - self.assertEqual(res['records'],2) - # ... and the missing event should be the internal one. - ids = [r['id'] for r in res['rows']] - self.assertTrue(self.internal_event.id not in ids) - - def test_public_search(self): - """Test search by public users""" - query = '{group} {search}'.format(group=TEST_NAMES['group'], - search=TEST_NAMES['search']) - res = self.search_helper(query, self.public_user) - - # You should only get one event ... - self.assertEqual(res['records'], 1) - # ... and that event should be the public one. - self.assertEqual(res['rows'][0]['id'], self.public_event.id) - #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # Tests of event annotation @@ -490,16 +458,6 @@ class TestPerms(TestCase): response = request_event_creation(self.client, user, test=True) self.assertEqual(response.status_code, 302) - @override_settings(GRACEDB_DATA_DIR=TMP_DATA_DIR) - def test_search_on_new_event(self): - """Test the availability of a newly created event via search""" - response = request_event_creation(self.client, self.pipeline_user) - redirect_url = response['Location'] - graceid = redirect_url.split('/')[-1] - res = self.search_helper(graceid, self.internal_user) - # You should get exactly one record. - self.assertEqual(res['records'],1) - # Actually, you can only replace an event that you yourself created. # Thus, not sure if we really need this. # def test_event_replacement(self): diff --git a/gracedb/events/urls.py b/gracedb/events/urls.py index fedad1a4a..18c2016ea 100644 --- a/gracedb/events/urls.py +++ b/gracedb/events/urls.py @@ -34,11 +34,6 @@ urlpatterns = [ url(r'^(?P<graceid>[GEHMT]\d+)/log/(?P<num>\d+)/tag/(?P<tagname>.*)$', views.taglogentry, name="taglogentry"), - # old event-only searches - url(r'^latest/$', views.latest, name="eventlatest"), - url(r'^search/(?P<format>(json|flex))?$', views.search, - name="eventsearch"), - # Legacy URLs url(r'^view/(?P<graceid>[GEHMT]\d+)', views.view, name="legacyview"), url(r'^(?P<graceid>[GEHMT]\d+)$', views.view, name="legacyview2"), diff --git a/gracedb/events/view_utils.py b/gracedb/events/view_utils.py index fd994a31b..e402d83a8 100644 --- a/gracedb/events/view_utils.py +++ b/gracedb/events/view_utils.py @@ -18,17 +18,11 @@ import os from django.conf import settings from .templatetags.scientific import scientific +from .templatetags.timeutil import timeSelections import logging logger = logging.getLogger(__name__) -# XXX This should be configurable / moddable or something -MAX_QUERY_RESULTS = 1000 - -# The maximum number of rows to be returned by flexigridResponse -# in the event that the user asks for all of them. -MAX_FLEXI_ROWS = 250 - GRACEDB_DATA_DIR = settings.GRACEDB_DATA_DIR import json @@ -712,128 +706,6 @@ def sanitize_html(data): s = serializer.htmlserializer.HTMLSerializer(omit_optional_tags=False) return "".join(s.serialize(stream)) -from .templatetags.timeutil import timeSelections - -def jqgridResponse(request, objects): - # "GET /data?_search=false&nd=1266350238476&rows=10&page=1&sidx=invid&sord=asc HTTP/1.1" - pass - -def flexigridResponse(request, objects): - response = HttpResponse(content_type='application/json') - - #sortname = request.POST.get('sortname', None) - #sortorder = request.POST.get('sortorder', 'desc') - #page = int(request.POST.get('page', 1)) - #rp = int(request.POST.get('rp', 10)) - - sortname = request.GET.get('sidx', None) # get index row - i.e. user click to sort - sortorder = request.GET.get('sord', 'desc') # get the direction - page = int(request.GET.get('page', 1)) # get the requested page - rp = int(request.GET.get('rows', 10)) # get how many rows we want to have into the grid - - get_neighbors = request.GET.get('get_neighbors', False) # whether to retrieve the neighbors - if get_neighbors in ['True', 'true', 'T', 't', 1, '1']: - get_neighbors = True - else: - get_neighbors = False - - # select related objects to reduce the number of queries. - objects = objects.select_related('group', 'pipeline', 'search', 'submitter') - - if sortname: - if sortorder == "desc": - sortname = "-" + sortname - objects = objects.order_by(sortname) - - total = objects.count() - rows = [] - if rp > -1: - start = (page-1) * rp - - if total: - total_pages = (total / rp) + 1 - else: - total_pages = 0 - - if page > total_pages: - page = total_pages - - end = start+rp - else: - start = 0 - total_pages = 1 - page = 1 - end = total-1 - - if total > MAX_FLEXI_ROWS: - return HttpResponseBadRequest("Too many rows! Please try loading a smaller number.") - - for object in objects[start:end]: - event_times = timeSelections(object.gpstime) - created_times = timeSelections(object.created) - if object.search: - search_name = object.search.name - else: - search_name = '' - - display_far = scientific(object.far) - if object.far and is_external(request.user): - if object.far < settings.VOEVENT_FAR_FLOOR: - display_far = "< %s" % scientific(settings.VOEVENT_FAR_FLOOR) - - cell_values = [ '<a href="%s">%s</a>' % - (django_reverse("view", args=[object.graceid()]), object.graceid()), - #Labels - " ".join(["""<span onmouseover="tooltip.show(tooltiptext('%s', '%s', '%s'));" onmouseout="tooltip.hide();" style="color: %s"> %s </span>""" % (label.label.name, label.creator.username, label.created, label.label.defaultColor, label.label.name) - for label in object.labelling_set.all()]), - object.group.name, - object.pipeline.name, - search_name, - - event_times.get('gps',""), - #event_times['utc'], - - object.instruments, - - #scientific(display_far), - display_far, - - '<a href="%s">Data</a>' % object.weburl(), - - #created_times['gps'], - created_times.get('utc',""), - - "%s %s" % (object.submitter.first_name, object.submitter.last_name) - - ] - - if get_neighbors: - # Links to neighbors - cell_values.insert(2, ', '.join([ '<a href="%s">%s</a>' % - (django_reverse("view", args=[n.graceid()]), n.graceid()) for n in object.neighbors()])) - - rows.append( - { 'id' : object.id, - 'cell': cell_values, - } - ) - d = { - 'page': page, - 'total': total_pages, - 'records': total, - 'rows': rows, - } - try: - msg = json.dumps(d) - except Exception: - # XXX Not right not right not right. - msg = "{}" - response['Content-length'] = len(msg) - response.write(msg) - - #query = request.POST['query'] - - return response def get_file(event, filename="event.log"): logfilename = os.path.join(event.datadir, filename) diff --git a/gracedb/events/views.py b/gracedb/events/views.py index fc5254bd2..0781639d5 100644 --- a/gracedb/events/views.py +++ b/gracedb/events/views.py @@ -25,8 +25,7 @@ from .view_logic import get_lvem_perm_status from .view_logic import create_eel from .view_logic import create_emobservation from .view_logic import create_label, delete_label -from .view_utils import assembleLigoLw, get_file -from .view_utils import flexigridResponse, jqgridResponse +from .view_utils import get_file from .view_utils import get_recent_events_string from .view_utils import eventLogToDict from .view_utils import signoffToDict @@ -476,264 +475,6 @@ def view(request, event): return render(request, templates, context=context) -def search(request, format=""): - if not request.user or not request.user.is_authenticated(): - return HttpResponseForbidden("Forbidden") - # XXX DO NOT HARDCODE THIS - # Also, user should be notified if their result hits this limit. - limit = MAX_QUERY_RESULTS - form2 = None - - if request.method == "GET" and "query" not in request.GET: - form = SimpleSearchForm() - form2 = EventSearchForm() - else: - if request.method == "POST" and 'query' not in request.POST: - return oldsearch(request) - if request.method == "GET": - form = SimpleSearchForm(request.GET) - rawquery = request.GET['query'] - else: - form = SimpleSearchForm(request.POST) - rawquery = request.POST['query'] - - # If the user is external, we must check to make sure that any query on FAR - # value is within the safe range. - if is_external(request.user): - try: - check_query_far_range(parseQuery(rawquery)) - except BadFARRange: - msg = 'FAR query out of range, upper limit must be below %s' % settings.VOEVENT_FAR_FLOOR - return HttpResponseBadRequest(msg) - except ParseException: - # If the user's query throws a parse exception, it is safe to - # proceed and show the error message in the search results - # template (red star) - pass - except Exception, e: - return HttpResponseServerError(str(e)) - if form.is_valid(): - objects = form.cleaned_data['query'] - get_neighbors = form.cleaned_data['get_neighbors'] - - # Filter objects according to user permissions. - # NOTE: This is bad. Creates a complete list of pks to which the user has - # access for a given content type. Then filters according to this list. - #objects = guardian.shortcuts.get_objects_for_user(request.user, 'events.view_event', objects) - - # Instead, use the alternative that uses perm info residing on the event itself. - objects = filter_events_for_user(objects, request.user, 'view') - - if format == "json": - return HttpResponse("Not Implemented") - elif format == "flex": - # Flexigrid request. - return flexigridResponse(request, objects) - elif format == "jqgrid": - return jqgridResponse(request, objects) - elif 'ligolw' in request.POST or 'ligolw' in request.GET: - - from glue.ligolw import utils - if objects.count() > 1000: - # XXX Make this -- Better. - return HttpResponse("Sorry -- no more than 1000 events currently allowed.") - - try: - xmldoc = assembleLigoLw(objects) - except IOError: - msg = "At least one of the query results has no associated coinc.xml file." - msg += " LigoLw tables are available for queries that return only coinc inspiral events." - msg += " Please try your query again." - return HttpResponseBadRequest(msg) - except Exception, e: - msg = "An error occured while trying to compile LigoLw results: %s" % str(e) - return HttpResponseServerError(msg) - - response = HttpResponse(content_type='application/xml') - response['Content-Disposition'] = 'attachment; filename=gracedb-query.xml' - utils.write_fileobj(xmldoc, response) - return response - - else: - # XXX FIXME Use this instead. - # count = objects.count() - if objects.count() == 1: - title = "Query Results. %s event" % objects.count() - else: - title = "Query Results. %s events" % objects.count() - - context = { - 'title' : title, - 'form' : form, - 'formAction' : reverse(search), - 'maxCount' : limit, - 'rawquery' : rawquery, - 'get_neighbors' : get_neighbors, - } - return render(request, 'gracedb/event_list.html', - context=context) - - return render(request, 'gracedb/query.html', context={'form': form, - 'form2': form2}) - -def oldsearch(request): - assert request.user - if request.method == 'GET': - form = EventSearchForm() - else: - form = EventSearchForm(request.POST) - if form.is_valid(): - start = form.cleaned_data['graceidStart'] - end = form.cleaned_data['graceidEnd'] - submitter = form.cleaned_data['submitter'] - groupname = form.cleaned_data['group'] - pipelinename = form.cleaned_data['pipeline'] - searchname = form.cleaned_data['search'] - labels = form.cleaned_data['labels'] - gpsStart = form.cleaned_data['gpsStart'] - gpsEnd = form.cleaned_data['gpsEnd'] - get_neighbors = form.cleaned_data['get_neighbors'] - offline = form.cleaned_data['offline'] - - textQuery = [] - - # XXX: this whole thing could be redone by defining Q objects - # and doing a single query. I think that might simplify the - # database queries. (Tanner) - - if not groupname: - # don't show test events unless explicitly requested - # Scales? Or should we find test group and do group= ? - objects = Event.objects.exclude(group__name='Test') - else: - objects = Event.objects.all() - - if start: - if start[0] in 'GEHMT': - objects = objects.filter(id__gte=int(start[1:])) - else: - return HttpResponseBadRequest("Invalid GraceID") - if end: - if end[0] in 'GEHMT': - objects = objects.filter(id__lte=int(end[1:])) - else: - return HttpResponseBadRequest("Invalid GraceID") - - if start and end: - textQuery.append("gid: %s..%s" % (start, end)) - elif start or end: - textQuery.append("gid: %s" % (start or end)) - - if gpsStart != None or gpsEnd != None : - if gpsStart == gpsEnd: - objects = objects.filter(gpstime=gpsStart) - textQuery.append("gpstime: %s" % gpsStart) - else: - if gpsStart != None: - objects = objects.filter(gpstime__gte=gpsStart) - if gpsEnd != None: - objects = objects.filter(gpstime__lte=gpsEnd) - if gpsStart and gpsEnd: - textQuery.append("gpstime: %s .. %s" % (gpsStart, gpsEnd)) - elif gpsStart: - textQuery.append("gpstime: %s..2000000000" % gpsStart) - else: - textQuery.append("gpstime: 0..%s" % gpsEnd) - - if submitter: - objects = objects.filter(submitter=submitter) - textQuery.append('submitter: "%s"' % submitter.username) - if groupname: - group = Group.objects.filter(name=groupname)[0] - objects = objects.filter(group=group) - textQuery.append("group: %s" % group.name) - if pipelinename: - pipeline = Pipeline.objects.get(name=pipelinename) - objects = objects.filter(pipeline=pipeline) - textQuery.append("pipeline: %s" % pipeline.name) - if searchname: - search = Search.objects.get(name=searchname) - objects = objects.filter(search=search) - textQuery.append("search: %s" % search.name) - - if labels: - objects = objects.filter(labels__in=labels) - textQuery.append("label: %s" % " ".join([l.name for l in labels])) - - # Need this because events with multiple labels can appear multiple times! - objects = objects.distinct() - - # Offline - if offline is not None: - objects = objects.filter(offline=offline) - textQuery.append("offline: \"{0}\"".format(offline)) - - # Filter for user. - objects = filter_events_for_user(objects, request.user, 'view') - - if objects.count() == 1: - title = "Query Results. %s event" % objects.count() - else: - title = "Query Results. %s events" % objects.count() - - textQuery = " ".join(textQuery) - simple_form = SimpleSearchForm({'query': textQuery}) - context = { - 'title' : title, - 'form' : simple_form, - 'maxCount' : MAX_QUERY_RESULTS, - 'rawquery' : textQuery, - 'get_neighbors' : get_neighbors, - } - return render(request, 'gracedb/event_list.html', context=context) - - return render(request, 'gracedb/query.html', context={'form': form}) - -def latest(request): - if not request.user or not request.user.is_authenticated(): - return HttpResponseForbidden("Forbidden") - context = {} - - if request.method == "GET": - form = SimpleSearchForm(request.GET) - else: - form = SimpleSearchForm(request.POST) - - template = 'gracedb/latest.html' - context['form'] = form - rawquery = request.GET.get('query') or request.POST.get('query') or "" - context['rawquery'] = rawquery - - # If the user is external, we must check to make sure that any query on FAR - # value is within the safe range. - if is_external(request.user): - try: - check_query_far_range(parseQuery(rawquery)) - except BadFARRange: - msg = 'FAR query out of range, upper limit must be below %s' % settings.VOEVENT_FAR_FLOOR - return HttpResponseBadRequest(msg) - except ParseException: - # If the user's query throws a parse exception, it is safe to - # proceed and show the error message in the search results template - # (red star) - pass - - if form.is_valid(): - # XXX This makes the requests much faster for internal users. - if is_external(request.user): - objects = form.cleaned_data['query'] - objects = filter_events_for_user(objects, request.user, 'view')[0:50] - else: - objects = form.cleaned_data['query'][:50] - context['objects'] = objects - context['error'] = False - else: - context['error'] = True - - context['far_floor'] = settings.VOEVENT_FAR_FLOOR - context['user_is_external'] = is_external(request.user) - - return render(request, template, context=context) #----------------------------------------------------------------------------------- # For tags. A new view function. We need this because the API one would want users diff --git a/gracedb/search/utils.py b/gracedb/search/utils.py index fad5cd41a..d9c85ce53 100644 --- a/gracedb/search/utils.py +++ b/gracedb/search/utils.py @@ -1,11 +1,28 @@ -from django.http import HttpResponse, HttpResponseServerError, \ - HttpResponseBadRequest +import json +import logging +import os -from events.view_utils import assembleLigoLw +from django.conf import settings +from django.http import HttpResponse, HttpResponseBadRequest, \ + HttpResponseServerError +from django.urls import reverse as django_reverse from glue.ligolw import utils +from events.permission_utils import is_external +from events.templatetags.scientific import scientific +from events.templatetags.timeutil import timeSelections +from events.view_utils import assembleLigoLw + +# Set up logger +logger = logging.getLogger(__name__) +# The maximum number of rows to be returned by flexigridResponse +# in the event that the user asks for all of them. +MAX_FLEXI_ROWS = 250 + + +# Limit on number of LIGOLW results RESULTS_LIMIT = 1000 @@ -35,4 +52,206 @@ def get_search_results_as_ligolw(objects): response = HttpResponse(content_type='application/xml') response['Content-Disposition'] = 'attachment; filename=gracedb-query.xml' utils.write_fileobj(xmldoc, response) - return response + return response + + +def superevent_flexigrid_response(request, objects): + response = HttpResponse(content_type='application/json') + + sortname = request.GET.get('sidx', None) # get index row - i.e. user click to sort + sortorder = request.GET.get('sord', 'desc') # get the direction + page = int(request.GET.get('page', 1)) # get the requested page + rp = int(request.GET.get('rows', 10)) # get how many rows we want to have into the grid + + # select related objects to reduce the number of queries. + objects = objects.select_related('submitter', 'preferred_event') + objects = objects.prefetch_related('events', 'labels') + + if sortname: + if sortorder == "desc": + sortname = "-" + sortname + objects = objects.order_by(sortname) + + total = objects.count() + rows = [] + if rp > -1: + start = (page-1) * rp + + if total: + total_pages = (total / rp) + 1 + else: + total_pages = 0 + + if page > total_pages: + page = total_pages + + end = start+rp + else: + start = 0 + total_pages = 1 + page = 1 + end = total-1 + + if total > MAX_FLEXI_ROWS: + return HttpResponseBadRequest("Too many rows! Please try loading a smaller number.") + + # Function for constructing HTML link to event page from graceid + ev_link = lambda gid: '<a href="{url}">{graceid}</a>'.format( + url=django_reverse("view", args=[gid]), graceid=gid) + + for object in objects[start:end]: + t_start_times = timeSelections(object.t_start) + t_0_times = timeSelections(object.t_0) + t_end_times = timeSelections(object.t_end) + created_times = timeSelections(object.created) + + cell_values = [ + '<a href="{0}">{1}</a>'.format( + django_reverse("superevents:view", args=[ + object.superevent_id]), object.superevent_id), + #Labels + " ".join(["""<span onmouseover="tooltip.show(tooltiptext('%s', '%s', '%s'));" onmouseout="tooltip.hide();" style="color: %s"> %s </span>""" % (label.label.name, label.creator.username, label.created, label.label.defaultColor, label.label.name) for label in object.labelling_set.all()]), + ev_link(object.preferred_event.graceid()), + ", ".join([ev_link(ev.graceid()) for ev in object.get_internal_events()]), + ", ".join([ev_link(ev.graceid()) for ev in object.get_external_events()]), + t_start_times.get('gps', ""), + t_0_times.get('gps', ""), + t_end_times.get('gps', ""), + str(object.is_gw), + '<a href="%s">Data</a>' % '#', # TODO: fix this #object.weburl(), + created_times.get('utc', ""), + "%s %s" % (object.submitter.first_name, object.submitter.last_name) + ] + + rows.append({ + 'id' : object.id, + 'cell': cell_values, + }) + + d = { + 'page': page, + 'total': total_pages, + 'records': total, + 'rows': rows, + } + + try: + msg = json.dumps(d) + except Exception: + # XXX Not right not right not right. + msg = "{}" + response['Content-length'] = len(msg) + response.write(msg) + + return response + + +def event_flexigrid_response(request, objects): + response = HttpResponse(content_type='application/json') + + sortname = request.GET.get('sidx', None) # get index row - i.e. user click to sort + sortorder = request.GET.get('sord', 'desc') # get the direction + page = int(request.GET.get('page', 1)) # get the requested page + rp = int(request.GET.get('rows', 10)) # get how many rows we want to have into the grid + + get_neighbors = request.GET.get('get_neighbors', False) # whether to retrieve the neighbors + if get_neighbors in ['True', 'true', 'T', 't', 1, '1']: + get_neighbors = True + else: + get_neighbors = False + + # select related objects to reduce the number of queries. + objects = objects.select_related('group', 'pipeline', 'search', 'submitter') + + if sortname: + if sortorder == "desc": + sortname = "-" + sortname + objects = objects.order_by(sortname) + + total = objects.count() + rows = [] + if rp > -1: + start = (page-1) * rp + + if total: + total_pages = (total / rp) + 1 + else: + total_pages = 0 + + if page > total_pages: + page = total_pages + + end = start+rp + else: + start = 0 + total_pages = 1 + page = 1 + end = total-1 + + if total > MAX_FLEXI_ROWS: + return HttpResponseBadRequest("Too many rows! Please try loading a smaller number.") + + for object in objects[start:end]: + event_times = timeSelections(object.gpstime) + created_times = timeSelections(object.created) + if object.search: + search_name = object.search.name + else: + search_name = '' + + display_far = scientific(object.far) + if object.far and is_external(request.user): + if object.far < settings.VOEVENT_FAR_FLOOR: + display_far = "< %s" % scientific(settings.VOEVENT_FAR_FLOOR) + + cell_values = [ '<a href="%s">%s</a>' % + (django_reverse("view", args=[object.graceid()]), object.graceid()), + #Labels + " ".join(["""<span onmouseover="tooltip.show(tooltiptext('%s', '%s', '%s'));" onmouseout="tooltip.hide();" style="color: %s"> %s </span>""" % (label.label.name, label.creator.username, label.created, label.label.defaultColor, label.label.name) + for label in object.labelling_set.all()]), + object.group.name, + object.pipeline.name, + search_name, + + event_times.get('gps',""), + #event_times['utc'], + + object.instruments, + + #scientific(display_far), + display_far, + + '<a href="%s">Data</a>' % object.weburl(), + + #created_times['gps'], + created_times.get('utc',""), + + "%s %s" % (object.submitter.first_name, object.submitter.last_name) + + ] + + if get_neighbors: + # Links to neighbors + cell_values.insert(2, ', '.join([ '<a href="%s">%s</a>' % + (django_reverse("view", args=[n.graceid()]), n.graceid()) for n in object.neighbors()])) + + rows.append( + { 'id' : object.id, + 'cell': cell_values, + } + ) + d = { + 'page': page, + 'total': total_pages, + 'records': total, + 'rows': rows, + } + try: + msg = json.dumps(d) + except Exception: + # XXX Not right not right not right. + msg = "{}" + response['Content-length'] = len(msg) + response.write(msg) + + return response diff --git a/gracedb/search/views.py b/gracedb/search/views.py index 81bb6311f..55b4867de 100644 --- a/gracedb/search/views.py +++ b/gracedb/search/views.py @@ -1,26 +1,19 @@ +import logging + from django import forms from django.conf import settings -from django.http import HttpResponse, HttpResponseRedirect, \ - HttpResponseBadRequest +from django.http import HttpResponse, HttpResponseBadRequest from django.shortcuts import render -from django.urls import reverse -from django.utils.html import escape from django.views.decorators.http import require_GET from guardian.shortcuts import get_objects_for_user from .forms import MainSearchForm -from .utils import get_search_results_as_ligolw -from core.http import check_and_serve_file +from .utils import get_search_results_as_ligolw, event_flexigrid_response, \ + superevent_flexigrid_response -from events.models import Event -from events.view_utils import flexigridResponse as events_flex -from superevents.models import Superevent -from superevents.search_flex import flexigridResponse as superevents_flex - -import os -import logging +# Set up logger logger = logging.getLogger(__name__) @@ -61,10 +54,10 @@ def search(request): # Flex format if query_type == 'S': # Superevent query - flex_func = superevents_flex + flex_func = superevent_flexigrid_response elif query_type == 'E': # Event query - flex_func = events_flex + flex_func = event_flexigrid_response else: # TODO: raise error pass diff --git a/gracedb/superevents/search_flex.py b/gracedb/superevents/search_flex.py deleted file mode 100644 index 9a6a33e6b..000000000 --- a/gracedb/superevents/search_flex.py +++ /dev/null @@ -1,109 +0,0 @@ -from django.conf import settings -from django.http import HttpResponse, HttpResponseBadRequest -from django.urls import reverse as django_reverse - -from events.templatetags.scientific import scientific -from events.templatetags.timeutil import timeSelections - -import os -import json -import logging -logger = logging.getLogger(__name__) - -# XXX This should be configurable / moddable or something -MAX_QUERY_RESULTS = 1000 - -# The maximum number of rows to be returned by flexigridResponse -# in the event that the user asks for all of them. -MAX_FLEXI_ROWS = 250 - - -def flexigridResponse(request, objects): - response = HttpResponse(content_type='application/json') - - sortname = request.GET.get('sidx', None) # get index row - i.e. user click to sort - sortorder = request.GET.get('sord', 'desc') # get the direction - page = int(request.GET.get('page', 1)) # get the requested page - rp = int(request.GET.get('rows', 10)) # get how many rows we want to have into the grid - - # select related objects to reduce the number of queries. - objects = objects.select_related('submitter', 'preferred_event') - objects = objects.prefetch_related('events', 'labels') - - if sortname: - if sortorder == "desc": - sortname = "-" + sortname - objects = objects.order_by(sortname) - - total = objects.count() - rows = [] - if rp > -1: - start = (page-1) * rp - - if total: - total_pages = (total / rp) + 1 - else: - total_pages = 0 - - if page > total_pages: - page = total_pages - - end = start+rp - else: - start = 0 - total_pages = 1 - page = 1 - end = total-1 - - if total > MAX_FLEXI_ROWS: - return HttpResponseBadRequest("Too many rows! Please try loading a smaller number.") - - # Function for constructing HTML link to event page from graceid - ev_link = lambda gid: '<a href="{url}">{graceid}</a>'.format( - url=django_reverse("view", args=[gid]), graceid=gid) - - for object in objects[start:end]: - t_start_times = timeSelections(object.t_start) - t_0_times = timeSelections(object.t_0) - t_end_times = timeSelections(object.t_end) - created_times = timeSelections(object.created) - - cell_values = [ - '<a href="{0}">{1}</a>'.format( - django_reverse("superevents:view", args=[ - object.superevent_id]), object.superevent_id), - #Labels - " ".join(["""<span onmouseover="tooltip.show(tooltiptext('%s', '%s', '%s'));" onmouseout="tooltip.hide();" style="color: %s"> %s </span>""" % (label.label.name, label.creator.username, label.created, label.label.defaultColor, label.label.name) for label in object.labelling_set.all()]), - ev_link(object.preferred_event.graceid()), - ", ".join([ev_link(ev.graceid()) for ev in object.get_internal_events()]), - ", ".join([ev_link(ev.graceid()) for ev in object.get_external_events()]), - t_start_times.get('gps', ""), - t_0_times.get('gps', ""), - t_end_times.get('gps', ""), - str(object.is_gw), - '<a href="%s">Data</a>' % '#', # TODO: fix this #object.weburl(), - created_times.get('utc', ""), - "%s %s" % (object.submitter.first_name, object.submitter.last_name) - ] - - rows.append({ - 'id' : object.id, - 'cell': cell_values, - }) - - d = { - 'page': page, - 'total': total_pages, - 'records': total, - 'rows': rows, - } - - try: - msg = json.dumps(d) - except Exception: - # XXX Not right not right not right. - msg = "{}" - response['Content-length'] = len(msg) - response.write(msg) - - return response diff --git a/gracedb/templates/gracedb/cbc_report.html b/gracedb/templates/gracedb/cbc_report.html index f63d526c0..527e21848 100644 --- a/gracedb/templates/gracedb/cbc_report.html +++ b/gracedb/templates/gracedb/cbc_report.html @@ -37,7 +37,7 @@ onload="document.search_form.query.focus();" {{ form.as_table }} <tr><td/><td><a onClick="toggle_visibility('hints');">Hints on querying</a></td> </tr> - <tr><td/><td>{% include "gracedb/query_help_frag.html" %}</td></tr> + <tr><td/><td>{% include "search/query_help_frag.html" %}</td></tr> </table> </form> diff --git a/gracedb/templates/gracedb/event_list.html b/gracedb/templates/gracedb/event_list.html deleted file mode 100644 index 67b137003..000000000 --- a/gracedb/templates/gracedb/event_list.html +++ /dev/null @@ -1,137 +0,0 @@ -{% extends "base.html" %} -{% load timeutil %} - -{% block title %}{{ title }}{% endblock %} -{% block heading %}{{ title }}{% endblock %} -{% block pageid %}search{% endblock %} - -{% block jscript %} -{% load static %} -<link rel="stylesheet" type="text/css" media="screen" - href="{% static "css/jqgrid/theme/jquery-ui.css" %}" /> - -<link rel="stylesheet" type="text/css" media="screen" - href="{% static "css/jqgrid/theme/ui.all.css" %}" /> - -<link rel="stylesheet" type="text/css" media="screen" - href="{% static "css/jqgrid/ui.jqgrid.css" %}" /> - -<link rel="stylesheet" href="{% static "css/labeltips.css" %}" /> - -<script src="{% static "js/jquery-1.3.2.min.js" %}" type="text/javascript"></script> -<script src="{% static "js/grid.locale-en.js" %}" type="text/javascript"></script> -<script src="{% static "js/jquery.jqGrid.min.js" %}" type="text/javascript"></script> -<script src="{% static "js/jquery-ui-1.7.2.custom.min.js" %}" type="text/javascript"></script> - -<!-- This is an attempt to get updated jqGrind stuff from a CDN --> -<!-- -<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jqgrid/4.6.0/css/ui.jqgrid.css"/> -<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> -<script src="https://cdnjs.cloudflare.com/ajax/libs/jqgrid/4.6.0/js/i18n/grid.locale-en.js"></script> -<script src="https://cdnjs.cloudflare.com/ajax/libs/jqgrid/4.6.0/js/jquery.jqGrid.min.js"></script> ---> - - <script type="text/javascript"> - <!-- - function toggle_visibility(id) { - var e = document.getElementById(id); - if(e.style.display == 'block') - e.style.display = 'none'; - else - e.style.display = 'block'; - } - //--> - </script> - <script type="text/javascript"> - <!-- - $(document).ready(function(){ - $("#flex1").jqGrid - ( - { - sortable: true, - {% if get_neighbors %} - url: '{% url "eventsearch" %}flex?query={{rawquery|urlencode}}&get_neighbors=true', - {% else %} - url: '{% url "eventsearch" %}flex?query={{rawquery|urlencode}}', - {% endif %} - datatype: 'json', - mtype: "GET", - colNames : ["UID", "Labels", {% if get_neighbors %} "Neighbors (+/-5sec)", {% endif %} "Group", "Pipeline", "Search", "Event Time", "Instruments", "FAR (Hz)", "Links", "Submitted", "Submitted By", - ], - colModel : [ - {name : 'id', index: 'id', width : 50, sortable : true, align: 'left', hidden: false}, - {name : 'labels', index: 'labels', width : 70, sortable : false, align: 'left'}, - {% if get_neighbors %} - {name : 'neighbors', index: 'neighbors', width : 100, sortable : false, align: 'left'}, - {% endif %} - {name : 'group', index: 'group', width : 50, sortable : false, align: 'left', hidden: true}, - {name : 'pipeline', index: 'pipeline', width : 50, sortable : false, align: 'left', hidden: true}, - {name : 'search', index: 'search', width : 50, sortable : false, align: 'left', hidden: true}, - {name : 'gpstime', index: 'gpstime', width : 90, sortable : true, align: 'right'}, - {name : 'instruments', index: 'instruments', width : 50, sortable : true, align: 'right', hidden: false}, - {name : 'far', index: 'far', width : 80, sortable : true, align: 'right', hidden: false}, - {name : 'links', index: 'links', width : 80, sortable : false, align: 'center'}, - {name : 'created', index: 'created', width : 100, sortable : true, align: 'right'}, - {name : 'submitter', index: 'submitter', width : 80, sortable : true, align: 'right'}, - - ], - pager: '#pager1', - rowNum:20, - rowList:[10,20,30,40,50,75,100,-1], - sortname: 'id', - sortorder: "desc", - viewrecords: true, - caption: 'Query Results', - //forceFit: true, - //height: '150px', - height: 'auto', - //loadui: 'enable', // block, disable - toolbar: [true, "top"], - autowidth: true, - loadError: function (jqXHR, textStatus, errorThrown) { - alert('Error, ' + jqXHR.status + ': ' + jqXHR.responseText); - }, - } - ); - - //$("#flex1").jqGrid('gridResize',{}); - //$("#flex1").jqGrid('gridResize',{ minWidth:350,maxWidth:800,minHeight:80, maxHeight:350 }); - $("#flex1").jqGrid('navGrid','#pager1',{search: false, edit:false,add:false,del:false}); - $("#t_flex1").append("<input type='button' value='Select Columns' style='height:20px;font-size:-3'/>"); - $("input","#t_flex1").click(function(){ - $("#flex1").jqGrid('setColumns'); - }); - - }); - --> - </script> -{% endblock %} - -{% block content %} - -<p style="color:red; font-weight: bold;">24 May 2018: This search page is deprecated and will be removed at some point in the future.</p> -<p>{{ message }}</p> - -{% if form %} - <form action="{{ formAction }}" method="POST"> - <table> - {{ form.as_table }} - <tr><td/><td><input type="Submit" value="Search" class="searchButtonClass"></td> - <tr><td/> - <td> - <a onClick="toggle_visibility('hints');">Hints on querying</a> - {% if rawquery %} - | <a href="{% url "eventsearch" %}?query={{rawquery|urlencode}}">Link to current query</a> - | <a href="{% url "eventsearch" %}?query={{rawquery|urlencode}}&ligolw">Download LIGOLW File</a> - {% endif %} - </td> - </tr> - <tr><td/><td>{% include "gracedb/query_help_frag.html" %}</td></tr> - </table> - </form> -{% endif %} - -<table id="flex1"></table> -<div id="pager1"></div> - -{% endblock %} diff --git a/gracedb/templates/gracedb/query.html b/gracedb/templates/gracedb/query.html deleted file mode 100644 index 6eeebc559..000000000 --- a/gracedb/templates/gracedb/query.html +++ /dev/null @@ -1,73 +0,0 @@ -{% extends "base.html" %} - -{% block bodyattrs %} -onload="document.search_form.query.focus();" -{% endblock %} - -{% block title %}Search{% endblock %} -{% block heading %}{% endblock %} -{% block pageid %}search{% endblock %} - -{% block jscript %} - <script type="text/javascript"> - <!-- - function toggle_visibility(id) { - var e = document.getElementById(id); - if(e.style.display == 'block') - e.style.display = 'none'; - else - e.style.display = 'block'; - } - //--> - </script> -{% endblock %} - -{% block content %} - -<p style="color: red; font-weight: bold;">24 May 2018: This search page is deprecated and will be removed at some point in the future.</p> - -<form method="POST" name="search_form"> - <table> - {{ form.as_table }} - <tr><td/><td><input type="Submit" value="Search" class="searchButtonClass"></td> - <!-- XXX this is very annoying. Need the tooltip stuff if you're going to try to use this. - <tr> - <th> - <label for="id_query">Query:</label> - </th> - <td> - <input id="id_query" name="query" size="60" type="text" /> - </td> - <td> - <label for="id_get_neighbors"><span onmouseover="tooltip.show(tooltiptext('retrieve neighbors for each event in search results (warning: slows down queries)'));" onmouseout="tooltip.hide();">Show neighbors:</span></label> - </td> - <td> - <input id="id_get_neighbors" name="get_neighbors" type="checkbox" /> - </td> - </tr> - {# if form.query.errors #} - <tr> <td/> <td> {{ form.query.errors.as_text|safe }} </td> </tr> - {# endif #} - --> - <tr><td/><td><a onClick="toggle_visibility('hints');">Hints on querying</a></td> - </tr> - <tr><td/><td>{% include "gracedb/query_help_frag.html" %}</td></tr> - </table> -</form> - -<!-- Old style Query --> - -{% if form2 %} -<a><h3 onClick="toggle_visibility('hiddenqueryform');">Guided Query</h3></a> -<div id="hiddenqueryform" style="display: none;"> - <form method="POST"> - <table> - {{ form2.as_table }} - <tr><td></td><td><input type="Submit"/></td></tr> - </table> - </form> -</div> -{% endif %} - -{% endblock %} - diff --git a/gracedb/templates/search/query.html b/gracedb/templates/search/query.html index 9557220ba..c9b47ceff 100644 --- a/gracedb/templates/search/query.html +++ b/gracedb/templates/search/query.html @@ -39,18 +39,18 @@ onload="document.search_form.query.focus();" colNames : ["UID", "Labels", "Preferred Event", "GW events", "External events", "t_start", "t_0", "t_end", "is_gw", "Links", "Submitted", "Submitted By", ], colModel : [ - {name : 'id', index: 'id', width : 30, sortable : true, align: 'left', hidden: false}, - {name : 'labels', index: 'labels', width : 40, sortable : false, align: 'left'}, - {name : 'preferred_event', index: 'preferred_event', width : 20, sortable : true, align: 'left', hidden: false}, - {name : 'gw_events', index: 'gw_events', width : 50, sortable : false, align: 'left', hidden: false}, - {name : 'em_events', index: 'em_events', width : 50, sortable : false, align: 'left', hidden: true}, - {name : 't_start', index: 't_start', width : 20, sortable : false, align: 'left', hidden: true}, - {name : 't_0', index: 't_0', width : 20, sortable : false, align: 'left', hidden: false}, - {name : 't_end', index: 't_end', width : 20, sortable : false, align: 'left', hidden: true}, - {name : 'is_gw', index: 'is_gw', width : 20, sortable : true, align: 'left', hidden: true}, - {name : 'links', index: 'links', width : 20, sortable : false, align: 'center'}, - {name : 'created', index: 'created', width : 40, sortable : true, align: 'left'}, - {name : 'submitter', index: 'submitter', width : 60, sortable : true, align: 'left'}, + {name: 'id', index: 'id', width: 30, sortable: true, align: 'left', hidden: false}, + {name: 'labels', index: 'labels', width: 40, sortable: true, align: 'left'}, + {name: 'preferred_event', index: 'preferred_event', width: 20, sortable: true, align: 'left', hidden: false}, + {name: 'gw_events', index: 'gw_events', width: 50, sortable: false, align: 'left', hidden: false}, + {name: 'em_events', index: 'em_events', width: 50, sortable: false, align: 'left', hidden: true}, + {name: 't_start', index: 't_start', width: 20, sortable: true, align: 'left', hidden: true}, + {name: 't_0', index: 't_0', width: 20, sortable: true, align: 'left', hidden: false}, + {name: 't_end', index: 't_end', width: 20, sortable: true, align: 'left', hidden: true}, + {name: 'is_gw', index: 'is_gw', width: 20, sortable: true, align: 'left', hidden: true}, + {name: 'links', index: 'links', width: 20, sortable: false, align: 'center'}, + {name: 'created', index: 'created', width: 40, sortable: true, align: 'left'}, + {name: 'submitter', index: 'submitter', width: 60, sortable: true, align: 'left'}, ], pager: '#pager1', @@ -91,20 +91,20 @@ onload="document.search_form.query.focus();" colNames : ["UID", "Labels", {% if get_neighbors %} "Neighbors (+/-5sec)", {% endif %} "Group", "Pipeline", "Search", "Event Time", "Instruments", "FAR (Hz)", "Links", "Submitted", "Submitted By", ], colModel : [ - {name : 'id', index: 'id', width : 50, sortable : true, align: 'left', hidden: false}, - {name : 'labels', index: 'labels', width : 70, sortable : false, align: 'left'}, + {name: 'id', index: 'id', width: 50, sortable: true, align: 'left', hidden: false}, + {name: 'labels', index: 'labels', width: 70, sortable: true, align: 'left'}, {% if get_neighbors %} - {name : 'neighbors', index: 'neighbors', width : 100, sortable : false, align: 'left'}, + {name: 'neighbors', index: 'neighbors', width: 100, sortable: false, align: 'left'}, {% endif %} - {name : 'group', index: 'group', width : 50, sortable : false, align: 'left', hidden: false}, - {name : 'pipeline', index: 'pipeline', width : 50, sortable : false, align: 'left', hidden: false}, - {name : 'search', index: 'search', width : 50, sortable : false, align: 'left', hidden: false}, - {name : 'gpstime', index: 'gpstime', width : 70, sortable : true, align: 'right'}, - {name : 'instruments', index: 'instruments', width : 50, sortable : true, align: 'right', hidden: false}, - {name : 'far', index: 'far', width : 80, sortable : true, align: 'right', hidden: false}, - {name : 'links', index: 'links', width : 80, sortable : false, align: 'center', hidden: true}, - {name : 'created', index: 'created', width : 100, sortable : true, align: 'right'}, - {name : 'submitter', index: 'submitter', width : 80, sortable : true, align: 'right'}, + {name: 'group', index: 'group', width: 50, sortable: true, align: 'left', hidden: false}, + {name: 'pipeline', index: 'pipeline', width: 50, sortable: false, align: 'left', hidden: false}, + {name: 'search', index: 'search', width: 50, sortable: true, align: 'left', hidden: false}, + {name: 'gpstime', index: 'gpstime', width: 70, sortable: true, align: 'right'}, + {name: 'instruments', index: 'instruments', width: 50, sortable: true, align: 'right', hidden: false}, + {name: 'far', index: 'far', width: 80, sortable: true, align: 'right', hidden: false}, + {name: 'links', index: 'links', width: 80, sortable: false, align: 'center', hidden: true}, + {name: 'created', index: 'created', width: 100, sortable: true, align: 'right'}, + {name: 'submitter', index: 'submitter', width: 80, sortable: true, align: 'right'}, ], pager: '#pager1', @@ -144,12 +144,8 @@ onload="document.search_form.query.focus();" {{ form.as_table }} <tr><td></td><td><input type="Submit" value="Search" class="searchButtonClass"></td></tr> <tr><td></td><td><a onClick="toggle_visibility('hints');">Query help</a> - | <a href="{% url "eventsearch" %}">Link to old search page</a> - {% if raw_query %} - | <a href="{{ request.build_absolute_uri }}">Link to current query</a> - {% if query_type == 'E' %} - | <a href="{% url "mainsearch" %}?query={{raw_query|urlencode}}&query_type={{query_type}}&get_neighbors={{get_neighbors}}&results_format=L">Download LIGOLW file</a> - {% endif %} + {% if raw_query and query_type == 'E' %} + | <a href="{% url "mainsearch" %}?query={{raw_query|urlencode}}&query_type={{query_type}}&get_neighbors={{get_neighbors}}&results_format=L">Download LIGOLW file</a> {% endif %} </td></tr> <tr><td></td><td>{% include "search/query_help_frag.html" %}</td></tr> -- GitLab