From 5357e645919105f0b4d1273d8cd3a72e5099208b Mon Sep 17 00:00:00 2001 From: Etzelia Date: Thu, 15 Aug 2019 17:00:54 -0500 Subject: [PATCH] Start pagination SQL is finished Signed-off-by: Etzelia --- migrations/0001_initial.py | 302 ------------------------- models.py | 22 +- static/coreprotect/css/main.css | 3 + static/coreprotect/js/main.js | 47 ++++ templates/coreprotect/coreprotect.html | 64 +++++- templates/coreprotect/table.html | 11 +- views.py | 108 ++++++--- 7 files changed, 203 insertions(+), 354 deletions(-) delete mode 100644 migrations/0001_initial.py create mode 100644 static/coreprotect/css/main.css diff --git a/migrations/0001_initial.py b/migrations/0001_initial.py deleted file mode 100644 index 72066f7..0000000 --- a/migrations/0001_initial.py +++ /dev/null @@ -1,302 +0,0 @@ -# Generated by Django 2.2.3 on 2019-07-27 03:17 - -import django.contrib.auth.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0011_update_proxy_permissions'), - ] - - operations = [ - migrations.CreateModel( - name='CoArtMap', - fields=[ - ('id', models.IntegerField(primary_key=True, serialize=False)), - ('art', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Art Map', - 'verbose_name_plural': 'Art Maps', - 'db_table': 'co_art_map', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoBlock', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('user', models.IntegerField(blank=True, null=True)), - ('wid', models.IntegerField(blank=True, null=True)), - ('x', models.IntegerField(blank=True, null=True)), - ('y', models.IntegerField(blank=True, null=True)), - ('z', models.IntegerField(blank=True, null=True)), - ('type', models.IntegerField(blank=True, null=True)), - ('data', models.IntegerField(blank=True, null=True)), - ('meta', models.BinaryField(blank=True, null=True)), - ('blockdata', models.BinaryField(blank=True, null=True)), - ('action', models.IntegerField(blank=True, null=True)), - ('rolled_back', models.IntegerField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Block', - 'verbose_name_plural': 'Blocks', - 'db_table': 'co_block', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoBlockdataMap', - fields=[ - ('id', models.IntegerField(primary_key=True, serialize=False)), - ('data', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'BlockData Map', - 'verbose_name_plural': 'BlockData Maps', - 'db_table': 'co_blockdata_map', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoChat', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('user', models.IntegerField(blank=True, null=True)), - ('message', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Chat Message', - 'verbose_name_plural': 'Chat Messages', - 'db_table': 'co_chat', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoCommand', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('user', models.IntegerField(blank=True, null=True)), - ('message', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Command', - 'verbose_name_plural': 'Commands', - 'db_table': 'co_command', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoContainer', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('user', models.IntegerField(blank=True, null=True)), - ('wid', models.IntegerField(blank=True, null=True)), - ('x', models.IntegerField(blank=True, null=True)), - ('y', models.IntegerField(blank=True, null=True)), - ('z', models.IntegerField(blank=True, null=True)), - ('type', models.IntegerField(blank=True, null=True)), - ('data', models.IntegerField(blank=True, null=True)), - ('amount', models.IntegerField(blank=True, null=True)), - ('metadata', models.BinaryField(blank=True, null=True)), - ('action', models.IntegerField(blank=True, null=True)), - ('rolled_back', models.IntegerField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Container Transaction', - 'verbose_name_plural': 'Container Transactions', - 'db_table': 'co_container', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoDatabaseLock', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('status', models.IntegerField(blank=True, null=True)), - ('time', models.IntegerField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Database Lock', - 'verbose_name_plural': 'Database Locks', - 'db_table': 'co_database_lock', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoEntity', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('data', models.BinaryField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Entity', - 'verbose_name_plural': 'Entities', - 'db_table': 'co_entity', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoEntityMap', - fields=[ - ('id', models.IntegerField(primary_key=True, serialize=False)), - ('entity', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Entity Mapping', - 'verbose_name_plural': 'Entity Mappings', - 'db_table': 'co_entity_map', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoMaterialMap', - fields=[ - ('id', models.IntegerField(primary_key=True, serialize=False)), - ('material', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Material Mapping', - 'verbose_name_plural': 'Material Mappings', - 'db_table': 'co_material_map', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoSession', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('user', models.IntegerField(blank=True, null=True)), - ('wid', models.IntegerField(blank=True, null=True)), - ('x', models.IntegerField(blank=True, null=True)), - ('y', models.IntegerField(blank=True, null=True)), - ('z', models.IntegerField(blank=True, null=True)), - ('action', models.IntegerField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Session', - 'verbose_name_plural': 'Sessions', - 'db_table': 'co_session', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoSign', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('user', models.IntegerField(blank=True, null=True)), - ('wid', models.IntegerField(blank=True, null=True)), - ('x', models.IntegerField(blank=True, null=True)), - ('y', models.IntegerField(blank=True, null=True)), - ('z', models.IntegerField(blank=True, null=True)), - ('color', models.IntegerField(blank=True, null=True)), - ('line_1', models.TextField(blank=True, null=True)), - ('line_2', models.TextField(blank=True, null=True)), - ('line_3', models.TextField(blank=True, null=True)), - ('line_4', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Sign', - 'verbose_name_plural': 'Signs', - 'db_table': 'co_sign', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoSkull', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('owner', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Skull', - 'verbose_name_plural': 'Skulls', - 'db_table': 'co_skull', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoUser', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('user', models.TextField(blank=True, null=True)), - ('uuid', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'User', - 'verbose_name_plural': 'Users', - 'db_table': 'co_user', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoUsernameLog', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('uuid', models.TextField(blank=True, null=True)), - ('user', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Username', - 'verbose_name_plural': 'Usernames', - 'db_table': 'co_username_log', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoVersion', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('time', models.IntegerField(blank=True, null=True)), - ('version', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'Version', - 'verbose_name_plural': 'Versions', - 'db_table': 'co_version', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoWorld', - fields=[ - ('id', models.IntegerField(primary_key=True, serialize=False)), - ('world', models.TextField(blank=True, null=True)), - ], - options={ - 'verbose_name': 'World', - 'verbose_name_plural': 'Worlds', - 'db_table': 'co_world', - 'managed': False, - }, - ), - migrations.CreateModel( - name='CoreProtectUser', - fields=[ - ], - options={ - 'permissions': (('coreprotect_base', 'Can use CoreProtect GUI'), ('coreprotect_extra', 'Can search Chat/Commands'), ('coreprotect_activity', 'Can use CoreProtect Activity Monitor')), - 'proxy': True, - 'indexes': [], - 'constraints': [], - }, - bases=('auth.user',), - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - ] diff --git a/models.py b/models.py index 3eb00a4..5a3cbee 100644 --- a/models.py +++ b/models.py @@ -312,19 +312,31 @@ class CoResult(models.Model): 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" + if self.type == "command": + return "Command" + if self.type == "session": + return "Logout" if self.action == 0 else "Login" + if self.type == "sign": + return "Sign" return self.action @property def display_data(self): + def material_name(namespace): + m = namespace.replace("minecraft:", "") + pp = m.split("_") + return " ".join([p.capitalize() for p in pp]) + if self.type == "block": - material = self.data.replace("minecraft:", "") - parts = material.split("_") - return " ".join([part.capitalize() for part in parts]) + return material_name(self.data) + if self.type == "container": + parts = self.data.split() + return "{} {}".format(parts[0], material_name(parts[1])) return self.data class Meta: managed = False + verbose_name = "Result" + verbose_name_plural = "Results" diff --git a/static/coreprotect/css/main.css b/static/coreprotect/css/main.css new file mode 100644 index 0000000..4f12016 --- /dev/null +++ b/static/coreprotect/css/main.css @@ -0,0 +1,3 @@ +.click-row { + cursor: pointer; +} \ No newline at end of file diff --git a/static/coreprotect/js/main.js b/static/coreprotect/js/main.js index a8406a3..0629b98 100644 --- a/static/coreprotect/js/main.js +++ b/static/coreprotect/js/main.js @@ -28,4 +28,51 @@ function formURI(formData) { function elementHTML(id, html) { document.getElementById(id).innerHTML = html +} + +function downloadCSV(csv, filename) { + let csvFile; + let downloadLink; + + // CSV file + csvFile = new Blob([csv], {type: "text/csv"}); + + // Download link + downloadLink = document.createElement("a"); + + // File name + downloadLink.download = filename; + + // Create a link to the file + downloadLink.href = window.URL.createObjectURL(csvFile); + + // Hide download link + downloadLink.style.display = "none"; + + // Add the link to DOM + document.body.appendChild(downloadLink); + + // Click download link + downloadLink.click(); +} + +function exportTableToCSV(filename, elementId) { + let csv = []; + let selector = "table tr"; + if (elementId !== "") { + selector = "#" + elementId + " tr" + } + let rows = document.querySelectorAll(selector); + + for (let i = 0; i < rows.length; i++) { + let row = [], cols = rows[i].querySelectorAll("td, th"); + + for (let j = 0; j < cols.length; j++) + row.push('"' + cols[j].innerText + '"'); + + csv.push(row.join(",")); + } + + // Download CSV file + downloadCSV(csv.join("\n"), filename); } \ No newline at end of file diff --git a/templates/coreprotect/coreprotect.html b/templates/coreprotect/coreprotect.html index 988335e..01d3ca1 100644 --- a/templates/coreprotect/coreprotect.html +++ b/templates/coreprotect/coreprotect.html @@ -9,6 +9,7 @@ + @@ -86,24 +87,23 @@

