Implement more elements on Insights page

This commit is contained in:
Mark Veidemanis 2022-07-21 13:52:10 +01:00
parent 9de9ddff6f
commit 17d465171b
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
11 changed files with 291 additions and 152 deletions

View File

@ -53,4 +53,4 @@ THRESHOLD_API_KEY = "name"
THRESHOLD_API_TOKEN = "token" THRESHOLD_API_TOKEN = "token"
THRESHOLD_API_COUNTER = "counter" THRESHOLD_API_COUNTER = "counter"
DEBUG = True DEBUG = True

View File

@ -33,6 +33,8 @@ from core.views.dynamic.drilldown import DrilldownSearch, ThresholdInfoModal
from core.views.dynamic.insights import ( from core.views.dynamic.insights import (
InsightsChannels, InsightsChannels,
InsightsInfoModal, InsightsInfoModal,
InsightsMeta,
InsightsNicks,
InsightsSearch, InsightsSearch,
) )
@ -54,13 +56,18 @@ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("accounts/", include("django.contrib.auth.urls")), path("accounts/", include("django.contrib.auth.urls")),
path("accounts/signup/", Signup.as_view(), name="signup"), path("accounts/signup/", Signup.as_view(), name="signup"),
##
path("ui/drilldown/", Drilldown.as_view(), name="drilldown"), path("ui/drilldown/", Drilldown.as_view(), name="drilldown"),
path("ui/drilldown/modal/", ThresholdInfoModal.as_view(), name="modal_drilldown"),
path("ui/drilldown/search/", DrilldownSearch.as_view(), name="search_drilldown"),
##
path("ui/insights/", Insights.as_view(), name="insights"), path("ui/insights/", Insights.as_view(), name="insights"),
path("parts/search/drilldown/", DrilldownSearch.as_view(), name="search_drilldown"), path("ui/insights/search/", InsightsSearch.as_view(), name="search_insights"),
path("parts/search/insights/", InsightsSearch.as_view(), name="search_insights"), path("ui/insights/channels/", InsightsChannels.as_view(), name="chans_insights"),
path("parts/channels/insights/", InsightsChannels.as_view(), name="chans_insights"), path("ui/insights/nicks/", InsightsNicks.as_view(), name="nicks_insights"),
path("modal/drilldown/", ThresholdInfoModal.as_view(), name="modal_drilldown"), path("ui/insights/meta/", InsightsMeta.as_view(), name="meta_insights"),
path("modal/insights/", InsightsInfoModal.as_view(), name="modal_insights"), path("ui/insights/modal/", InsightsInfoModal.as_view(), name="modal_insights"),
##
path("api/chans/", ThresholdChans.as_view(), name="chans"), path("api/chans/", ThresholdChans.as_view(), name="chans"),
path("api/users/", ThresholdUsers.as_view(), name="users"), path("api/users/", ThresholdUsers.as_view(), name="users"),
path("api/online/", ThresholdOnline.as_view(), name="online"), path("api/online/", ThresholdOnline.as_view(), name="online"),

15
core/lib/nicktrace.py Normal file
View File

@ -0,0 +1,15 @@
from core.lib.opensearch import client, run_main_query
def get_nicks(request, net, nick):
"""
Get all related nicknames of the given nickname by tracking nickname changes.
"""
# Get the initial query
query = {}
results = set()
# nicks = query["nicks"]
# for nick in nicks:
# if nick not in results:
# nicks_result = get_nicks(request, net_nick)
# results.add(nick)

View File

@ -93,7 +93,7 @@ def filter_blacklisted(user, response):
response["exemption"] = True response["exemption"] = True
def run_main_query(client, user, query, size=None): def run_main_query(client, user, query, custom_query=False, size=None):
""" """
Low level helper to run an ES query. Low level helper to run an ES query.
Accept a user to pass it to the filter, so we can Accept a user to pass it to the filter, so we can
@ -101,7 +101,10 @@ def run_main_query(client, user, query, size=None):
Accept fields and size, for the fields we want to match and the Accept fields and size, for the fields we want to match and the
number of results to return. number of results to return.
""" """
search_query = construct_query(query, size) if custom_query:
search_query = query
else:
search_query = construct_query(query, size)
try: try:
response = client.search( response = client.search(
body=search_query, index=settings.OPENSEARCH_INDEX_MAIN body=search_query, index=settings.OPENSEARCH_INDEX_MAIN
@ -131,7 +134,7 @@ def query_results(request, size=None):
client, client,
request.user, request.user,
query, query,
size, size=size,
) )
if not results: if not results:
return False return False

