Implement query strings for shareable search links
This commit is contained in:
parent
c1071f3d55
commit
648526a6bf
|
@ -141,7 +141,7 @@ def run_main_query(client, user, query, custom_query=False, index=None, size=Non
|
|||
return response
|
||||
|
||||
|
||||
def query_results(request, size=None):
|
||||
def query_results(request, query_params, size=None):
|
||||
"""
|
||||
API helper to alter the OpenSearch return format into something
|
||||
a bit better to parse.
|
||||
|
@ -160,14 +160,14 @@ def query_results(request, size=None):
|
|||
else:
|
||||
sizes = settings.OPENSEARCH_MAIN_SIZES
|
||||
if not size:
|
||||
if "size" in request.POST:
|
||||
size = request.POST["size"]
|
||||
if "size" in query_params:
|
||||
size = query_params["size"]
|
||||
if size not in sizes:
|
||||
message = "Size is not permitted"
|
||||
message_class = "danger"
|
||||
return {"message": message, "class": message_class}
|
||||
if "source" in request.POST:
|
||||
source = request.POST["source"]
|
||||
if "source" in query_params:
|
||||
source = query_params["source"]
|
||||
if source not in settings.OPENSEARCH_MAIN_SOURCES:
|
||||
message = "Invalid source"
|
||||
message_class = "danger"
|
||||
|
@ -175,16 +175,13 @@ def query_results(request, size=None):
|
|||
if source != "all":
|
||||
add_bool.append({"src": source})
|
||||
|
||||
if "dates" in request.POST:
|
||||
dates = request.POST["dates"]
|
||||
spl = dates.split(" - ")
|
||||
if all(spl):
|
||||
spl = [f"{x.replace(' ', 'T')}Z" for x in spl]
|
||||
if not len(spl) == 2:
|
||||
message = "Invalid dates"
|
||||
message_class = "danger"
|
||||
return {"message": message, "class": message_class}
|
||||
from_ts, to_ts = spl
|
||||
if set({"from_date", "to_date", "from_time", "to_time"}).issubset(
|
||||
query_params.keys()
|
||||
):
|
||||
from_ts = f"{query_params['from_date']}T{query_params['from_time']}Z"
|
||||
to_ts = f"{query_params['to_date']}T{query_params['to_time']}Z"
|
||||
print("from ts", from_ts)
|
||||
print("to_ts", to_ts)
|
||||
range_query = {
|
||||
"range": {
|
||||
"ts": {
|
||||
|
@ -194,8 +191,8 @@ def query_results(request, size=None):
|
|||
}
|
||||
}
|
||||
add_top.append(range_query)
|
||||
if "sorting" in request.POST:
|
||||
sorting = request.POST["sorting"]
|
||||
if "sorting" in query_params:
|
||||
sorting = query_params["sorting"]
|
||||
if sorting not in ("asc", "desc", "none"):
|
||||
message = "Invalid sort"
|
||||
message_class = "danger"
|
||||
|
@ -209,20 +206,20 @@ def query_results(request, size=None):
|
|||
}
|
||||
]
|
||||
|
||||
if "check-sentiment" in request.POST:
|
||||
if "sentiment-method" not in request.POST:
|
||||
if "check_sentiment" in query_params:
|
||||
if "sentiment_method" not in query_params:
|
||||
message = "No sentiment method"
|
||||
message_class = "danger"
|
||||
return {"message": message, "class": message_class}
|
||||
if "sentiment" in request.POST:
|
||||
sentiment = request.POST["sentiment"]
|
||||
if "sentiment" in query_params:
|
||||
sentiment = query_params["sentiment"]
|
||||
try:
|
||||
sentiment = float(sentiment)
|
||||
except ValueError:
|
||||
message = "Sentiment is not a float"
|
||||
message_class = "danger"
|
||||
return {"message": message, "class": message_class}
|
||||
sentiment_method = request.POST["sentiment-method"]
|
||||
sentiment_method = query_params["sentiment_method"]
|
||||
range_query_compare = {"range": {"sentiment": {}}}
|
||||
range_query_precise = {
|
||||
"match": {
|
||||
|
@ -242,8 +239,8 @@ def query_results(request, size=None):
|
|||
range_query_precise["match"]["sentiment"] = 0
|
||||
add_top_negative.append(range_query_precise)
|
||||
|
||||
if "query" in request.POST:
|
||||
query = request.POST["query"]
|
||||
if "query" in query_params:
|
||||
query = query_params["query"]
|
||||
search_query = construct_query(query, size)
|
||||
if add_bool:
|
||||
for item in add_bool:
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
hx-post="{% url 'home' %}"
|
||||
hx-trigger="keyup changed delay:200ms"
|
||||
hx-target="#results"
|
||||
hx-swap="innerHTML" id="query" name="query" class="input" type="text" placeholder="msg: science AND nick: BillNye AND channel: #science">
|
||||
hx-swap="innerHTML" id="query" name="query" value="{{ params.query }}" class="input" type="text" placeholder="msg: science AND nick: BillNye AND channel: #science">
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-magnifying-glass"></i>
|
||||
</span>
|
||||
|
@ -122,7 +122,11 @@
|
|||
<span class="select">
|
||||
<select name="size">
|
||||
{% for size in sizes %}
|
||||
{% if size == params.size %}
|
||||
<option selected value="{{ size }}">{{ size }}</option>
|
||||
{% else %}
|
||||
<option value="{{ size }}">{{ size }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span class="icon is-small is-left">
|
||||
|
@ -142,9 +146,26 @@
|
|||
<div class="control has-icons-left">
|
||||
<span class="select">
|
||||
<select id="source" name="source">
|
||||
<option selected value="all">All</option>
|
||||
{% if params.source == 'irc' %}
|
||||
<option selected value="irc">IRC</option>
|
||||
{% else %}
|
||||
<option value="irc">IRC</option>
|
||||
{% endif %}
|
||||
|
||||
{% if params.source == 'dis' %}
|
||||
<option selected value="dis">Discord</option>
|
||||
{% else %}
|
||||
<option value="dis">Discord</option>
|
||||
{% endif %}
|
||||
|
||||
{% if params.source == None %}
|
||||
<option selected value="all">All</option>
|
||||
{% elif params.source == 'all' %}
|
||||
<option selected value="all">All</option>
|
||||
{% else %}
|
||||
<option value="all">All</option>
|
||||
{% endif %}
|
||||
|
||||
</select>
|
||||
<span class="icon is-small is-left">
|
||||
<i class="fas fa-magnifying-glass"></i>
|
||||
|
@ -162,8 +183,24 @@
|
|||
<div id="sentiment">
|
||||
<div class="field has-addons">
|
||||
<div class="control">
|
||||
<input disabled="undefined" name="sentiment" id="sliderWithValue" class="slider has-output-tooltip is-fullwidth" min="-1" max="1" value="0" step="0.05" type="range">
|
||||
<output for="sliderWithValue" class="slider-output">0</output>
|
||||
<input
|
||||
{% if params.check_sentiment != "on" %}
|
||||
disabled="undefined"
|
||||
{% endif %}
|
||||
name="sentiment" id="sliderWithValue" class="slider has-output-tooltip is-fullwidth" min="-1" max="1"
|
||||
{% if params.sentiment == None %}
|
||||
value="0"
|
||||
{% else %}
|
||||
value="{{ params.sentiment }}"
|
||||
{% endif %}
|
||||
step="0.05" type="range">
|
||||
<output for="sliderWithValue" class="slider-output">
|
||||
{% if params.sentiment == None %}
|
||||
0
|
||||
{% else %}
|
||||
{{ params.sentiment }}
|
||||
{% endif %}
|
||||
</output>
|
||||
<script>bulmaSlider.attach();</script>
|
||||
</div>
|
||||
<p class="control">
|
||||
|
@ -174,25 +211,47 @@
|
|||
</div>
|
||||
<div class="control">
|
||||
<label class="radio button has-text-link">
|
||||
<input type="radio" value="below" name="sentiment-method">
|
||||
|
||||
<input type="radio"
|
||||
value="below"
|
||||
{% if params.sentiment_method == 'below' %}
|
||||
checked
|
||||
{% endif %}
|
||||
name="sentiment_method">
|
||||
<span class="icon" data-tooltip="Below">
|
||||
<i class="fa-solid fa-face-frown"></i>
|
||||
</span>
|
||||
</label>
|
||||
<label class="radio button has-text-link is-hidden">
|
||||
<input type="radio" value="exact" name="sentiment-method">
|
||||
|
||||
<input type="radio"
|
||||
value="exact"
|
||||
{% if params.sentiment_method == 'exact' %}
|
||||
checked
|
||||
{% endif %}
|
||||
name="sentiment_method">
|
||||
<span class="icon" data-tooltip="Exact">
|
||||
<i class="fa-solid fa-face-smile"></i>
|
||||
</span>
|
||||
</label>
|
||||
<label class="radio button has-text-link">
|
||||
<input type="radio" value="above" name="sentiment-method">
|
||||
<input type="radio"
|
||||
value="above"
|
||||
{% if params.sentiment_method == 'above' %}
|
||||
checked
|
||||
{% endif %}
|
||||
name="sentiment_method">
|
||||
<span class="icon" data-tooltip="Above">
|
||||
<i class="fa-solid fa-face-smile"></i>
|
||||
</span>
|
||||
</label>
|
||||
<label class="radio button has-text-link">
|
||||
<input type="radio" value="nonzero" name="sentiment-method">
|
||||
<input type="radio"
|
||||
value="nonzero"
|
||||
{% if params.sentiment_method == 'nonzero' %}
|
||||
checked
|
||||
{% endif %}
|
||||
name="sentiment_method">
|
||||
<span class="icon" data-tooltip="Nonzero">
|
||||
<i class="fa-solid fa-face-meh-blank"></i>
|
||||
</span>
|
||||
|
@ -201,7 +260,11 @@
|
|||
</div>
|
||||
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="check-sentiment"
|
||||
<input type="checkbox"
|
||||
name="check_sentiment"
|
||||
{% if params.check_sentiment == "on" %}
|
||||
checked
|
||||
{% endif %}
|
||||
data-script="on click toggle @disabled on #sliderWithValue then toggle @disabled on #sentiment">
|
||||
Check sentiment
|
||||
</label>
|
||||
|
@ -210,7 +273,7 @@
|
|||
<div id="date">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input type="date" name="dates">
|
||||
<input type="date" name="dates" value="{{ params.date }}">
|
||||
<script>
|
||||
var options = {
|
||||
"type": "datetime",
|
||||
|
@ -218,6 +281,10 @@
|
|||
"color": "info",
|
||||
"validateLabel": "Save",
|
||||
"dateFormat": "yyyy-MM-dd",
|
||||
"startDate": "{{ params.from_date|escapejs }}",
|
||||
"startTime": "{{ params.from_time|escapejs }}",
|
||||
"endDate": "{{ params.to_date|escapejs }}",
|
||||
"endTime": "{{ params.to_time|escapejs }}",
|
||||
};
|
||||
// Initialize all input of type date
|
||||
var calendars = bulmaCalendar.attach('[type="date"]', options);
|
||||
|
@ -234,19 +301,31 @@
|
|||
</div>
|
||||
<div class="control">
|
||||
<label class="radio button has-text-link">
|
||||
<input type="radio" value="desc" name="sorting" checked>
|
||||
<input type="radio" value="desc" name="sorting"
|
||||
{% if params.sorting == None %}
|
||||
checked
|
||||
{% elif params.sorting == 'desc' %}
|
||||
checked
|
||||
{% endif %}
|
||||
>
|
||||
<span class="icon" data-tooltip="Sort descending">
|
||||
<i class="fa-solid fa-sort-down"></i>
|
||||
</span>
|
||||
</label>
|
||||
<label class="radio button">
|
||||
<input type="radio" value="asc" name="sorting">
|
||||
<input type="radio" value="asc" name="sorting"
|
||||
{% if params.sorting == 'asc' %}
|
||||
checked
|
||||
{% endif %}>
|
||||
<span class="icon" data-tooltip="Sort ascending">
|
||||
<i class="fa-solid fa-sort-up"></i>
|
||||
</span>
|
||||
</label>
|
||||
<label class="radio button">
|
||||
<input type="radio" value="none" name="sorting">
|
||||
<input type="radio" value="none" name="sorting"
|
||||
{% if params.sorting == 'none' %}
|
||||
checked
|
||||
{% endif %}>
|
||||
<span class="icon" data-tooltip="No sort">
|
||||
<i class="fa-solid fa-sort"></i>
|
||||
</span>
|
||||
|
@ -264,6 +343,9 @@
|
|||
<script>
|
||||
var inputTags = document.getElementById('tags');
|
||||
new BulmaTagsInput(inputTags);
|
||||
{% for tag in tags %}
|
||||
inputTags.BulmaTagsInput().add("{{ tag|escapejs }}");
|
||||
{% endfor %}
|
||||
inputTags.BulmaTagsInput().on('before.add', function(item) {
|
||||
if (item.includes(": ")) {
|
||||
var spl = item.split(": ");
|
||||
|
@ -293,6 +375,9 @@
|
|||
</div>
|
||||
<div class="block">
|
||||
<div id="results">
|
||||
{% if results %}
|
||||
{% include 'ui/drilldown/results.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="modals-here">
|
||||
|
|
|
@ -46,32 +46,71 @@ class DrilldownTableView(View, SingleTableMixin):
|
|||
return HttpResponse("No results")
|
||||
|
||||
|
||||
class Drilldown(View):
|
||||
template_name = "ui/drilldown/drilldown.html"
|
||||
plan_name = "drilldown"
|
||||
def parse_dates(dates):
|
||||
spl = dates.split(" - ")
|
||||
if all(spl):
|
||||
spl = [f"{x.replace(' ', 'T')}" for x in spl]
|
||||
if not len(spl) == 2:
|
||||
message = "Invalid dates"
|
||||
message_class = "danger"
|
||||
return {"message": message, "class": message_class}
|
||||
from_ts, to_ts = spl
|
||||
from_date, from_time = from_ts.split("T")
|
||||
to_date, to_time = to_ts.split("T")
|
||||
|
||||
def get(self, request):
|
||||
if request.user.is_anonymous:
|
||||
sizes = settings.OPENSEARCH_MAIN_SIZES_ANON
|
||||
else:
|
||||
sizes = settings.OPENSEARCH_MAIN_SIZES
|
||||
context = {
|
||||
"sizes": sizes,
|
||||
return {
|
||||
"from_date": from_date,
|
||||
"to_date": to_date,
|
||||
"from_time": from_time,
|
||||
"to_time": to_time,
|
||||
}
|
||||
return render(request, self.template_name, context)
|
||||
else:
|
||||
message = "Invalid dates"
|
||||
message_class = "danger"
|
||||
return {"message": message, "class": message_class}
|
||||
|
||||
def post(self, request):
|
||||
|
||||
def create_tags(query):
|
||||
"""
|
||||
Grab the tags out of the query and make a list
|
||||
we can add to the Bulma tags element when the page loads.
|
||||
"""
|
||||
spl = query.split("AND")
|
||||
spl = [x.strip() for x in spl if ":" in x]
|
||||
spl = [x.replace('"', "") for x in spl]
|
||||
return spl
|
||||
|
||||
|
||||
def drilldown_search(request):
|
||||
template_name = "ui/drilldown/results.html"
|
||||
data_args = request.POST.dict()
|
||||
del data_args["csrfmiddlewaretoken"]
|
||||
print("rep", repr(data_args["dates"]))
|
||||
if data_args["dates"] == " - ":
|
||||
del data_args["dates"]
|
||||
url_params = urllib.parse.urlencode(data_args)
|
||||
print("url_params", url_params)
|
||||
context = query_results(request)
|
||||
if request.GET:
|
||||
query_params = request.GET.dict()
|
||||
elif request.POST:
|
||||
query_params = request.POST.dict()
|
||||
|
||||
# Parse the dates
|
||||
if "dates" in query_params:
|
||||
dates = parse_dates(query_params["dates"])
|
||||
del query_params["dates"]
|
||||
if "message" in dates:
|
||||
return render(request, template_name, dates)
|
||||
query_params["from_date"] = dates["from_date"]
|
||||
query_params["to_date"] = dates["to_date"]
|
||||
query_params["from_time"] = dates["from_time"]
|
||||
query_params["to_time"] = dates["to_time"]
|
||||
|
||||
if request.GET:
|
||||
context = query_results(request, query_params)
|
||||
elif request.POST:
|
||||
context = query_results(request, query_params)
|
||||
|
||||
# Turn the query into tags for populating the taglist
|
||||
tags = create_tags(query_params["query"])
|
||||
context["tags"] = tags
|
||||
|
||||
context["params"] = query_params
|
||||
if "message" in context:
|
||||
return render(request, self.template_name, context)
|
||||
return render(request, template_name, context)
|
||||
|
||||
context["data"] = json.dumps(
|
||||
[
|
||||
|
@ -86,12 +125,37 @@ class Drilldown(View):
|
|||
)
|
||||
if context:
|
||||
response = render(request, template_name, context)
|
||||
if request.GET:
|
||||
return context
|
||||
elif request.POST:
|
||||
del query_params["csrfmiddlewaretoken"]
|
||||
url_params = urllib.parse.urlencode(query_params)
|
||||
response["HX-Push"] = reverse("home") + "?" + url_params
|
||||
return response
|
||||
else:
|
||||
return HttpResponse("No results")
|
||||
|
||||
|
||||
class Drilldown(View):
|
||||
template_name = "ui/drilldown/drilldown.html"
|
||||
plan_name = "drilldown"
|
||||
|
||||
def get(self, request):
|
||||
if request.user.is_anonymous:
|
||||
sizes = settings.OPENSEARCH_MAIN_SIZES_ANON
|
||||
else:
|
||||
sizes = settings.OPENSEARCH_MAIN_SIZES
|
||||
|
||||
context = {}
|
||||
if request.GET:
|
||||
context = drilldown_search(request)
|
||||
context["sizes"] = sizes
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
def post(self, request):
|
||||
return drilldown_search(request)
|
||||
|
||||
|
||||
class ThresholdInfoModal(APIView):
|
||||
parser_classes = [FormParser]
|
||||
plan_name = "drilldown"
|
||||
|
|
Loading…
Reference in New Issue