Implement more UI elements
This commit is contained in:
parent
2c62c343b8
commit
b2a1c42f3d
10
app/urls.py
10
app/urls.py
|
@ -19,6 +19,12 @@ from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
from core.api.views.threshold import (
|
||||||
|
ThresholdChans,
|
||||||
|
ThresholdInfoModal,
|
||||||
|
ThresholdOnline,
|
||||||
|
ThresholdUsers,
|
||||||
|
)
|
||||||
from core.ui.views.drilldown import Drilldown
|
from core.ui.views.drilldown import Drilldown
|
||||||
from core.views import Billing, Cancel, Home, Order, Portal, Signup
|
from core.views import Billing, Cancel, Home, Order, Portal, Signup
|
||||||
from core.views.callbacks import Callback
|
from core.views.callbacks import Callback
|
||||||
|
@ -44,5 +50,9 @@ urlpatterns = [
|
||||||
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("parts/search/", Search.as_view(), name="search"),
|
path("parts/search/", Search.as_view(), name="search"),
|
||||||
|
path("modal/info/", ThresholdInfoModal.as_view(), name="modal_info"),
|
||||||
|
path("api/chans/", ThresholdChans.as_view(), name="chans"),
|
||||||
|
path("api/users/", ThresholdUsers.as_view(), name="users"),
|
||||||
|
path("api/online/", ThresholdOnline.as_view(), name="online"),
|
||||||
path("api/search/", APISearch.as_view(), name="api_search"),
|
path("api/search/", APISearch.as_view(), name="api_search"),
|
||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.http import HttpResponse, JsonResponse
|
||||||
|
from django.shortcuts import render
|
||||||
|
from rest_framework.parsers import FormParser
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from core.lib.threshold import annotate_online, get_chans, get_users
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ThresholdChans(LoginRequiredMixin, APIView):
|
||||||
|
parser_classes = [FormParser]
|
||||||
|
plan_name = "drilldown"
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
if not request.user.has_plan(self.plan_name):
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "net" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "query" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
net = request.data["net"]
|
||||||
|
query = request.data["query"]
|
||||||
|
channels = get_chans(net, [query])
|
||||||
|
if not channels:
|
||||||
|
return HttpResponse("")
|
||||||
|
channels_human = ", ".join(channels)
|
||||||
|
return HttpResponse(channels_human)
|
||||||
|
|
||||||
|
|
||||||
|
class ThresholdUsers(LoginRequiredMixin, APIView):
|
||||||
|
parser_classes = [FormParser]
|
||||||
|
plan_name = "drilldown"
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
if not request.user.has_plan(self.plan_name):
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "net" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "query" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
net = request.data["net"]
|
||||||
|
query = request.data["query"]
|
||||||
|
users = get_users(net, [query])
|
||||||
|
if not users:
|
||||||
|
return HttpResponse("")
|
||||||
|
users_human = ", ".join(users)
|
||||||
|
return HttpResponse(users_human)
|
||||||
|
|
||||||
|
|
||||||
|
class ThresholdOnline(LoginRequiredMixin, APIView):
|
||||||
|
parser_classes = [FormParser]
|
||||||
|
plan_name = "drilldown"
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
if not request.user.has_plan(self.plan_name):
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "net" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "query" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
net = request.data["net"]
|
||||||
|
query = request.data["query"]
|
||||||
|
online_info = annotate_online(net, query)
|
||||||
|
return JsonResponse(online_info)
|
||||||
|
|
||||||
|
|
||||||
|
class ThresholdInfoModal(LoginRequiredMixin, APIView):
|
||||||
|
parser_classes = [FormParser]
|
||||||
|
plan_name = "drilldown"
|
||||||
|
template_name = "modals/info.html"
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
if not request.user.has_plan(self.plan_name):
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "net" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "nick" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
if "channel" not in request.data:
|
||||||
|
return JsonResponse({"success": False})
|
||||||
|
net = request.data["net"]
|
||||||
|
nick = request.data["nick"]
|
||||||
|
channel = request.data["channel"]
|
||||||
|
channels = get_chans(net, [nick])
|
||||||
|
users = get_users(net, [channel])
|
||||||
|
if channels:
|
||||||
|
inter_users = get_users(net, channels)
|
||||||
|
else:
|
||||||
|
inter_users = []
|
||||||
|
if users:
|
||||||
|
inter_chans = get_chans(net, users)
|
||||||
|
else:
|
||||||
|
inter_chans = []
|
||||||
|
context = {
|
||||||
|
"nick": nick,
|
||||||
|
"channel": channel,
|
||||||
|
"chans": channels,
|
||||||
|
"users": users,
|
||||||
|
"inter_chans": inter_chans,
|
||||||
|
"inter_users": inter_users,
|
||||||
|
}
|
||||||
|
return render(request, self.template_name, context)
|
|
@ -0,0 +1,76 @@
|
||||||
|
import logging
|
||||||
|
from json import dumps
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from django.conf import settings
|
||||||
|
from requests.exceptions import JSONDecodeError
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def escape(obj):
|
||||||
|
chars = ["[", "]", "^", "-", "*", "?"]
|
||||||
|
if isinstance(obj, str):
|
||||||
|
obj = obj.replace("\\", "\\\\")
|
||||||
|
for i in chars:
|
||||||
|
obj = obj.replace(i, "\\" + i)
|
||||||
|
elif isinstance(obj, list):
|
||||||
|
for i in obj:
|
||||||
|
i = escape(i)
|
||||||
|
elif isinstance(obj, dict):
|
||||||
|
for key in obj:
|
||||||
|
obj[key] = escape(obj[key])
|
||||||
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def threshold_request(url, data):
|
||||||
|
headers = {
|
||||||
|
"ApiKey": settings.THRESHOLD_API_KEY,
|
||||||
|
"Token": settings.THRESHOLD_API_TOKEN,
|
||||||
|
}
|
||||||
|
for key in data:
|
||||||
|
data[key] = escape(data[key])
|
||||||
|
r = requests.post(
|
||||||
|
f"{settings.THRESHOLD_ENDPOINT}/{url}/", data=dumps(data), headers=headers
|
||||||
|
)
|
||||||
|
if not r.headers.get("Counter") == settings.THRESHOLD_API_COUNTER:
|
||||||
|
logger.error(
|
||||||
|
(
|
||||||
|
f"Threshold API counter mismatch: "
|
||||||
|
f"{r.headers.get('Counter')} != "
|
||||||
|
f"{settings.THRESHOLD_API_COUNTER}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
response = r.json()
|
||||||
|
except JSONDecodeError:
|
||||||
|
logging.error(f"Invalid JSON response: {r.text}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def get_chans(net, query):
|
||||||
|
url = "chans"
|
||||||
|
payload = {"net": net, "query": query}
|
||||||
|
channels = threshold_request(url, payload)
|
||||||
|
if not channels:
|
||||||
|
return []
|
||||||
|
return channels["chans"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_users(net, query):
|
||||||
|
url = "users"
|
||||||
|
payload = {"net": net, "query": query}
|
||||||
|
users = threshold_request(url, payload)
|
||||||
|
if not users:
|
||||||
|
return []
|
||||||
|
return users["users"]
|
||||||
|
|
||||||
|
|
||||||
|
def annotate_online(net, query):
|
||||||
|
url = "online"
|
||||||
|
payload = {"net": net, "query": query}
|
||||||
|
online_info = threshold_request(url, payload)
|
||||||
|
if not online_info:
|
||||||
|
return {}
|
||||||
|
return online_info
|
|
@ -9,6 +9,7 @@
|
||||||
<title>Pathogen - {{ request.path_info }}</title>
|
<title>Pathogen - {{ request.path_info }}</title>
|
||||||
<link rel="shortcut icon" href="{% static 'favicon.ico' %}">
|
<link rel="shortcut icon" href="{% static 'favicon.ico' %}">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@creativebulma/bulma-tooltip@1.2.0/dist/bulma-tooltip.min.css">
|
||||||
<link rel="stylesheet" href="https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css" />
|
<link rel="stylesheet" href="https://site-assets.fontawesome.com/releases/v6.1.1/css/all.css" />
|
||||||
<script src="https://unpkg.com/htmx.org@1.8.0" integrity="sha384-cZuAZ+ZbwkNRnrKi05G/fjBX+azI9DNOkNYysZ0I/X5ZFgsmMiBXgDZof30F5ofc" crossorigin="anonymous"></script>
|
<script src="https://unpkg.com/htmx.org@1.8.0" integrity="sha384-cZuAZ+ZbwkNRnrKi05G/fjBX+azI9DNOkNYysZ0I/X5ZFgsmMiBXgDZof30F5ofc" crossorigin="anonymous"></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
<script>
|
||||||
|
var modal = document.querySelector('.modal'); // assuming you have only 1
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
modal.querySelector('.modal-background').addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
modal.classList.remove('is-active');
|
||||||
|
html.classList.remove('is-clipped');
|
||||||
|
});
|
||||||
|
modal.querySelector('.modal-close').addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
modal.classList.remove('is-active');
|
||||||
|
html.classList.remove('is-clipped');
|
||||||
|
});
|
||||||
|
|
||||||
|
var TABS = [...document.querySelectorAll('#tabs li')];
|
||||||
|
var CONTENT = [...document.querySelectorAll('#tab-content div')];
|
||||||
|
var ACTIVE_CLASS = 'is-active';
|
||||||
|
initTabs();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
#tab-content div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tab-content div.is-active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="modal is-active is-clipped">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="box">
|
||||||
|
<div class="tabs is-toggle is-fullwidth" id="tabs">
|
||||||
|
<ul>
|
||||||
|
<li class="is-active" data-tab="1">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="fa-solid fa-user"></i></span>
|
||||||
|
<span>Channels</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-tab="2">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="fa-solid fa-hashtag"></i></span>
|
||||||
|
<span>Users</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-tab="3">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="fa-solid fa-people"></i></span>
|
||||||
|
<span>Intersection</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-tab="4">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="fa-solid fa-hashtag"></i></span>
|
||||||
|
<span>Intersection</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="tab-content">
|
||||||
|
<div class="is-active" data-content="1">
|
||||||
|
<h4 class="subtitle is-4">Channels {{ nick }} is on</h4>
|
||||||
|
{% for channel in chans %}
|
||||||
|
<a class="panel-block">
|
||||||
|
<span class="panel-icon">
|
||||||
|
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
{{ channel }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div data-content="2">
|
||||||
|
<h4 class="subtitle is-4">Users on {{ channel }}</h4>
|
||||||
|
{% for user in users %}
|
||||||
|
<a class="panel-block">
|
||||||
|
<span class="panel-icon">
|
||||||
|
<i class="fa-solid fa-user" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
{{ user }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div data-content="3">
|
||||||
|
<h4 class="subtitle is-4">Users sharing channels with {{ nick }}</h4>
|
||||||
|
{% for user in inter_users %}
|
||||||
|
<a class="panel-block">
|
||||||
|
<span class="panel-icon">
|
||||||
|
<i class="fa-solid fa-user" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
{{ user }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div data-content="4">
|
||||||
|
<h4 class="subtitle is-4">Channels sharing users with {{ channel }}</h4>
|
||||||
|
{% for channel in inter_chans %}
|
||||||
|
<a class="panel-block">
|
||||||
|
<span class="panel-icon">
|
||||||
|
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
{{ channel }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="modal-close is-large" aria-label="close"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -2,6 +2,38 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<script>
|
||||||
|
function initTabs() {
|
||||||
|
TABS.forEach((tab) => {
|
||||||
|
tab.addEventListener('click', (e) => {
|
||||||
|
let selected = tab.getAttribute('data-tab');
|
||||||
|
updateActiveTab(tab);
|
||||||
|
updateActiveContent(selected);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateActiveTab(selected) {
|
||||||
|
TABS.forEach((tab) => {
|
||||||
|
if (tab && tab.classList.contains(ACTIVE_CLASS)) {
|
||||||
|
tab.classList.remove(ACTIVE_CLASS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
selected.classList.add(ACTIVE_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateActiveContent(selected) {
|
||||||
|
CONTENT.forEach((item) => {
|
||||||
|
if (item && item.classList.contains(ACTIVE_CLASS)) {
|
||||||
|
item.classList.remove(ACTIVE_CLASS);
|
||||||
|
}
|
||||||
|
let data = item.getAttribute('data-content');
|
||||||
|
if (data === selected) {
|
||||||
|
item.classList.add(ACTIVE_CLASS);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -10,7 +42,7 @@
|
||||||
<div class="field-body">
|
<div class="field-body">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control is-expanded has-icons-left">
|
<div class="control is-expanded has-icons-left">
|
||||||
<input name="query" class="input" type="text" placeholder="Query">
|
<input name="query" class="input" type="text" placeholder="msg: science AND nick: BillNye AND channel: #science">
|
||||||
<span class="icon is-small is-left">
|
<span class="icon is-small is-left">
|
||||||
<i class="fas fa-magnifying-glass"></i>
|
<i class="fas fa-magnifying-glass"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -9,14 +9,21 @@
|
||||||
<script src="{% static 'chart.js' %}"></script>
|
<script src="{% static 'chart.js' %}"></script>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
<span class="tag is-success">Online</span>
|
||||||
|
<span class="tag is-danger">Offline</span>
|
||||||
|
<span class="tag is-warning">Unknown</span>
|
||||||
|
IRC: <i class="fa-solid fa-hashtag" aria-hidden="true"></i>
|
||||||
|
Discord: <i class="fa-brands fa-discord" aria-hidden="true"></i>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table class="table is-striped is-hoverable is-fullwidth">
|
<table class="table is-striped is-hoverable is-fullwidth">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>src</th>
|
||||||
<th>TS</th>
|
<th>TS</th>
|
||||||
<th>msg</th>
|
<th>msg</th>
|
||||||
<th>host</th>
|
<th>host</th>
|
||||||
<th>nick</th>
|
<th>nick</th>
|
||||||
|
<th>actions</th>
|
||||||
<th>channel</th>
|
<th>channel</th>
|
||||||
<th>net</th>
|
<th>net</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -25,10 +32,43 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in results %}
|
{% for item in results %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.ts }}</td>
|
<td>
|
||||||
|
{% if item.src == 'irc' %}
|
||||||
|
<i class="fa-solid fa-hashtag" aria-hidden="true"></i>
|
||||||
|
{% elif item.src == 'dis' %}
|
||||||
|
<i class="fa-brands fa-discord" aria-hidden="true"></i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p>{{ item.date }}</p>
|
||||||
|
<p>{{ item.time }}</p>
|
||||||
|
</td>
|
||||||
<td>{{ item.msg }}</td>
|
<td>{{ item.msg }}</td>
|
||||||
<td>{{ item.host }}</td>
|
<td>{{ item.host }}</td>
|
||||||
<td>{{ item.nick }}</td>
|
<td>
|
||||||
|
{% if item.online is True %}
|
||||||
|
<span class="tag is-success">{{ item.nick }}</span>
|
||||||
|
{% elif item.online is False %}
|
||||||
|
<span class="tag is-danger">{{ item.nick }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="tag is-warning">{{ item.nick }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if item.src == 'irc' %}
|
||||||
|
<button
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-post="{% url 'modal_info' %}"
|
||||||
|
hx-vals='{"net": "{{ item.net }}", "nick": "{{ item.nick }}", "channel": "{{ item.channel }}"}'
|
||||||
|
hx-target="#modals-here"
|
||||||
|
hx-trigger="click"
|
||||||
|
class="btn btn-primary"
|
||||||
|
_="on htmx:afterOnLoad wait 10ms then add .show to #modal then add .show to #modal-backdrop">
|
||||||
|
Information
|
||||||
|
</button>
|
||||||
|
<div id="modals-here"></div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ item.channel }}</td>
|
<td>{{ item.channel }}</td>
|
||||||
<td>{{ item.net }}</td>
|
<td>{{ item.net }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.shortcuts import render
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
|
||||||
from core.lib.opensearch import initialise_opensearch, run_main_query
|
from core.lib.opensearch import initialise_opensearch, run_main_query
|
||||||
|
from core.lib.threshold import annotate_online
|
||||||
|
|
||||||
client = initialise_opensearch()
|
client = initialise_opensearch()
|
||||||
|
|
||||||
|
@ -26,7 +27,30 @@ def query_results(request, post_params, api=False):
|
||||||
if "hits" in results.keys():
|
if "hits" in results.keys():
|
||||||
if "hits" in results["hits"]:
|
if "hits" in results["hits"]:
|
||||||
for item in results["hits"]["hits"]:
|
for item in results["hits"]["hits"]:
|
||||||
results_parsed.append(item["_source"])
|
element = item["_source"]
|
||||||
|
element["id"] = item["_id"]
|
||||||
|
ts = element["ts"]
|
||||||
|
ts_spl = ts.split("T")
|
||||||
|
date = ts_spl[0]
|
||||||
|
time = ts_spl[1]
|
||||||
|
element["date"] = date
|
||||||
|
element["time"] = time
|
||||||
|
results_parsed.append(element)
|
||||||
|
# Figure out items with net (not discord)
|
||||||
|
nets = set()
|
||||||
|
for x in results_parsed:
|
||||||
|
if "net" in x:
|
||||||
|
nets.add(x["net"])
|
||||||
|
|
||||||
|
# Annotate the online attribute from Threshold
|
||||||
|
for net in nets:
|
||||||
|
online_info = annotate_online(
|
||||||
|
net, [x["nick"] for x in results_parsed if x["src"] == "irc"]
|
||||||
|
)
|
||||||
|
for item in results_parsed:
|
||||||
|
if item["nick"] in online_info:
|
||||||
|
item["online"] = online_info[item["nick"]]
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"query": query,
|
"query": query,
|
||||||
"results": results_parsed,
|
"results": results_parsed,
|
||||||
|
|
Loading…
Reference in New Issue