View File

@ -1,12 +1,9 @@
<div id="channels">
<div class="panel is-primary" style="max-height: 30em; overflow: auto">
<div class="tile is-parent" id="chans1"> <p class="panel-heading">
<div class="tile is-child "> Channels
<div class="panel is-primary" style="height: 30rem; overflow: auto"> </p>
<p class="panel-heading"> {% for chan in chans %}
Channels
</p>
{% for chan in chans %}
<a class="panel-block is-active"> <a class="panel-block is-active">
<span class="panel-icon"> <span class="panel-icon">
<i class="fa-solid fa-hashtag" aria-hidden="true"></i> <i class="fa-solid fa-hashtag" aria-hidden="true"></i>
@ -20,9 +17,7 @@
class="button is-small"> class="button is-small">
{{ chan }} {{ chan }}
</span> </span>
</a> </a>
{% endfor %} {% endfor %}
</div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,112 @@
{% load static %}
{% load index %}
<div id="info">
<div class="panel is-primary">
<p class="panel-heading">
Information
</p>
<div class="panel-block is-active">
{% if item is not None %}
<div class="table-container">
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr>
<th>src</th>
<td>
{% if item.src == 'irc' %}
<span class="icon" data-tooltip="IRC">
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
</span>
IRC
{% elif item.src == 'dis' %}
<span class="icon" data-tooltip="Discord">
<i class="fa-brands fa-discord" aria-hidden="true"></i>
</span>
Discord
{% endif %}
</td>
</tr>
<tr>
<th>nick</th>
<td>
{% if item.online is True %}
<span class="icon has-text-success has-tooltip-success" data-tooltip="Online">
<i class="fa-solid fa-circle"></i>
</span>
{{ item.nick }}
{% elif item.online is False %}
<span class="icon has-text-danger has-tooltip-danger" data-tooltip="Offline">
<i class="fa-solid fa-circle"></i>
</span>
{{ item.nick }}
{% else %}
<span class="icon has-text-warning has-tooltip-warning" data-tooltip="Unknown">
<i class="fa-solid fa-circle"></i>
</span>
{{ item.nick }}
{% endif %}
{% if item.num_chans is not None %}
<span class="tag">
{{ item.num_chans }}
</span>
{% endif %}
</td>
</tr>
<tr>
<th>host</th>
<td>{{ item.host }}</td>
</tr>
<tr>
<th>actions</th>
<td>
{% if item.src == 'irc' %}
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'modal_insights' %}"
hx-vals='{"net": "{{ item.net }}", "nick": "{{ item.nick }}", "channel": "{{ item.channel }}"}'
hx-target="#modals-here"
hx-trigger="click"
class="button is-small">
Information
</button>
{% endif %}
</td>
</tr>
<tr>
<th>net</th>
<td>{{ item.net }}</td>
</tr>
</tbody>
</table>
</div>
{% endif %}
</div>
<div
style="display: none;"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-vals='{"net": "{{ item.net }}", "nick": "{{ item.nick }}"}'
hx-post="{% url 'chans_insights' %}"
hx-trigger="load"
hx-target="#channels"
hx-swap="outerHTML">
</div>
<div
style="display: none;"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-vals='{"net": "{{ item.net }}", "nick": "{{ item.nick }}"}'
hx-post="{% url 'nicks_insights' %}"
hx-trigger="load"
hx-target="#nicks"
hx-swap="outerHTML">
</div>
<div
style="display: none;"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-vals='{"net": "{{ item.net }}", "nick": "{{ item.nick }}"}'
hx-post="{% url 'meta_insights' %}"
hx-trigger="load"
hx-target="#meta"
hx-swap="outerHTML">
</div>
</div>
</div>

