Begin implementing smarter WM system for multi-type objects
This commit is contained in:
parent
fd4cecee05
commit
ac3a57a2e8
|
@ -320,8 +320,18 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{% block content %}
|
{% block content_wrapper %}
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
<div id="modals-here">
|
||||||
|
</div>
|
||||||
|
<div id="windows-here">
|
||||||
|
</div>
|
||||||
|
<div id="widgets-here" style="display: none;">
|
||||||
|
{% block widgets %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,48 +1,152 @@
|
||||||
{% extends "base.html" %}
|
{% extends 'base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load joinsep %}
|
||||||
|
{% block outer_content %}
|
||||||
|
{% if params.modal == 'context' %}
|
||||||
|
<div
|
||||||
|
style="display: none;"
|
||||||
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
|
hx-post="{% url 'modal_context' %}"
|
||||||
|
hx-vals='{"net": "{{ params.net|escapejs }}",
|
||||||
|
"num": "{{ params.num|escapejs }}",
|
||||||
|
"source": "{{ params.source|escapejs }}",
|
||||||
|
"channel": "{{ params.channel|escapejs }}",
|
||||||
|
"time": "{{ params.time|escapejs }}",
|
||||||
|
"date": "{{ params.date|escapejs }}",
|
||||||
|
"index": "{{ params.index }}",
|
||||||
|
"type": "{{ params.type|escapejs }}",
|
||||||
|
"mtype": "{{ params.mtype|escapejs }}",
|
||||||
|
"nick": "{{ params.nick|escapejs }}"}'
|
||||||
|
hx-target="#modals-here"
|
||||||
|
hx-trigger="load">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<script src="{% static 'js/chart.js' %}"></script>
|
||||||
|
<script src="{% static 'tabs.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
function setupTags() {
|
||||||
|
var inputTags = document.getElementById('tags');
|
||||||
|
new BulmaTagsInput(inputTags);
|
||||||
|
|
||||||
{% block content %}
|
inputTags.BulmaTagsInput().on('before.add', function(item) {
|
||||||
<div class="block">
|
if (item.includes(": ")) {
|
||||||
{% for block in blocks %}
|
var spl = item.split(": ");
|
||||||
{% if block.title is not None %}
|
} else {
|
||||||
<h1 class="title">{{ block.title }}</h1>
|
var spl = item.split(":");
|
||||||
{% endif %}
|
}
|
||||||
<div class="box">
|
var field = spl[0];
|
||||||
<div class="columns">
|
try {
|
||||||
{% if block.column1 is not None %}
|
var value = JSON.parse(spl[1]);
|
||||||
<div class="column">
|
} catch {
|
||||||
{{ block.column1 }}
|
var value = spl[1];
|
||||||
</div>
|
}
|
||||||
{% endif %}
|
return `${field}: ${value}`;
|
||||||
{% if block.column2 is not None %}
|
});
|
||||||
<div class="column">
|
inputTags.BulmaTagsInput().on('after.remove', function(item) {
|
||||||
{{ block.column2 }}
|
var spl = item.split(": ");
|
||||||
</div>
|
var field = spl[0];
|
||||||
{% endif %}
|
var value = spl[1].trim();
|
||||||
{% if block.column3 is not None %}
|
});
|
||||||
<div class="column">
|
}
|
||||||
{{ block.column3 }}
|
function populateSearch(field, value) {
|
||||||
</div>
|
var inputTags = document.getElementById('tags');
|
||||||
{% endif %}
|
inputTags.BulmaTagsInput().add(field+": "+value);
|
||||||
</div>
|
//htmx.trigger("#search", "click");
|
||||||
<div class="columns">
|
}
|
||||||
{% if block.image1 is not None %}
|
</script>
|
||||||
<div class="column">
|
|
||||||
<img src="{% static block.image1 %}">
|
<div class="grid-stack" id="grid-stack-main">
|
||||||
</div>
|
<div class="grid-stack-item" gs-w="7" gs-h="10" gs-y="0" gs-x="1">
|
||||||
{% endif %}
|
<div class="grid-stack-item-content">
|
||||||
{% if block.image2 is not None %}
|
<nav class="panel">
|
||||||
<div class="column">
|
<p class="panel-heading" style="padding: .2em; line-height: .5em;">
|
||||||
<img src="{% static block.image2 %}">
|
<i class="fa-solid fa-arrows-up-down-left-right has-text-grey-light"></i>
|
||||||
</div>
|
Search
|
||||||
{% endif %}
|
</p>
|
||||||
{% if block.image3 is not None %}
|
<article class="panel-block is-active">
|
||||||
<div class="column">
|
{% include 'window-content/search.html' %}
|
||||||
<img src="{% static block.image3 %}">
|
</article>
|
||||||
</div>
|
</nav>
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var grid = GridStack.init({
|
||||||
|
cellHeight: 20,
|
||||||
|
cellWidth: 50,
|
||||||
|
cellHeightUnit: 'px',
|
||||||
|
auto: true,
|
||||||
|
float: true,
|
||||||
|
draggable: {handle: '.panel-heading', scroll: false, appendTo: 'body'},
|
||||||
|
removable: false,
|
||||||
|
animate: true,
|
||||||
|
});
|
||||||
|
// GridStack.init();
|
||||||
|
setupTags();
|
||||||
|
|
||||||
|
// a widget is ready to be loaded
|
||||||
|
document.addEventListener('load-widget', function(event) {
|
||||||
|
let container = htmx.find('#widget');
|
||||||
|
// get the scripts, they won't be run on the new element so we need to eval them
|
||||||
|
var scripts = htmx.findAll(container, "script");
|
||||||
|
let widgetelement = container.firstElementChild.cloneNode(true);
|
||||||
|
var new_id = widgetelement.id;
|
||||||
|
|
||||||
|
// check if there's an existing element like the one we want to swap
|
||||||
|
let grid_element = htmx.find('#grid-stack-main');
|
||||||
|
let existing_widget = htmx.find(grid_element, "#"+new_id);
|
||||||
|
|
||||||
|
// get the size and position attributes
|
||||||
|
if (existing_widget) {
|
||||||
|
let attrs = existing_widget.getAttributeNames();
|
||||||
|
for (let i = 0, len = attrs.length; i < len; i++) {
|
||||||
|
if (attrs[i].startsWith('gs-')) { // only target gridstack attributes
|
||||||
|
widgetelement.setAttribute(attrs[i], existing_widget.getAttribute(attrs[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the queue element
|
||||||
|
container.outerHTML = "";
|
||||||
|
|
||||||
|
// temporary workaround, other widgets can be duplicated, but not results
|
||||||
|
if (widgetelement.id == 'widget-results') {
|
||||||
|
grid.removeWidget("widget-results");
|
||||||
|
}
|
||||||
|
|
||||||
|
grid.addWidget(widgetelement);
|
||||||
|
// re-create the HTMX JS listeners, otherwise HTMX won't work inside the grid
|
||||||
|
htmx.process(widgetelement);
|
||||||
|
|
||||||
|
// update size when the widget is loaded
|
||||||
|
document.addEventListener('load-widget-results', function(evt) {
|
||||||
|
var added_widget = htmx.find(grid_element, '#widget-results');
|
||||||
|
var itemContent = htmx.find(added_widget, ".control");
|
||||||
|
var scrollheight = itemContent.scrollHeight+80;
|
||||||
|
var verticalmargin = 0;
|
||||||
|
var cellheight = grid.opts.cellHeight;
|
||||||
|
var height = Math.ceil((scrollheight + verticalmargin) / (cellheight + verticalmargin));
|
||||||
|
var opts = {
|
||||||
|
h: height,
|
||||||
|
}
|
||||||
|
grid.update(
|
||||||
|
added_widget,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// run the JS scripts inside the added element again
|
||||||
|
// for instance, this will fix the dropdown
|
||||||
|
for (var i = 0; i < scripts.length; i++) {
|
||||||
|
eval(scripts[i].innerHTML);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
{% block widgets %}
|
||||||
|
{% if table %}
|
||||||
|
{% include 'partials/results_load.html' %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1 @@
|
||||||
|
<button class="modal-close is-large" aria-label="close"></button>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<i
|
||||||
|
class="fa-solid fa-xmark has-text-grey-light float-right"
|
||||||
|
onclick='grid.removeWidget("widget-{{ unique }}");'></i>
|
|
@ -0,0 +1,3 @@
|
||||||
|
<i
|
||||||
|
class="fa-solid fa-xmark has-text-grey-light float-right"
|
||||||
|
data-script="on click remove the closest <nav/>"></i>
|
|
@ -1,20 +1,10 @@
|
||||||
{% extends 'wm/widget.html' %}
|
{% extends 'wm/widget.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block widget_options %}
|
|
||||||
gs-w="10" gs-h="1" gs-y="10" gs-x="1"
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block heading %}
|
{% block heading %}
|
||||||
Results
|
Results
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block close_button %}
|
|
||||||
<i
|
|
||||||
class="fa-solid fa-xmark has-text-grey-light float-right"
|
|
||||||
onclick='grid.removeWidget("drilldown-widget-{{ unique }}"); //grid.compact();'></i>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block panel_content %}
|
{% block panel_content %}
|
||||||
{% include 'partials/notify.html' %}
|
{% include 'partials/notify.html' %}
|
||||||
<script src="{% static 'js/column-shifter.js' %}"></script>
|
<script src="{% static 'js/column-shifter.js' %}"></script>
|
||||||
|
@ -38,6 +28,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% include 'ui/drilldown/table_results_partial.html' %}
|
{% include 'partials/results_table.html' %}
|
||||||
{% include 'ui/drilldown/sentiment_partial.html' %}
|
{% include 'partials/sentiment_chart.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -141,10 +141,6 @@
|
||||||
<i class="fa-solid fa-file-slash"></i>
|
<i class="fa-solid fa-file-slash"></i>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
{% elif column.name == 'tokens' %}
|
|
||||||
<td class="{{ column.name }} wrap" style="max-width: 10em">
|
|
||||||
{{ cell|joinsep:',' }}
|
|
||||||
</td>
|
|
||||||
{% elif column.name == 'src' %}
|
{% elif column.name == 'src' %}
|
||||||
<td class="{{ column.name }}">
|
<td class="{{ column.name }}">
|
||||||
<a
|
<a
|
||||||
|
@ -301,7 +297,7 @@
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
||||||
hx-post="{% url 'modal_drilldown' type='window' %}"
|
hx-post="{% url 'modal_drilldown' type='window' %}"
|
||||||
hx-vals='{"net": "{{ row.cells.net }}", "nick": "{{ row.cells.nick }}", "channel": "{{ row.cells.channel }}"}'
|
hx-vals='{"net": "{{ row.cells.net }}", "nick": "{{ row.cells.nick }}", "channel": "{{ row.cells.channel }}"}'
|
||||||
hx-target="#items-here"
|
hx-target="#windows-here"
|
||||||
hx-swap="afterend"
|
hx-swap="afterend"
|
||||||
hx-trigger="click"
|
hx-trigger="click"
|
||||||
class="has-text-black">
|
class="has-text-black">
|
||||||
|
@ -364,25 +360,17 @@
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
{% elif column.name|slice:":6" == "words_" %}
|
{% elif column.name == "tokens" %}
|
||||||
<td class="{{ column.name }}">
|
<td class="{{ column.name }}">
|
||||||
{% if cell.0.1|length == 0 %}
|
<div class="tags">
|
||||||
<a
|
{% for word in cell %}
|
||||||
class="tag is-info"
|
<a
|
||||||
onclick="populateSearch('{{ column.name }}', '{{ cell }}')">
|
class="tag is-info"
|
||||||
{{ cell }}
|
onclick="populateSearch('{{ column.name }}', '{{ word }}')">
|
||||||
</a>
|
{{ word }}
|
||||||
{% else %}
|
</a>
|
||||||
<div class="tags">
|
{% endfor %}
|
||||||
{% for word in cell %}
|
</div>
|
||||||
<a
|
|
||||||
class="tag is-info"
|
|
||||||
onclick="populateSearch('{{ column.name }}', '{{ word }}')">
|
|
||||||
{{ word }}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="{{ column.name }}">
|
<td class="{{ column.name }}">
|
|
@ -1,163 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
{% load static %}
|
|
||||||
{% load joinsep %}
|
|
||||||
{% block outer_content %}
|
|
||||||
{% if params.modal == 'context' %}
|
|
||||||
<div
|
|
||||||
style="display: none;"
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-post="{% url 'modal_context' %}"
|
|
||||||
hx-vals='{"net": "{{ params.net|escapejs }}",
|
|
||||||
"num": "{{ params.num|escapejs }}",
|
|
||||||
"source": "{{ params.source|escapejs }}",
|
|
||||||
"channel": "{{ params.channel|escapejs }}",
|
|
||||||
"time": "{{ params.time|escapejs }}",
|
|
||||||
"date": "{{ params.date|escapejs }}",
|
|
||||||
"index": "{{ params.index }}",
|
|
||||||
"type": "{{ params.type|escapejs }}",
|
|
||||||
"mtype": "{{ params.mtype|escapejs }}",
|
|
||||||
"nick": "{{ params.nick|escapejs }}"}'
|
|
||||||
hx-target="#modals-here"
|
|
||||||
hx-trigger="load">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<script src="{% static 'js/chart.js' %}"></script>
|
|
||||||
<script src="{% static 'tabs.js' %}"></script>
|
|
||||||
<script>
|
|
||||||
function setupTags() {
|
|
||||||
var inputTags = document.getElementById('tags');
|
|
||||||
new BulmaTagsInput(inputTags);
|
|
||||||
|
|
||||||
inputTags.BulmaTagsInput().on('before.add', function(item) {
|
|
||||||
if (item.includes(": ")) {
|
|
||||||
var spl = item.split(": ");
|
|
||||||
} else {
|
|
||||||
var spl = item.split(":");
|
|
||||||
}
|
|
||||||
var field = spl[0];
|
|
||||||
try {
|
|
||||||
var value = JSON.parse(spl[1]);
|
|
||||||
} catch {
|
|
||||||
var value = spl[1];
|
|
||||||
}
|
|
||||||
return `${field}: ${value}`;
|
|
||||||
});
|
|
||||||
inputTags.BulmaTagsInput().on('after.remove', function(item) {
|
|
||||||
var spl = item.split(": ");
|
|
||||||
var field = spl[0];
|
|
||||||
var value = spl[1].trim();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function populateSearch(field, value) {
|
|
||||||
var inputTags = document.getElementById('tags');
|
|
||||||
inputTags.BulmaTagsInput().add(field+": "+value);
|
|
||||||
//htmx.trigger("#search", "click");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="grid-stack" id="grid-stack-main">
|
|
||||||
<div class="grid-stack-item" gs-w="7" gs-h="10" gs-y="0" gs-x="1">
|
|
||||||
<div class="grid-stack-item-content">
|
|
||||||
<nav class="panel">
|
|
||||||
<p class="panel-heading" style="padding: .2em; line-height: .5em;">
|
|
||||||
<i class="fa-solid fa-arrows-up-down-left-right has-text-grey-light"></i>
|
|
||||||
Search
|
|
||||||
</p>
|
|
||||||
<article class="panel-block is-active">
|
|
||||||
{% include 'ui/drilldown/search_partial.html' %}
|
|
||||||
</article>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var grid = GridStack.init({
|
|
||||||
cellHeight: 20,
|
|
||||||
cellWidth: 50,
|
|
||||||
cellHeightUnit: 'px',
|
|
||||||
auto: true,
|
|
||||||
float: true,
|
|
||||||
draggable: {handle: '.panel-heading', scroll: false, appendTo: 'body'},
|
|
||||||
removable: false,
|
|
||||||
animate: true,
|
|
||||||
});
|
|
||||||
// GridStack.init();
|
|
||||||
setupTags();
|
|
||||||
|
|
||||||
// a widget is ready to be loaded
|
|
||||||
document.addEventListener('load-widget', function(event) {
|
|
||||||
let container = htmx.find('#drilldown-widget');
|
|
||||||
// get the scripts, they won't be run on the new element so we need to eval them
|
|
||||||
var scripts = htmx.findAll(container, "script");
|
|
||||||
let widgetelement = container.firstElementChild.cloneNode(true);
|
|
||||||
|
|
||||||
// check if there's an existing element like the one we want to swap
|
|
||||||
let grid_element = htmx.find('#grid-stack-main');
|
|
||||||
let existing_widget = htmx.find(grid_element, '#drilldown-widget-results');
|
|
||||||
|
|
||||||
// get the size and position attributes
|
|
||||||
if (existing_widget) {
|
|
||||||
let attrs = existing_widget.getAttributeNames();
|
|
||||||
for (let i = 0, len = attrs.length; i < len; i++) {
|
|
||||||
if (attrs[i].startsWith('gs-')) { // only target gridstack attributes
|
|
||||||
widgetelement.setAttribute(attrs[i], existing_widget.getAttribute(attrs[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the queue element
|
|
||||||
container.outerHTML = "";
|
|
||||||
|
|
||||||
// temporary workaround, other widgets can be duplicated, but not results
|
|
||||||
if (widgetelement.id == 'drilldown-widget-results') {
|
|
||||||
grid.removeWidget("drilldown-widget-{{ unique }}");
|
|
||||||
}
|
|
||||||
|
|
||||||
grid.addWidget(widgetelement);
|
|
||||||
// re-create the HTMX JS listeners, otherwise HTMX won't work inside the grid
|
|
||||||
htmx.process(widgetelement);
|
|
||||||
|
|
||||||
// update size when the widget is loaded
|
|
||||||
document.addEventListener('load-widget-results', function(evt) {
|
|
||||||
var added_widget = htmx.find(grid_element, '#drilldown-widget-results');
|
|
||||||
console.log(added_widget);
|
|
||||||
var itemContent = htmx.find(added_widget, ".control");
|
|
||||||
console.log(itemContent);
|
|
||||||
var scrollheight = itemContent.scrollHeight+80;
|
|
||||||
var verticalmargin = 0;
|
|
||||||
var cellheight = grid.opts.cellHeight;
|
|
||||||
var height = Math.ceil((scrollheight + verticalmargin) / (cellheight + verticalmargin));
|
|
||||||
var opts = {
|
|
||||||
h: height,
|
|
||||||
}
|
|
||||||
grid.update(
|
|
||||||
added_widget,
|
|
||||||
opts
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// run the JS scripts inside the added element again
|
|
||||||
// for instance, this will fix the dropdown
|
|
||||||
for (var i = 0; i < scripts.length; i++) {
|
|
||||||
eval(scripts[i].innerHTML);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="modals-here">
|
|
||||||
</div>
|
|
||||||
<div id="items-here">
|
|
||||||
</div>
|
|
||||||
<div id="widgets-here" style="display: none;">
|
|
||||||
</div>
|
|
||||||
<div id="results" style="display: none;">
|
|
||||||
{% if table %}
|
|
||||||
{% include 'widgets/table_results.html' %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,122 +0,0 @@
|
||||||
{% load index %}
|
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
<script src="{% static 'modal.js' %}"></script>
|
|
||||||
<script>
|
|
||||||
document.addEventListener("restore-modal-scroll", function(event) {
|
|
||||||
var modalContent = document.getElementsByClassName("modal-content")[0];
|
|
||||||
var maxScroll = modalContent.scrollHeight - modalContent.offsetHeight;
|
|
||||||
var scrollpos = localStorage.getItem('scrollpos_modal_content');
|
|
||||||
if (scrollpos == 'BOTTOM') {
|
|
||||||
modalContent.scrollTop = maxScroll;
|
|
||||||
} else if (scrollpos) {
|
|
||||||
modalContent.scrollTop = scrollpos;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("htmx:beforeSwap", function(event) {
|
|
||||||
var modalContent = document.getElementsByClassName("modal-content")[0];
|
|
||||||
var scrollpos = modalContent.scrollTop;
|
|
||||||
if(modalContent.scrollTop === (modalContent.scrollHeight - modalContent.offsetHeight)) {
|
|
||||||
localStorage.setItem('scrollpos_modal_content', 'BOTTOM');
|
|
||||||
} else {
|
|
||||||
localStorage.setItem('scrollpos_modal_content', scrollpos);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
#tab-content-{{ unique }} div {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tab-content-{{ unique }} div.is-active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div id="modal" class="modal is-active is-clipped">
|
|
||||||
<div class="modal-background"></div>
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="box">
|
|
||||||
{% include 'partials/notify.html' %}
|
|
||||||
<div class="tabs is-toggle is-fullwidth is-info" id="tabs-{{ unique }}">
|
|
||||||
<ul>
|
|
||||||
<li class="is-active" data-tab="1">
|
|
||||||
<a>
|
|
||||||
<span class="icon is-small"><i class="fa-solid fa-message-arrow-down"></i></span>
|
|
||||||
<span>Scrollback</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li data-tab="2">
|
|
||||||
<a>
|
|
||||||
<span class="icon is-small"><i class="fa-solid fa-messages"></i></span>
|
|
||||||
<span>Context</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li data-tab="3">
|
|
||||||
<a>
|
|
||||||
<span class="icon is-small"><i class="fa-solid fa-message"></i></span>
|
|
||||||
<span>Message</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li data-tab="4">
|
|
||||||
<a>
|
|
||||||
<span class="icon is-small"><i class="fa-solid fa-asterisk"></i></span>
|
|
||||||
<span>Info</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="tab-content-{{ unique }}">
|
|
||||||
<div class="is-active" data-content="1">
|
|
||||||
<h4 class="subtitle is-4">Scrollback of {{ channel }} on {{ net }}{{ num }}</h4>
|
|
||||||
{% include 'modals/context_table.html' %}
|
|
||||||
{% if user.is_superuser and src == 'irc' %}
|
|
||||||
<form method="PUT">
|
|
||||||
<article class="field has-addons">
|
|
||||||
<article class="control is-expanded has-icons-left">
|
|
||||||
<input id="context-input" name="msg" class="input" type="text" placeholder="Type your message here">
|
|
||||||
<span class="icon is-small is-left">
|
|
||||||
<i class="fas fa-magnifying-glass"></i>
|
|
||||||
</span>
|
|
||||||
</article>
|
|
||||||
<article class="control">
|
|
||||||
<article class="field">
|
|
||||||
<button
|
|
||||||
id="search"
|
|
||||||
class="button is-info is-fullwidth"
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-put="{% url 'threshold_irc_msg' net num %}"
|
|
||||||
hx-vals='{"channel": "{{ channel }}", "nick": "{{ nick }}"}'
|
|
||||||
hx-trigger="click"
|
|
||||||
hx-target="#context-input"
|
|
||||||
hx-swap="outerHTML">
|
|
||||||
Send
|
|
||||||
</button>
|
|
||||||
</article>
|
|
||||||
</article>
|
|
||||||
</article>
|
|
||||||
</form>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div data-content="2">
|
|
||||||
<h4 class="subtitle is-4">Scrollback of {{ channel }} on {{ net }}{{ num }} around {{ ts }}</h4>
|
|
||||||
Context
|
|
||||||
</div>
|
|
||||||
<div data-content="3">
|
|
||||||
<h4 class="subtitle is-4">Message details</h4>
|
|
||||||
Message deetails
|
|
||||||
</div>
|
|
||||||
<div data-content="4">
|
|
||||||
<h4 class="subtitle is-4">Information about {{ channel }} on {{ net }}{{ num }}</h4>
|
|
||||||
info
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>initTabs("{{ unique }}");</script>
|
|
||||||
<button class="modal-close is-large" aria-label="close"></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -1,177 +0,0 @@
|
||||||
<article class="table-container" id="modal-context-table">
|
|
||||||
<table class="table is-fullwidth">
|
|
||||||
<thead>
|
|
||||||
<th></th>
|
|
||||||
<th></th>
|
|
||||||
<th></th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for item in object_list %}
|
|
||||||
{% if item.type == 'control' %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td>
|
|
||||||
<span class="icon has-text-grey" data-tooltip="Hidden">
|
|
||||||
<i class="fa-solid fa-file-slash"></i>
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<p class="has-text-grey">Hidden {{ item.hidden }} similar result{% if item.hidden > 1%}s{% endif %}</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ item.time }}</td>
|
|
||||||
<td>
|
|
||||||
{% if item.type != 'znc' and item.type != 'self' and query is not True %}
|
|
||||||
<article class="nowrap-parent">
|
|
||||||
<article class="nowrap-child">
|
|
||||||
{% if item.type == 'msg' %}
|
|
||||||
<span class="icon" data-tooltip="Message">
|
|
||||||
<i class="fa-solid fa-message"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'join' %}
|
|
||||||
<span class="icon" data-tooltip="Join">
|
|
||||||
<i class="fa-solid fa-person-to-portal"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'part' %}
|
|
||||||
<span class="icon" data-tooltip="Part">
|
|
||||||
<i class="fa-solid fa-person-from-portal"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'quit' %}
|
|
||||||
<span class="icon" data-tooltip="Quit">
|
|
||||||
<i class="fa-solid fa-circle-xmark"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'kick' %}
|
|
||||||
<span class="icon" data-tooltip="Kick">
|
|
||||||
<i class="fa-solid fa-user-slash"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'nick' %}
|
|
||||||
<span class="icon" data-tooltip="Nick">
|
|
||||||
<i class="fa-solid fa-signature"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'mode' %}
|
|
||||||
<span class="icon" data-tooltip="Mode">
|
|
||||||
<i class="fa-solid fa-gear"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'action' %}
|
|
||||||
<span class="icon" data-tooltip="Action">
|
|
||||||
<i class="fa-solid fa-exclamation"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'notice' %}
|
|
||||||
<span class="icon" data-tooltip="Notice">
|
|
||||||
<i class="fa-solid fa-message-code"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'conn' %}
|
|
||||||
<span class="icon" data-tooltip="Connection">
|
|
||||||
<i class="fa-solid fa-cloud-exclamation"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'znc' %}
|
|
||||||
<span class="icon" data-tooltip="ZNC">
|
|
||||||
<i class="fa-brands fa-unity"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'query' %}
|
|
||||||
<span class="icon" data-tooltip="Query">
|
|
||||||
<i class="fa-solid fa-message"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'highlight' %}
|
|
||||||
<span class="icon" data-tooltip="Highlight">
|
|
||||||
<i class="fa-solid fa-exclamation"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'who' %}
|
|
||||||
<span class="icon" data-tooltip="Who">
|
|
||||||
<i class="fa-solid fa-passport"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'topic' %}
|
|
||||||
<span class="icon" data-tooltip="Topic">
|
|
||||||
<i class="fa-solid fa-sign"></i>
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
{{ item.type }}
|
|
||||||
{% endif %}
|
|
||||||
{% 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>
|
|
||||||
{% 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>
|
|
||||||
{% else %}
|
|
||||||
<span class="icon has-text-warning has-tooltip-warning" data-tooltip="Unknown">
|
|
||||||
<i class="fa-solid fa-circle"></i>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if item.src == 'irc' %}
|
|
||||||
<a
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-post="{% url 'modal_drilldown' %}"
|
|
||||||
hx-vals='{"net": "{{ item.net|escapejs }}", "nick": "{{ item.nick|escapejs }}", "channel": "{{ item.channel|escapejs }}"}'
|
|
||||||
hx-target="#modals-here"
|
|
||||||
hx-trigger="click"
|
|
||||||
class="has-text-black">
|
|
||||||
<span class="icon" data-tooltip="Open drilldown modal">
|
|
||||||
<i class="fa-solid fa-album"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</article>
|
|
||||||
<a class="nowrap-child has-text-link is-underlined" onclick="populateSearch('nick', '{{ item.nick|escapejs }}')">
|
|
||||||
{{ item.nick }}
|
|
||||||
</a>
|
|
||||||
{% if item.num_chans != '—' %}
|
|
||||||
<article class="nowrap-child">
|
|
||||||
<span class="tag">
|
|
||||||
{{ item.num_chans }}
|
|
||||||
</span>
|
|
||||||
</article>
|
|
||||||
{% endif %}
|
|
||||||
</article>
|
|
||||||
{% endif %}
|
|
||||||
{% if item.type == 'self' %}
|
|
||||||
<span class="icon has-text-primary" data-tooltip="You">
|
|
||||||
<i class="fa-solid fa-message-check"></i>
|
|
||||||
</span>
|
|
||||||
{% elif item.type == 'znc' %}
|
|
||||||
<span class="icon has-text-info" data-tooltip="ZNC">
|
|
||||||
<i class="fa-brands fa-unity"></i>
|
|
||||||
</span>
|
|
||||||
{% elif query %}
|
|
||||||
<span class="icon has-text-info" data-tooltip="Auth">
|
|
||||||
<i class="fa-solid fa-passport"></i>
|
|
||||||
</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="wrap">{{ item.msg }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% if object_list %}
|
|
||||||
<div
|
|
||||||
class="modal-refresh"
|
|
||||||
style="display: none;"
|
|
||||||
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
|
|
||||||
hx-post="{% url 'modal_context_table' %}"
|
|
||||||
hx-vals='{"net": "{{ net }}",
|
|
||||||
"num": "{{ num }}",
|
|
||||||
"src": "{{ src }}",
|
|
||||||
"channel": "{{ channel }}",
|
|
||||||
"time": "{{ time }}",
|
|
||||||
"date": "{{ date }}",
|
|
||||||
"index": "{{ index }}",
|
|
||||||
"type": "{{ type }}",
|
|
||||||
"mtype": "{{ mtype }}",
|
|
||||||
"nick": "{{ nick }}",
|
|
||||||
"dedup": "{{ params.dedup }}"}'
|
|
||||||
hx-target="#modal-context-table"
|
|
||||||
hx-trigger="every 5s">
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var modal_event = new Event('restore-modal-scroll');
|
|
||||||
document.dispatchEvent(modal_event);
|
|
||||||
</script>
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% include 'partials/notify.html' %}
|
||||||
|
<script src="{% static 'js/column-shifter.js' %}"></script>
|
||||||
|
{% if cache is not None %}
|
||||||
|
<span class="icon has-tooltip-bottom" data-tooltip="Cached">
|
||||||
|
<i class="fa-solid fa-database"></i>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
fetched {{ table.data|length }} hits in {{ took }}ms
|
||||||
|
|
||||||
|
{% if exemption is not None %}
|
||||||
|
<span class="icon has-tooltip-bottom" data-tooltip="God mode">
|
||||||
|
<i class="fa-solid fa-book-bible"></i>
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
{% if redacted is not None %}
|
||||||
|
<span class="icon has-tooltip-bottom" data-tooltip="{{ redacted }} redacted">
|
||||||
|
<i class="fa-solid fa-mask"></i>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% include 'partials/results_table.html' %}
|
||||||
|
{% include 'partials/sentiment_chart.html' %}
|
|
@ -1,6 +1,6 @@
|
||||||
<form class="skipEmptyFields" method="POST" hx-post="{% url 'search' %}"
|
<form class="skipEmptyFields" method="POST" hx-post="{% url 'search' %}"
|
||||||
hx-trigger="change"
|
hx-trigger="change"
|
||||||
hx-target="#results"
|
hx-target="#widgets-here"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
hx-indicator="#spinner">
|
hx-indicator="#spinner">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
<input
|
<input
|
||||||
hx-post="{% url 'search' %}"
|
hx-post="{% url 'search' %}"
|
||||||
hx-trigger="keyup changed delay:200ms"
|
hx-trigger="keyup changed delay:200ms"
|
||||||
hx-target="#results"
|
hx-target="#widgets-here"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
name="query"
|
name="query"
|
||||||
value="{{ params.query }}"
|
value="{{ params.query }}"
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
class="button is-info is-fullwidth"
|
class="button is-info is-fullwidth"
|
||||||
hx-post="{% url 'search' %}"
|
hx-post="{% url 'search' %}"
|
||||||
hx-trigger="click"
|
hx-trigger="click"
|
||||||
hx-target="#results"
|
hx-target="#widgets-here"
|
||||||
hx-swap="innerHTML">
|
hx-swap="innerHTML">
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
|
@ -394,7 +394,7 @@
|
||||||
<input
|
<input
|
||||||
hx-trigger="change"
|
hx-trigger="change"
|
||||||
hx-post="{% url 'search' %}"
|
hx-post="{% url 'search' %}"
|
||||||
hx-target="#results"
|
hx-target="#widgets-here"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
id="tags"
|
id="tags"
|
||||||
class="input"
|
class="input"
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends 'wm/magnet.html' %}
|
{% extends 'wm/window.html' %}
|
||||||
|
|
||||||
{% block heading %}
|
{% block heading %}
|
||||||
Drilldown
|
Drilldown
|
||||||
|
|
|
@ -12,8 +12,9 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
{% block modal_content %}
|
{% block modal_content %}
|
||||||
|
{% include window_content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<button class="modal-close is-large" aria-label="close"></button>
|
{% include 'partials/close-modal.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -0,0 +1,6 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% include window_content %}
|
||||||
|
{% endblock %}
|
|
@ -3,9 +3,7 @@
|
||||||
<p class="panel-heading" style="padding: .2em; line-height: .5em;">
|
<p class="panel-heading" style="padding: .2em; line-height: .5em;">
|
||||||
<i class="fa-solid fa-arrows-up-down-left-right has-text-grey-light"></i>
|
<i class="fa-solid fa-arrows-up-down-left-right has-text-grey-light"></i>
|
||||||
{% block close_button %}
|
{% block close_button %}
|
||||||
<i
|
{% include 'partials/close-window.html' %}
|
||||||
class="fa-solid fa-xmark has-text-grey-light float-right"
|
|
||||||
data-script="on click remove the closest <nav/>"></i>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block heading %}
|
{% block heading %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
<div id="drilldown-widget">
|
<div id="widget">
|
||||||
<div id="drilldown-widget-{{ unique }}" class="grid-stack-item" {% block widget_options %}{% endblock %}>
|
<div id="widget-{{ unique }}" class="grid-stack-item" {% block widget_options %}gs-w="10" gs-h="1" gs-y="10" gs-x="1"{% endblock %}>
|
||||||
<div class="grid-stack-item-content">
|
<div class="grid-stack-item-content">
|
||||||
|
|
||||||
<nav class="panel">
|
<nav class="panel">
|
||||||
<p class="panel-heading" style="padding: .2em; line-height: .5em;">
|
<p class="panel-heading" style="padding: .2em; line-height: .5em;">
|
||||||
<i class="fa-solid fa-arrows-up-down-left-right has-text-grey-light"></i>
|
<i class="fa-solid fa-arrows-up-down-left-right has-text-grey-light"></i>
|
||||||
{% block close_button %}
|
{% block close_button %}
|
||||||
<i
|
{% include 'partials/close-widget.html' %}
|
||||||
class="fa-solid fa-xmark has-text-grey-light float-right"
|
|
||||||
onclick='grid.removeWidget("drilldown-widget-{{ unique }}");'></i>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<i
|
<i
|
||||||
class="fa-solid fa-arrows-minimize has-text-grey-light float-right"
|
class="fa-solid fa-arrows-minimize has-text-grey-light float-right"
|
||||||
onclick='grid.compact();'></i>
|
onclick='grid.compact();'></i>
|
||||||
{% block heading %}
|
{% block heading %}
|
||||||
|
{{ title }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</p>
|
</p>
|
||||||
<article class="panel-block is-active">
|
<article class="panel-block is-active">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
{% block panel_content %}
|
{% block panel_content %}
|
||||||
|
{% include window_content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
<magnet-block attract-distance="10" align-to="outer|center" class="floating-window">
|
<magnet-block attract-distance="10" align-to="outer|center" class="floating-window">
|
||||||
{% extends 'wm/panel.html' %}
|
{% extends 'wm/panel.html' %}
|
||||||
{% block heading %}
|
{% block heading %}
|
||||||
|
{{ title }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block panel_content %}
|
{% block panel_content %}
|
||||||
|
{% include window_content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</magnet-block>
|
</magnet-block>
|
|
@ -6,7 +6,6 @@ from django.conf import settings
|
||||||
from django.http import HttpResponse, JsonResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views import View
|
|
||||||
from django_tables2 import SingleTableView
|
from django_tables2 import SingleTableView
|
||||||
from rest_framework.parsers import FormParser
|
from rest_framework.parsers import FormParser
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
@ -215,21 +214,132 @@ def drilldown_search(request, return_context=False, template=None):
|
||||||
|
|
||||||
class DrilldownTableView(SingleTableView):
|
class DrilldownTableView(SingleTableView):
|
||||||
table_class = DrilldownTable
|
table_class = DrilldownTable
|
||||||
template_name = "widgets/table_results.html"
|
template_name = "wm/widget.html"
|
||||||
|
window_content = "window-content/results.html"
|
||||||
|
# htmx_partial = "partials/"
|
||||||
paginate_by = settings.DRILLDOWN_RESULTS_PER_PAGE
|
paginate_by = settings.DRILLDOWN_RESULTS_PER_PAGE
|
||||||
|
|
||||||
def get_queryset(self, request, **kwargs):
|
def common_request(self, request, **kwargs):
|
||||||
context = drilldown_search(request, return_context=True)
|
extra_params = {}
|
||||||
# Save the context as we will need to merge other attributes later
|
|
||||||
self.context = context
|
|
||||||
|
|
||||||
if "object_list" in context:
|
if request.user.is_anonymous:
|
||||||
return context["object_list"]
|
sizes = settings.MAIN_SIZES_ANON
|
||||||
else:
|
else:
|
||||||
return []
|
sizes = settings.MAIN_SIZES
|
||||||
|
|
||||||
|
if request.GET:
|
||||||
|
self.template_name = "index.html"
|
||||||
|
# GET arguments in URL like ?query=xyz
|
||||||
|
query_params = request.GET.dict()
|
||||||
|
elif request.POST:
|
||||||
|
query_params = request.POST.dict()
|
||||||
|
else:
|
||||||
|
self.template_name = "index.html"
|
||||||
|
# No query, this is a fresh page load
|
||||||
|
# Don't try to search, since there's clearly nothing to do
|
||||||
|
params_with_defaults = {}
|
||||||
|
helpers.add_defaults(params_with_defaults)
|
||||||
|
context = {
|
||||||
|
"sizes": sizes,
|
||||||
|
"params": params_with_defaults,
|
||||||
|
"unique": "results",
|
||||||
|
"window_content": self.window_content,
|
||||||
|
"title": "Results",
|
||||||
|
}
|
||||||
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
# Merge everything together just in case
|
||||||
|
tmp_post = request.POST.dict()
|
||||||
|
tmp_get = request.GET.dict()
|
||||||
|
tmp_post = {k: v for k, v in tmp_post.items() if v and not v == "None"}
|
||||||
|
tmp_get = {k: v for k, v in tmp_get.items() if v and not v == "None"}
|
||||||
|
query_params.update(tmp_post)
|
||||||
|
query_params.update(tmp_get)
|
||||||
|
|
||||||
|
# URI we're passing to the template for linking
|
||||||
|
if "csrfmiddlewaretoken" in query_params:
|
||||||
|
del query_params["csrfmiddlewaretoken"]
|
||||||
|
|
||||||
|
# Parse the dates
|
||||||
|
if "dates" in query_params:
|
||||||
|
dates = parse_dates(query_params["dates"])
|
||||||
|
del query_params["dates"]
|
||||||
|
if dates:
|
||||||
|
if "message" in dates:
|
||||||
|
return render(request, self.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"]
|
||||||
|
|
||||||
|
# Remove null values
|
||||||
|
if "query" in query_params:
|
||||||
|
if query_params["query"] == "":
|
||||||
|
del query_params["query"]
|
||||||
|
|
||||||
|
# Remove null tags values
|
||||||
|
if "tags" in query_params:
|
||||||
|
if query_params["tags"] == "":
|
||||||
|
del query_params["tags"]
|
||||||
|
else:
|
||||||
|
# Parse the tags and populate cast to pass to search function
|
||||||
|
tags = parse_tags(query_params["tags"])
|
||||||
|
extra_params["tags"] = tags
|
||||||
|
|
||||||
|
context = db.query_results(request, query_params, **extra_params)
|
||||||
|
# Unique is for identifying the widgets.
|
||||||
|
# We don't want a random one since we only want one results pane.
|
||||||
|
context["unique"] = "results"
|
||||||
|
context["window_content"] = self.window_content
|
||||||
|
context["title"] = "Results"
|
||||||
|
|
||||||
|
# Valid sizes
|
||||||
|
context["sizes"] = sizes
|
||||||
|
|
||||||
|
# Add any default parameters to the context
|
||||||
|
params_with_defaults = dict(query_params)
|
||||||
|
helpers.add_defaults(params_with_defaults)
|
||||||
|
context["params"] = params_with_defaults
|
||||||
|
|
||||||
|
# Remove anything that we or the user set to a default for
|
||||||
|
# pretty URLs
|
||||||
|
helpers.remove_defaults(query_params)
|
||||||
|
url_params = urllib.parse.urlencode(query_params)
|
||||||
|
context["client_uri"] = url_params
|
||||||
|
|
||||||
|
# There's an error
|
||||||
|
if "message" in context:
|
||||||
|
response = render(request, self.template_name, context)
|
||||||
|
# Still push the URL so they can share it to get assistance
|
||||||
|
if request.GET:
|
||||||
|
if request.htmx:
|
||||||
|
response["HX-Push"] = reverse("home") + "?" + url_params
|
||||||
|
elif request.POST:
|
||||||
|
response["HX-Push"] = reverse("home") + "?" + url_params
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Create data for chart.js sentiment graph
|
||||||
|
graph = make_graph(context["object_list"])
|
||||||
|
context["data"] = graph
|
||||||
|
|
||||||
|
# Create the table
|
||||||
|
context = make_table(context)
|
||||||
|
|
||||||
|
# URI we're passing to the template for linking, table fields removed
|
||||||
|
table_fields = ["page", "sort"]
|
||||||
|
clean_params = {k: v for k, v in query_params.items() if k not in table_fields}
|
||||||
|
clean_url_params = urllib.parse.urlencode(clean_params)
|
||||||
|
context["uri"] = clean_url_params
|
||||||
|
|
||||||
|
# unique = str(uuid.uuid4())[:8]
|
||||||
|
# self.context = context
|
||||||
|
return context
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object_list = self.get_queryset(request)
|
self.context = self.common_request(request)
|
||||||
|
if isinstance(self.context, HttpResponse):
|
||||||
|
return self.context
|
||||||
|
self.object_list = self.context["object_list"]
|
||||||
show = []
|
show = []
|
||||||
show = set().union(*(d.keys() for d in self.object_list))
|
show = set().union(*(d.keys() for d in self.object_list))
|
||||||
allow_empty = self.get_allow_empty()
|
allow_empty = self.get_allow_empty()
|
||||||
|
@ -245,17 +355,17 @@ class DrilldownTableView(SingleTableView):
|
||||||
else:
|
else:
|
||||||
is_empty = not self.object_list # noqa
|
is_empty = not self.object_list # noqa
|
||||||
context = self.get_context_data()
|
context = self.get_context_data()
|
||||||
if isinstance(self.context, HttpResponse):
|
|
||||||
return self.context
|
|
||||||
|
|
||||||
for k, v in self.context.items():
|
for k, v in self.context.items():
|
||||||
if k not in context:
|
if k not in context:
|
||||||
context[k] = v
|
context[k] = v
|
||||||
context["show"] = show
|
context["show"] = show
|
||||||
|
|
||||||
if request.method == "GET":
|
# if request.htmx:
|
||||||
if not request.htmx:
|
# self.template_name = self.window_content
|
||||||
self.template_name = "ui/drilldown/drilldown.html"
|
# if request.method == "GET":
|
||||||
|
# if not request.htmx:
|
||||||
|
# self.template_name = "ui/drilldown/drilldown.html"
|
||||||
response = self.render_to_response(context)
|
response = self.render_to_response(context)
|
||||||
# if not request.method == "GET":
|
# if not request.method == "GET":
|
||||||
if "client_uri" in context:
|
if "client_uri" in context:
|
||||||
|
@ -266,15 +376,15 @@ class DrilldownTableView(SingleTableView):
|
||||||
return self.get(request, *args, **kwargs)
|
return self.get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Drilldown(View):
|
# class Drilldown(View):
|
||||||
template_name = "ui/drilldown/drilldown.html"
|
# template_name = "ui/drilldown/drilldown.html"
|
||||||
plan_name = "drilldown"
|
# plan_name = "drilldown"
|
||||||
|
|
||||||
def get(self, request):
|
# def get(self, request):
|
||||||
return drilldown_search(request)
|
# return drilldown_search(request)
|
||||||
|
|
||||||
def post(self, request):
|
# def post(self, request):
|
||||||
return drilldown_search(request)
|
# return drilldown_search(request)
|
||||||
|
|
||||||
|
|
||||||
class DrilldownContextModal(APIView):
|
class DrilldownContextModal(APIView):
|
||||||
|
@ -377,7 +487,6 @@ class DrilldownContextModal(APIView):
|
||||||
type=type,
|
type=type,
|
||||||
nicks=nicks_sensitive,
|
nicks=nicks_sensitive,
|
||||||
)
|
)
|
||||||
print("QUERY", search_query)
|
|
||||||
results = db.query_results(
|
results = db.query_results(
|
||||||
request,
|
request,
|
||||||
query_params,
|
query_params,
|
||||||
|
@ -437,45 +546,18 @@ class ThresholdInfoModal(APIView):
|
||||||
nick = request.data["nick"]
|
nick = request.data["nick"]
|
||||||
channel = request.data["channel"]
|
channel = request.data["channel"]
|
||||||
|
|
||||||
# SAFE BLOCK #
|
|
||||||
# Lookup the hash values but don't disclose them to the user
|
|
||||||
# if settings.HASHING:
|
|
||||||
# SAFE_PARAMS = request.data.dict()
|
|
||||||
# hash_lookup(request.user, SAFE_PARAMS)
|
|
||||||
|
|
||||||
channels = get_chans(net, [nick])
|
channels = get_chans(net, [nick])
|
||||||
print("CHANNELS", channels)
|
|
||||||
users = get_users(net, [nick])
|
users = get_users(net, [nick])
|
||||||
print("USERS", users)
|
|
||||||
num_users = annotate_num_users(net, channels)
|
num_users = annotate_num_users(net, channels)
|
||||||
print("NUM_USERS", num_users)
|
|
||||||
num_chans = annotate_num_chans(net, users)
|
num_chans = annotate_num_chans(net, users)
|
||||||
print("NUM_CHANS", num_chans)
|
|
||||||
if channels:
|
if channels:
|
||||||
inter_users = get_users(net, channels)
|
inter_users = get_users(net, channels)
|
||||||
else:
|
else:
|
||||||
inter_users = []
|
inter_users = []
|
||||||
print("INTER_USERS", inter_users)
|
|
||||||
if users:
|
if users:
|
||||||
inter_chans = get_chans(net, users)
|
inter_chans = get_chans(net, users)
|
||||||
else:
|
else:
|
||||||
inter_chans = []
|
inter_chans = []
|
||||||
print("INTER_CHANS", inter_chans)
|
|
||||||
# if settings.HASHING:
|
|
||||||
# hash_list(request.user, inter_chans)
|
|
||||||
# hash_list(request.user, inter_users)
|
|
||||||
|
|
||||||
# hash_list(request.user, num_chans, hash_keys=True)
|
|
||||||
# hash_list(request.user, num_users, hash_keys=True)
|
|
||||||
|
|
||||||
# hash_list(request.user, channels)
|
|
||||||
# hash_list(request.user, users)
|
|
||||||
|
|
||||||
# if settings.RANDOMISATION:
|
|
||||||
# randomise_list(request.user, num_chans)
|
|
||||||
# randomise_list(request.user, num_users)
|
|
||||||
|
|
||||||
# SAFE BLOCK END #
|
|
||||||
|
|
||||||
unique = str(uuid.uuid4())[:8]
|
unique = str(uuid.uuid4())[:8]
|
||||||
context = {
|
context = {
|
||||||
|
@ -490,5 +572,4 @@ class ThresholdInfoModal(APIView):
|
||||||
"num_chans": num_chans,
|
"num_chans": num_chans,
|
||||||
"unique": unique,
|
"unique": unique,
|
||||||
}
|
}
|
||||||
print("CON", context)
|
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM python:3
|
||||||
|
|
||||||
|
RUN useradd -d /code pathogen
|
||||||
|
RUN mkdir /code
|
||||||
|
RUN chown pathogen:pathogen /code
|
||||||
|
|
||||||
|
RUN mkdir /venv
|
||||||
|
RUN chown pathogen:pathogen /venv
|
||||||
|
|
||||||
|
USER pathogen
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
WORKDIR /code
|
||||||
|
COPY requirements.dev.txt /code/
|
||||||
|
RUN python -m venv /venv
|
||||||
|
RUN . /venv/bin/activate && pip install -r requirements.dev.txt
|
||||||
|
CMD . /venv/bin/activate && exec python manage.py runserver 0.0.0.0:8000
|
Loading…
Reference in New Issue