Implement more UI elements
parent
2c62c343b8
commit
b2a1c42f3d
@ -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
|
@ -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>
|
Loading…
Reference in New Issue