View File

@ -38,46 +38,66 @@
.icon { border-bottom: 0px !important;} .icon { border-bottom: 0px !important;}
</style> </style>
<div class="tile is-ancestor"> <div class="tile is-ancestor">
<div class="tile is-vertical is-9">
<div class="tile is-parent is-vertical"> <div class="tile">
<div class="tile is-child box"> <div class="tile is-parent is-vertical">
<form method="POST"> <article class="tile is-child box">
{% csrf_token %} <form method="POST">
<div class="field"> {% csrf_token %}
<label class="label">Search</label>
<div class="field-body">
<div class="field"> <div class="field">
<div class="control is-expanded has-icons-left"> <label class="label">Search</label>
<input name="query" class="input" type="text" placeholder="nickname"> <div class="field-body">
<span class="icon is-small is-left"> <div class="field">
<i class="fas fa-magnifying-glass"></i> <div class="control is-expanded has-icons-left">
</span> <input name="query" class="input" type="text" placeholder="nickname">
<span class="icon is-small is-left">
<i class="fas fa-magnifying-glass"></i>
</span>
</div>
</div>
</div> </div>
</div> </div>
</div> <div class="field">
</div> <div class="control">
<div class="field"> <button
<div class="control"> class="button is-primary is-fullwidth"
<button hx-post="{% url 'search_insights' %}"
class="button is-primary is-fullwidth" hx-trigger="click"
hx-post="{% url 'search_insights' %}" hx-target="#info"
hx-trigger="click" hx-swap="outerHTML">
hx-target="#results" Search
hx-swap="innerHTML"> </button>
Search </div>
</button> </div>
</div> </form>
</div> </article>
</form>
</div>
<div id="results">
</div>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<div id="nicks"></div>
</article>
</div>
</div>
<div class="tile">
<div class="tile is-parent is-vertical">
<article class="tile is-child box">
<div id="info"></div>
</article>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<div id="meta"></div>
</article>
</div>
</div>
</div>
<div class="tile is-parent">
<article class="tile is-child box">
<div id="channels"></div>
</article>
</div> </div>
<article id="chans1">
</article>
</div> </div>
<div id="modals-here"></div> <div id="modals-here"></div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,21 @@
<div id="meta">
<div class="panel is-primary" style="max-height: 30em; overflow: auto">
<p class="panel-heading">
Meta
</p>
<a class="panel-block is-active">
<span class="panel-icon">
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
</span>
<span
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'modal_drilldown' %}"
hx-vals='{"net": "{{ net }}", "nick": "{{ nick }}", "channel": "{{ chan }}"}'
hx-target="#modals-here"
hx-trigger="click"
class="button is-small">
bbb
</span>
</a>
</div>
</div>

View File

@ -0,0 +1,21 @@
<div id="nicks">
<div class="panel is-primary" style="max-height: 30em; overflow: auto">
<p class="panel-heading">
Nicks
</p>
<a class="panel-block is-active">
<span class="panel-icon">
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
</span>
<span
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'modal_drilldown' %}"
hx-vals='{"net": "{{ net }}", "nick": "{{ nick }}", "channel": "{{ chan }}"}'
hx-target="#modals-here"
hx-trigger="click"
class="button is-small">
aaa
</span>
</a>
</div>
</div>

View File