- Limit Results + Page Size

- + + + + + +

+ @@ -171,6 +171,9 @@

+

+ +

@@ -183,6 +186,35 @@ diff --git a/templates/coreprotect/table.html b/templates/coreprotect/table.html index 3a47b02..84de4e4 100644 --- a/templates/coreprotect/table.html +++ b/templates/coreprotect/table.html @@ -1,4 +1,4 @@ - +
@@ -13,7 +13,7 @@ {% for result in results %} - + @@ -25,4 +25,9 @@ {% endfor %} -
Time
{{ result.display_time }} {{ result.player }} {{ result.display_action }}
\ No newline at end of file + + \ No newline at end of file diff --git a/views.py b/views.py index a938d39..56d2324 100644 --- a/views.py +++ b/views.py @@ -23,7 +23,13 @@ class Query(View): if "format" in request.GET and request.GET["format"] == "json": return JsonResponse(results) - return render(request, "coreprotect/table.html", {"results": results}) + prev_page, next_page = False, False + if form.page != "1": + prev_page = True + if int(form.page) * int(form.page_size) < 1000: + next_page = True + + return render(request, "coreprotect/table.html", {"results": results, "num": len(results), "prev": prev_page, "next": next_page}) def post(self, request): pass @@ -42,8 +48,9 @@ class Form: self.sign_place = False self.worlds = [] self.ignore_environment = False + self.page = "" + self.page_size = "" self.start = "" - self.limit_results = "" self.players = "" self.x = "" self.y = "" @@ -84,8 +91,13 @@ def form_data(request_data): form.ignore_environment = checkbox(request_data["ignore_environment"]) if "ignore_environment" in request_data else False # 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.page = request_data["page"] if "page" in request_data else "1" + form.page_size = request_data["page_size"] if "page_size" in request_data else "20" + if int(form.page_size) > 1000: + form.page_size = "1000" + start = (int(form.page) * int(form.page_size)) + form.start = str(start) if start <= 1000 - int(form.page_size) else str(1000 - int(form.page_size)) + # Players form.players = request_data["players"] if "players" in request_data else "" @@ -102,10 +114,7 @@ def form_data(request_data): # Date and Time 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 "" - if form.date_from and not form.date_to: - form.date_to = datetime.now().timestamp() - if form.date_to and not form.date_from: - form.date_from = datetime.now().timestamp() + return form @@ -141,8 +150,15 @@ def result_data(form): 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) + if form.date_from or form.date_to: + df, dt = None, None + if form.date_from and not form.date_to: + df = form.date_from + dt = datetime.now().timestamp() + if form.date_to and not form.date_from: + df = datetime.now().timestamp() + dt = form.date_to + time_clause = " AND unix BETWEEN {} AND {} ".format(df, dt) # Block Break, Block Place, and Interact block_actions = [] @@ -161,30 +177,19 @@ def result_data(form): JOIN co_world cw ON cb.wid = cw.id WHERE cb.action IN ({action}) {ignore_environment} - {coords} - {players} + {players} + {coords} {worlds} {time} - '''.format(action=",".join(block_actions), ignore_environment=ignore_environment, coords=coords_clause, - players=players_clause, worlds=worlds_clause, time=time_clause)) + '''.format(action=",".join(block_actions), ignore_environment=ignore_environment, players=players_clause, + coords=coords_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 + JOIN co_user cu ON cc.user = cu.id WHERE 1 = 1 {players} {time} @@ -199,16 +204,55 @@ def result_data(form): 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} + {players} + {coords} {time} - '''.format(coords=coords_clause, - players=players_clause, worlds=worlds_clause, time=time_clause)) + '''.format(worlds=worlds_clause, players=players_clause, coords=coords_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_command cc + JOIN co_user cu ON cc.user = cu.id + WHERE 1 = 1 + {players} + {coords} + {time} + '''.format(players=players_clause, coords=coords_clause, time=time_clause)) + + # Login/Logout + if form.login_logout: + queries.append('''SELECT + 0 AS id, "session" AS type, cs.time AS unix, cu.user AS player, cs.action, "" AS data, cs.x, cs.y, cs.z, cw.world + FROM co_session cs + JOIN co_user cu ON cs.user = cu.id + JOIN co_world cw ON cs.wid = cw.id + WHERE 1 = 1 + {worlds} + {players} + {coords} + {time} + '''.format(worlds=worlds_clause, players=players_clause, coords=coords_clause, time=time_clause)) + + # Sign Place + if form.sign_place: + queries.append('''SELECT + 0 AS id, "sign" AS type, cs.time AS unix, cu.user AS player, "" AS action, cs.line_1 || "|" || cs.line_2 || "|" || cs.line_3 || "|" || cs.line_4 AS data, cs.x, cs.y, cs.z, cw.world + FROM co_sign cs + JOIN co_user cu ON cs.user = cu.id + JOIN co_world cw ON cs.wid = cw.id + WHERE 1 = 1 + {worlds} + {players} + {coords} + {time} + '''.format(worlds=worlds_clause, players=players_clause, coords=coords_clause, time=time_clause)) query = " UNION ".join(queries) - if query and form.limit_results != "-1": - query += " LIMIT {}, {}".format(form.start, form.limit_results) + if query: + query += " ORDER BY unix LIMIT {}, {}".format(form.start, form.page_size) print(query) if query: return CoResult.objects.raw(query)