parent
e41e5fe862
commit
0c0a9a0a0a
41
models.py
41
models.py
|
@ -1,5 +1,6 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
class CoreProtectUser(User):
|
class CoreProtectUser(User):
|
||||||
|
@ -287,3 +288,43 @@ class CoWorld(models.Model):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.world
|
return self.world
|
||||||
|
|
||||||
|
|
||||||
|
class CoResult(models.Model):
|
||||||
|
type = models.TextField()
|
||||||
|
unix = models.TextField()
|
||||||
|
player = models.TextField()
|
||||||
|
action = models.TextField()
|
||||||
|
data = models.TextField()
|
||||||
|
x = models.TextField()
|
||||||
|
y = models.TextField()
|
||||||
|
z = models.TextField()
|
||||||
|
world = models.TextField()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_time(self):
|
||||||
|
dt = datetime.fromtimestamp(float(self.unix))
|
||||||
|
return dt.strftime("%b %d, %Y at %I:%M:%S %p")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_action(self):
|
||||||
|
if self.type == "block":
|
||||||
|
return "Block Break" if self.action == 0 else "Block Place" if self.action == 1 else "Interact/Used"
|
||||||
|
if self.type == "chat":
|
||||||
|
return "Chat"
|
||||||
|
if self.type == "command":
|
||||||
|
return "Command"
|
||||||
|
if self.type == "container":
|
||||||
|
return "Took from Container" if self.action == 0 else "Placed in Container"
|
||||||
|
return self.action
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_data(self):
|
||||||
|
if self.type == "block":
|
||||||
|
material = self.data.replace("minecraft:", "")
|
||||||
|
parts = material.split("_")
|
||||||
|
return " ".join([part.capitalize() for part in parts])
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
managed = False
|
||||||
|
|
|
@ -2,8 +2,30 @@
|
||||||
flatpickr('[type="date"]', {
|
flatpickr('[type="date"]', {
|
||||||
altInput: true,
|
altInput: true,
|
||||||
altFormat: "F j, Y at H:i",
|
altFormat: "F j, Y at H:i",
|
||||||
dateFormat: "Y-m-d H:i",
|
dateFormat: "U",
|
||||||
enableTime: true,
|
enableTime: true,
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
function ajax(method, url, callback) {
|
||||||
|
let xmlhttp = new XMLHttpRequest();
|
||||||
|
xmlhttp.onreadystatechange = function() {
|
||||||
|
if (xmlhttp.readyState === XMLHttpRequest.DONE) { // XMLHttpRequest.DONE == 4
|
||||||
|
callback(xmlhttp.status, xmlhttp.responseText);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xmlhttp.open(method, url, true);
|
||||||
|
xmlhttp.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formURI(formData) {
|
||||||
|
let pairs = [];
|
||||||
|
for (let data of formData.entries()) {
|
||||||
|
pairs.push(encodeURIComponent(data[0]) + '=' + encodeURIComponent(data[1]))
|
||||||
|
}
|
||||||
|
return pairs.join("&").replace(/%20/g, '+');
|
||||||
|
}
|
||||||
|
|
||||||
|
function elementHTML(id, html) {
|
||||||
|
document.getElementById(id).innerHTML = html
|
||||||
|
}
|
|
@ -22,7 +22,7 @@
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<form style="margin-left: 7rem;" class="has-text-left column is-one-third">
|
<form style="margin-left: 7rem;" id="coreprotect_form" name="coreprotect_form" class="has-text-left column is-one-third">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-one-half">
|
<div class="column is-one-half">
|
||||||
<h4 class="title is-5 has-text-primary">
|
<h4 class="title is-5 has-text-primary">
|
||||||
|
@ -68,8 +68,8 @@
|
||||||
</h4>
|
</h4>
|
||||||
{% for world in form.worlds %}
|
{% for world in form.worlds %}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input class="is-checkradio is-block is-info" id="{{world.id}}" name="{{world.id}}" type="checkbox" {% if world.checked %}checked{% endif %}>
|
<input class="is-checkradio is-block is-info" id="{{world.world_id}}" name="world" value="{{world.id}}" type="checkbox" {% if world.checked %}checked{% endif %}>
|
||||||
<label for="{{world.id}}">{{world.name}}</label>
|
<label for="{{world.world_id}}">{{world.name}}</label>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
@ -142,8 +142,8 @@
|
||||||
<h4 class="title is-5 has-text-primary">
|
<h4 class="title is-5 has-text-primary">
|
||||||
Blocks
|
Blocks
|
||||||
</h4>
|
</h4>
|
||||||
<div class="field tooltip is-tooltip-multiline" data-tooltip="Accepts full names, partial names, or block IDs. Separated by commas.">
|
<div class="field tooltip is-tooltip-multiline" data-tooltip="Accepts full or partial names. Separated by commas.">
|
||||||
<input class="input is-4" id="blocks" name="blocks" type="text" placeholder="Block Names or IDs" value="{{form.blocks}}">
|
<input class="input is-4" id="blocks" name="blocks" type="text" placeholder="Block Names" value="{{form.blocks}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -166,7 +166,7 @@
|
||||||
|
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<button class="button is-primary" name="search" value="true" type="submit">Search</button>
|
<button class="button is-primary" id="search" type="button">Search</button>
|
||||||
</p>
|
</p>
|
||||||
<p class="control">
|
<p class="control">
|
||||||
<button class="button is-dark" id="clear" type="button">Clear</button>
|
<button class="button is-dark" id="clear" type="button">Clear</button>
|
||||||
|
@ -175,7 +175,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="column is-three-fifths">
|
<div class="column is-three-fifths" id="results">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -183,6 +183,16 @@
|
||||||
<script src="{% static 'coreprotect/js/bulma-extensions.min.js' %}"></script>
|
<script src="{% static 'coreprotect/js/bulma-extensions.min.js' %}"></script>
|
||||||
<script src="{% static 'coreprotect/js/main.js' %}"></script>
|
<script src="{% static 'coreprotect/js/main.js' %}"></script>
|
||||||
<script>
|
<script>
|
||||||
|
document.getElementById("search").addEventListener("click", function() {
|
||||||
|
elementHTML("results", 'Querying Database...<br/><progress class="progress is-primary"></progress>');
|
||||||
|
|
||||||
|
let form = document.getElementById("coreprotect_form");
|
||||||
|
let formData = new FormData(form);
|
||||||
|
ajax("GET", "{% url "coreprotect_query" %}?" + formURI(formData), function(status, data) {
|
||||||
|
elementHTML("results", data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
document.getElementById("clear").addEventListener("click", function() {
|
document.getElementById("clear").addEventListener("click", function() {
|
||||||
// Actions
|
// Actions
|
||||||
document.getElementById("block_break").checked = false;
|
document.getElementById("block_break").checked = false;
|
||||||
|
@ -196,7 +206,7 @@
|
||||||
|
|
||||||
// Worlds
|
// Worlds
|
||||||
{% for world in form.worlds %}
|
{% for world in form.worlds %}
|
||||||
document.getElementById("{{world.id}}").checked = false;
|
document.getElementById("{{world.world_id}}").checked = false;
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<table class="table is-fullwidth is-striped is-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Player</th>
|
||||||
|
<th>Action</th>
|
||||||
|
<th>Data</th>
|
||||||
|
<th>X</th>
|
||||||
|
<th>Y</th>
|
||||||
|
<th>Z</th>
|
||||||
|
<th>World</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for result in results %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ result.display_time }}</td>
|
||||||
|
<td>{{ result.player }}</td>
|
||||||
|
<td>{{ result.display_action }}</td>
|
||||||
|
<td>{{ result.display_data }}</td>
|
||||||
|
<td>{{ result.x }}</td>
|
||||||
|
<td>{{ result.y }}</td>
|
||||||
|
<td>{{ result.z }}</td>
|
||||||
|
<td>{{ result.world }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
5
urls.py
5
urls.py
|
@ -1,7 +1,8 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.contrib.auth.decorators import permission_required
|
from django.contrib.auth.decorators import permission_required
|
||||||
from django_coreprotect.views import Home
|
from django_coreprotect.views import Home, Query
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', permission_required('coreprotect_base')(Home.as_view())),
|
path('', permission_required('coreprotect_base')(Home.as_view()), name="coreprotect_index"),
|
||||||
|
path('query/', permission_required('coreprotect_base')(Query.as_view()), name="coreprotect_query"),
|
||||||
]
|
]
|
||||||
|
|
175
views.py
175
views.py
|
@ -1,39 +1,57 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django_coreprotect.models import CoWorld
|
from django.http.response import JsonResponse
|
||||||
|
from django_coreprotect.models import *
|
||||||
|
|
||||||
|
|
||||||
class Home(View):
|
class Home(View):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
form = form_data(request.GET)
|
form = form_data(request.GET)
|
||||||
return render(request, "coreprotect/coreprotect.html", {"form": form, "results": result_data(form)})
|
return render(request, "coreprotect/coreprotect.html", {"form": form})
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Query(View):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
form = form_data(request.GET)
|
||||||
|
results = result_data(form)
|
||||||
|
|
||||||
|
if "format" in request.GET and request.GET["format"] == "json":
|
||||||
|
return JsonResponse(results)
|
||||||
|
|
||||||
|
return render(request, "coreprotect/table.html", {"results": results})
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Form:
|
class Form:
|
||||||
block_break = False
|
|
||||||
block_place = False
|
def __init__(self):
|
||||||
chat = False
|
self.block_break = False
|
||||||
chest_use = False
|
self.block_place = False
|
||||||
command = False
|
self.chat = False
|
||||||
interact = False
|
self.chest_use = False
|
||||||
login_logout = False
|
self.command = False
|
||||||
sign_place = False
|
self.interact = False
|
||||||
worlds = []
|
self.login_logout = False
|
||||||
ignore_environment = False
|
self.sign_place = False
|
||||||
limit_results = "200"
|
self.worlds = []
|
||||||
players = ""
|
self.ignore_environment = False
|
||||||
x = ""
|
self.start = ""
|
||||||
y = ""
|
self.limit_results = ""
|
||||||
z = ""
|
self.players = ""
|
||||||
radius = ""
|
self.x = ""
|
||||||
blocks = ""
|
self.y = ""
|
||||||
date_from = ""
|
self.z = ""
|
||||||
date_to = ""
|
self.radius = ""
|
||||||
search = False
|
self.blocks = ""
|
||||||
|
self.date_from = ""
|
||||||
|
self.date_to = ""
|
||||||
|
|
||||||
|
|
||||||
def form_data(request_data):
|
def form_data(request_data):
|
||||||
|
@ -51,11 +69,13 @@ def form_data(request_data):
|
||||||
form.sign_place = checkbox(request_data["sign_place"]) if "sign_place" in request_data else False
|
form.sign_place = checkbox(request_data["sign_place"]) if "sign_place" in request_data else False
|
||||||
|
|
||||||
# Worlds
|
# Worlds
|
||||||
|
request_worlds = request_data.getlist("world")
|
||||||
for world in worlds:
|
for world in worlds:
|
||||||
world_id = "world_{}".format(world.id)
|
world_id = "world_{}".format(world.id)
|
||||||
w = {
|
w = {
|
||||||
"id": world_id,
|
"id": str(world.id),
|
||||||
"checked": checkbox(request_data[world_id]) if world_id in request_data else False,
|
"world_id": world_id,
|
||||||
|
"checked": True if str(world.id) in request_worlds else False,
|
||||||
"name": world.world
|
"name": world.world
|
||||||
}
|
}
|
||||||
form.worlds.append(w)
|
form.worlds.append(w)
|
||||||
|
@ -64,6 +84,7 @@ def form_data(request_data):
|
||||||
form.ignore_environment = checkbox(request_data["ignore_environment"]) if "ignore_environment" in request_data else False
|
form.ignore_environment = checkbox(request_data["ignore_environment"]) if "ignore_environment" in request_data else False
|
||||||
|
|
||||||
# Limit Results
|
# Limit Results
|
||||||
|
form.start = request_data["start"] if "start" in request_data else "0"
|
||||||
form.limit_results = request_data["limit_results"] if "limit_results" in request_data else "200"
|
form.limit_results = request_data["limit_results"] if "limit_results" in request_data else "200"
|
||||||
|
|
||||||
# Players
|
# Players
|
||||||
|
@ -81,9 +102,10 @@ def form_data(request_data):
|
||||||
# Date and Time
|
# Date and Time
|
||||||
form.date_from = request_data["date_from"] if "date_from" in request_data else ""
|
form.date_from = request_data["date_from"] if "date_from" in request_data else ""
|
||||||
form.date_to = request_data["date_to"] if "date_to" in request_data else ""
|
form.date_to = request_data["date_to"] if "date_to" in request_data else ""
|
||||||
|
if form.date_from and not form.date_to:
|
||||||
# Search
|
form.date_to = datetime.now().timestamp()
|
||||||
form.search = "search" in request_data and request_data["search"] == "true"
|
if form.date_to and not form.date_from:
|
||||||
|
form.date_from = datetime.now().timestamp()
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
|
||||||
|
@ -93,6 +115,101 @@ def checkbox(value):
|
||||||
|
|
||||||
|
|
||||||
def result_data(form):
|
def result_data(form):
|
||||||
results = {}
|
queries = []
|
||||||
|
ignore_environment = " AND player NOT LIKE '#%' " if form.ignore_environment else ""
|
||||||
|
|
||||||
return results
|
coords = []
|
||||||
|
radius = form.radius if form.radius else "0"
|
||||||
|
if form.x:
|
||||||
|
coords.append(" AND x BETWEEN {0} - {1} AND {0} + {1}".format(form.x, radius))
|
||||||
|
if form.y:
|
||||||
|
coords.append(" AND y BETWEEN {0} - {1} AND {0} + {1}".format(form.y, radius))
|
||||||
|
if form.z:
|
||||||
|
coords.append(" AND z BETWEEN {0} - {1} AND {0} + {1}".format(form.z, radius))
|
||||||
|
coords_clause = " AND ".join(coords)
|
||||||
|
|
||||||
|
players = []
|
||||||
|
players_clause = ""
|
||||||
|
if form.players:
|
||||||
|
for player in form.players.split(","):
|
||||||
|
players.append(player.strip())
|
||||||
|
players_clause = " AND ({})".format(" OR ".join(["player LIKE '%{}%'".format(p) for p in players]))
|
||||||
|
|
||||||
|
worlds_clause = ""
|
||||||
|
worlds = [world["id"] for world in form.worlds if world["checked"]]
|
||||||
|
if len(worlds):
|
||||||
|
worlds_clause = " AND cw.id IN ({})".format(",".join(worlds))
|
||||||
|
|
||||||
|
time_clause = ""
|
||||||
|
if form.date_from and form.date_to:
|
||||||
|
time_clause = " AND unix BETWEEN {} AND {} ".format(form.date_from, form.date_to)
|
||||||
|
|
||||||
|
# Block Break, Block Place, and Interact
|
||||||
|
block_actions = []
|
||||||
|
if form.block_break:
|
||||||
|
block_actions.append("0")
|
||||||
|
if form.block_place:
|
||||||
|
block_actions.append("1")
|
||||||
|
if form.interact:
|
||||||
|
block_actions.append("2")
|
||||||
|
if len(block_actions):
|
||||||
|
queries.append('''SELECT
|
||||||
|
0 AS id, "block" AS type, cb.time AS unix, cu.user AS player, cb.action, cmm.material AS data, cb.x, cb.y, cb.z, cw.world
|
||||||
|
FROM co_block cb
|
||||||
|
JOIN co_user cu ON cb.user = cu.id
|
||||||
|
JOIN co_material_map cmm ON cb.type = cmm.id
|
||||||
|
JOIN co_world cw ON cb.wid = cw.id
|
||||||
|
WHERE cb.action IN ({action})
|
||||||
|
{ignore_environment}
|
||||||
|
{coords}
|
||||||
|
{players}
|
||||||
|
{worlds}
|
||||||
|
{time}
|
||||||
|
'''.format(action=",".join(block_actions), ignore_environment=ignore_environment, coords=coords_clause,
|
||||||
|
players=players_clause, worlds=worlds_clause, time=time_clause))
|
||||||
|
|
||||||
|
# Chat
|
||||||
|
if form.chat:
|
||||||
|
queries.append('''SELECT
|
||||||
|
0 AS id, "chat" AS type, cc.time AS unix, cu.user AS player, "" AS action, cc.message AS data, "" AS x, "" AS y, "" AS z, "" AS world
|
||||||
|
FROM co_chat cc
|
||||||
|
JOIN co_user cu ON cch.user = cu.id
|
||||||
|
WHERE 1 = 1
|
||||||
|
{players}
|
||||||
|
{time}
|
||||||
|
'''.format(players=players_clause, time=time_clause))
|
||||||
|
|
||||||
|
# Commands
|
||||||
|
if form.command:
|
||||||
|
queries.append('''SELECT
|
||||||
|
0 AS id, "command" AS type, cc.time AS unix, cu.user AS player, "" AS action, cc.message AS data, "" AS x, "" AS y, "" AS z, "" AS world
|
||||||
|
FROM co_chat cc
|
||||||
|
JOIN co_user cu ON cco.user = cu.id
|
||||||
|
WHERE 1 = 1
|
||||||
|
{players}
|
||||||
|
{time}
|
||||||
|
'''.format(players=players_clause, time=time_clause))
|
||||||
|
|
||||||
|
# Chest Use
|
||||||
|
if form.chest_use:
|
||||||
|
queries.append('''SELECT
|
||||||
|
0 AS id, "container" AS type, cc.time AS unix, cu.user AS player, cc.action, cc.amount || " " || cmm.material AS data, cc.x, cc.y, cc.z, cw.world
|
||||||
|
FROM co_container cc
|
||||||
|
JOIN co_user cu ON cc.user = cu.id
|
||||||
|
JOIN co_material_map cmm ON cc.type = cmm.id
|
||||||
|
JOIN co_world cw ON cc.wid = cw.id
|
||||||
|
WHERE 1 = 1
|
||||||
|
{coords}
|
||||||
|
{players}
|
||||||
|
{worlds}
|
||||||
|
{time}
|
||||||
|
'''.format(coords=coords_clause,
|
||||||
|
players=players_clause, worlds=worlds_clause, time=time_clause))
|
||||||
|
|
||||||
|
query = " UNION ".join(queries)
|
||||||
|
if query and form.limit_results != "-1":
|
||||||
|
query += " LIMIT {}, {}".format(form.start, form.limit_results)
|
||||||
|
print(query)
|
||||||
|
if query:
|
||||||
|
return CoResult.objects.raw(query)
|
||||||
|
return []
|
||||||
|
|
Loading…
Reference in New Issue