@ -1,94 +0,0 @@
{% load static %}
{% load index %}
<div class="panel is-primary">
<p class="panel-heading">
Information
</p>
<div class="panel-block is-active">
<div class="tile is-child">
{% if item is not None %}
<div class="table-container">
<table class="table is-fullwidth is-hoverable">
<tbody>
<tr>
<th>src</th>
<td>
{% if item.src == 'irc' %}
<span class="icon" data-tooltip="IRC">
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
</span>
IRC
{% elif item.src == 'dis' %}
<span class="icon" data-tooltip="Discord">
<i class="fa-brands fa-discord" aria-hidden="true"></i>
</span>
Discord
{% endif %}
</td>
</tr>
<tr>
<th>nick</th>
<td>
{% if item.online is True %}
<span class="icon has-text-success has-tooltip-success" data-tooltip="Online">
<i class="fa-solid fa-circle"></i>
</span>
{{ item.nick }}
{% elif item.online is False %}
<span class="icon has-text-danger has-tooltip-danger" data-tooltip="Offline">
<i class="fa-solid fa-circle"></i>
</span>
{{ item.nick }}
{% else %}
<span class="icon has-text-warning has-tooltip-warning" data-tooltip="Unknown">
<i class="fa-solid fa-circle"></i>
</span>
{{ item.nick }}
{% endif %}
{% if item.num_chans is not None %}
<span class="tag">
{{ item.num_chans }}
</span>
{% endif %}
</td>
</tr>
<tr>
<th>host</th>
<td>{{ item.host }}</td>
</tr>
<tr>
<th>actions</th>
<td>
{% if item.src == 'irc' %}
<button
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-post="{% url 'modal_insights' %}"
hx-vals='{"net": "{{ item.net }}", "nick": "{{ item.nick }}", "channel": "{{ item.channel }}"}'
hx-target="#modals-here"
hx-trigger="click"
class="button is-small">
Information
</button>
{% endif %}
</td>
</tr>
<tr>
<th>net</th>
<td>{{ item.net }}</td>
</tr>
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
</div>
<div
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-vals='{"net": "{{ item.net }}", "nick": "{{ item.nick }}"}'
hx-post="{% url 'chans_insights' %}"
hx-trigger="load"
hx-target="#chans1"
hx-swap="outerHTML">
</div>

View File

@ -5,6 +5,7 @@ from django.views import View
from rest_framework.parsers import FormParser from rest_framework.parsers import FormParser
from rest_framework.views import APIView from rest_framework.views import APIView
from core.lib.nicktrace import get_nicks
from core.lib.opensearch import query_single_result from core.lib.opensearch import query_single_result
from core.lib.threshold import ( from core.lib.threshold import (
annotate_num_chans, annotate_num_chans,
@ -16,7 +17,7 @@ from core.lib.threshold import (
class InsightsSearch(LoginRequiredMixin, View): class InsightsSearch(LoginRequiredMixin, View):
# parser_classes = [JSONParser] # parser_classes = [JSONParser]
template_name = "ui/insights/results.html" template_name = "ui/insights/info.html"
plan_name = "drilldown" plan_name = "drilldown"
def post(self, request): def post(self, request):
@ -57,6 +58,44 @@ class InsightsChannels(LoginRequiredMixin, APIView):
return HttpResponse("No results") return HttpResponse("No results")
class InsightsNicks(LoginRequiredMixin, APIView):
parser_classes = [FormParser]
template_name = "ui/insights/nicks.html"
plan_name = "drilldown"
def post(self, request):
if not request.user.has_plan(self.plan_name):
return HttpResponseForbidden()
if "net" not in request.data:
return HttpResponse("No net")
if "nick" not in request.data:
return HttpResponse("No nick")
net = request.data["net"]
nick = request.data["nick"]
context = {"net": net, "nick": nick}
return render(request, self.template_name, context)
class InsightsMeta(LoginRequiredMixin, APIView):
parser_classes = [FormParser]
template_name = "ui/insights/meta.html"
plan_name = "drilldown"
def post(self, request):
if not request.user.has_plan(self.plan_name):
return HttpResponseForbidden()
if "net" not in request.data:
return HttpResponse("No net")
if "nick" not in request.data:
return HttpResponse("No nick")
net = request.data["net"]
nick = request.data["nick"]
context = {"net": net, "nick": nick}
return render(request, self.template_name, context)
class InsightsInfoModal(LoginRequiredMixin, APIView): class InsightsInfoModal(LoginRequiredMixin, APIView):
parser_classes = [FormParser] parser_classes = [FormParser]
plan_name = "drilldown" plan_name = "drilldown"