Merge branch 'master' of https://git.etztech.xyz/Etzelia/MinecraftManagerDjango
commit
938ec65422
2
admin.py
2
admin.py
|
@ -5,6 +5,7 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
|||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from minecraft_manager.models import Application, Warning, Ticket, Player, IP, UserSettings, Alert, Note
|
||||
from minecraft_manager.api.admin import register as api_register
|
||||
|
||||
|
||||
class PlayerInline(admin.StackedInline):
|
||||
|
@ -100,6 +101,7 @@ try:
|
|||
admin.site.register(IP, IPAdmin)
|
||||
admin.site.register(Alert)
|
||||
admin.site.register(Note)
|
||||
api_register()
|
||||
except admin.sites.AlreadyRegistered:
|
||||
pass
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
from django.contrib import admin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from minecraft_manager.api.models import Token
|
||||
|
||||
|
||||
class TokenActiveFilter(admin.SimpleListFilter):
|
||||
title = _('Active')
|
||||
parameter_name = 'active'
|
||||
|
||||
def lookups(self, request, model_admin):
|
||||
return (
|
||||
('0', _('Active')),
|
||||
('1', _('Inactive')),
|
||||
)
|
||||
|
||||
def queryset(self, request, queryset):
|
||||
if self.value() == '0':
|
||||
return queryset.filter(active=True)
|
||||
if self.value() == '1':
|
||||
return queryset.filter(active=False)
|
||||
|
||||
|
||||
class TokenAdmin(admin.ModelAdmin):
|
||||
list_filter = (TokenActiveFilter,)
|
||||
list_display = ('display', 'active', 'web_get_permission', 'web_post_permission', 'plugin_get_permission',
|
||||
'plugin_post_permission', 'form_get_permission', 'form_post_permission', 'model_get_permission',
|
||||
'model_post_permission', 'stats_get_permission', 'stats_post_permission')
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': ('key', 'active', 'description')
|
||||
}),
|
||||
('Permissions', {
|
||||
'fields': ('web_get_permission', 'web_post_permission', 'plugin_get_permission', 'plugin_post_permission',
|
||||
'form_get_permission', 'form_post_permission', 'model_get_permission', 'model_post_permission',
|
||||
'stats_get_permission', 'stats_post_permission')
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
try:
|
||||
admin.site.register(Token, TokenAdmin)
|
||||
except admin.sites.AlreadyRegistered:
|
||||
pass
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import socket, requests, logging, os, datetime, pytz, mcstatus, discord
|
||||
import socket, requests, logging, os, datetime, pytz, mcstatus, random, string
|
||||
from minecraft_manager.models import Alert
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
|
@ -18,6 +18,7 @@ PLUGIN_ACCEPT = 'accept'
|
|||
PLUGIN_DENY = 'deny'
|
||||
PLUGIN_GLOBAL_CHAT = 'global'
|
||||
PLUGIN_STAFF_CHAT = 'staff'
|
||||
PLUGIN_DEMOTE = 'demote'
|
||||
|
||||
|
||||
def plugin(key, command):
|
||||
|
@ -140,3 +141,7 @@ def get_query():
|
|||
except:
|
||||
return {'max': 0, 'online': 0,
|
||||
'players': []}
|
||||
|
||||
|
||||
def generate_password(size=20):
|
||||
return "".join([random.choice(string.ascii_letters + string.digits) for idx in range(0, size)])
|
19
api/bot.py
19
api/bot.py
|
@ -1,6 +1,7 @@
|
|||
import discord, logging, re, sys, traceback, asyncio, datetime, os, time
|
||||
from minecraft_manager.models import Application, Player
|
||||
from minecraft_manager.api import api
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from django.db import close_old_connections
|
||||
from threading import Thread
|
||||
|
@ -67,7 +68,8 @@ class Discord(discord.Client):
|
|||
embed.add_field(name="{}[app ]search <username>".format(self.prefix), value="Search for applications by partial or exact username.")
|
||||
embed.add_field(name="{}[app ]info <app ID>".format(self.prefix), value="Get detailed information about a specific application.")
|
||||
embed.add_field(name="{}[app ]accept|deny <app ID>".format(self.prefix), value="Take action on an application.")
|
||||
embed.add_field(name="{}compare".format(self.prefix), value="Compare Discord users to the Whitelist")
|
||||
embed.add_field(name="{}demote <username>".format(self.prefix), value="Demote a player to the role given to accepted applications.")
|
||||
embed.add_field(name="{}compare".format(self.prefix), value="Compare Discord users to the Whitelist.")
|
||||
yield from self.discord_message(message.channel, embed)
|
||||
# APP COMMANDS WITH APP ID
|
||||
match = re.match("[{0}](?:app )?(i|info|a|accept|d|deny) (\d+)$".format(self.prefix), message.content)
|
||||
|
@ -103,7 +105,7 @@ class Discord(discord.Client):
|
|||
match = re.match("[{0}](?:app )?(?:search|info) (\S+)?$".format(self.prefix), message.content)
|
||||
if match:
|
||||
search = match.group(1)
|
||||
yield from self.discord_message(message.channel,"Searching for applications whose username contains '{0}'".format(search))
|
||||
yield from self.discord_message(message.channel, "Searching for applications whose username contains '{0}'".format(search))
|
||||
applications = Application.objects.filter(username__icontains=search)[:10]
|
||||
count = Application.objects.filter(username__icontains=search).count()
|
||||
if count > 0:
|
||||
|
@ -118,6 +120,19 @@ class Discord(discord.Client):
|
|||
else:
|
||||
info = "No applications matched that search."
|
||||
yield from self.discord_message(message.channel, info)
|
||||
# DEMOTE A PLAYER TO MEMBER
|
||||
match = re.match("[{0}]demote (\w+)$".format(self.prefix), message.content)
|
||||
if match:
|
||||
yield from self.delete_message(message)
|
||||
username = match.group(1)
|
||||
api.plugin(api.PLUGIN_DEMOTE, username)
|
||||
deactivated = ""
|
||||
if User.objects.filter(username__iexact=username).exists():
|
||||
user = User.objects.get(username__iexact=username)
|
||||
user.is_active = False
|
||||
user.save()
|
||||
deactivated = " and de-activated"
|
||||
yield from self.discord_message(message.channel, "{} has been demoted{}.".format(username, deactivated))
|
||||
# COMPARE DISCORD USERS TO WHITELIST
|
||||
match = re.match("[{0}]compare".format(self.prefix), message.content)
|
||||
if match:
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
from django.db import models
|
||||
from minecraft_manager.api.api import generate_password
|
||||
|
||||
|
||||
class Token(models.Model):
|
||||
key = models.CharField("Key", default=generate_password, max_length=50, unique=True)
|
||||
description = models.CharField("Description", max_length=200, blank=True)
|
||||
active = models.BooleanField("Active", default=True)
|
||||
web_get_permission = models.BooleanField("Web GET", default=False)
|
||||
web_post_permission = models.BooleanField("Web POST", default=False)
|
||||
plugin_get_permission = models.BooleanField("Plugin GET", default=False)
|
||||
plugin_post_permission = models.BooleanField("Plugin POST", default=False)
|
||||
form_get_permission = models.BooleanField("Form GET", default=False)
|
||||
form_post_permission = models.BooleanField("Form POST", default=False)
|
||||
model_get_permission = models.BooleanField("Model GET", default=False)
|
||||
model_post_permission = models.BooleanField("Model POST", default=False)
|
||||
stats_get_permission = models.BooleanField("Stats GET", default=False)
|
||||
stats_post_permission = models.BooleanField("Stats POST", default=False)
|
||||
|
||||
@property
|
||||
def display(self):
|
||||
return self.description if self.description else self.key
|
||||
|
||||
def __str__(self):
|
||||
return self.key
|
55
api/views.py
55
api/views.py
|
@ -1,43 +1,43 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import logging, random, string, datetime
|
||||
import logging, datetime
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth import update_session_auth_hash
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import JsonResponse, HttpResponse
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.views.generic import View
|
||||
from django.forms import modelform_factory
|
||||
|
||||
import minecraft_manager.forms as MCMForms
|
||||
import minecraft_manager.forms as mcm_forms
|
||||
from minecraft_manager.models import Player, UserSettings, Application, IP, Ticket, Warning
|
||||
import minecraft_manager.api.api as mcm_api
|
||||
from minecraft_manager.api.models import Token
|
||||
import minecraft_manager.utils as mcm_utils
|
||||
import minecraft_manager.external.stats as mcm_stats
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def request_allowed(request):
|
||||
def request_allowed(request, permission):
|
||||
is_authenticated = False
|
||||
if hasattr(request, 'user'):
|
||||
if hasattr(request.user, 'is_authenticated'):
|
||||
is_authenticated = request.user.is_authenticated
|
||||
password = getattr(settings, 'API_PASSWORD', None)
|
||||
get = request.GET
|
||||
post= request.POST
|
||||
post = request.POST
|
||||
request_password = None
|
||||
if 'api' in get:
|
||||
request_password = get['api']
|
||||
elif 'api' in post:
|
||||
request_password = post['api']
|
||||
correct_password = False
|
||||
if password and request_password:
|
||||
correct_password = request_password == password
|
||||
return is_authenticated or correct_password
|
||||
token_permission = False
|
||||
if Token.objects.filter(active=True, key=request_password).exists():
|
||||
token = Token.objects.get(active=True, key=request_password)
|
||||
token_permission = getattr(token, permission, False)
|
||||
return is_authenticated or token_permission
|
||||
|
||||
|
||||
def clean(model, data):
|
||||
|
@ -51,16 +51,12 @@ def clean(model, data):
|
|||
return cleaned
|
||||
|
||||
|
||||
def generate_password():
|
||||
return "".join([random.choice(string.ascii_letters + string.digits) for idx in range(0, 20)])
|
||||
|
||||
|
||||
class WebAPI(View):
|
||||
|
||||
def get(self, request, keyword):
|
||||
get = request.GET
|
||||
data = {'success': False, 'message': 'API failed'}
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'web_get_permission'):
|
||||
keyword = keyword.lower()
|
||||
if keyword == 'log':
|
||||
html_global = ""
|
||||
|
@ -102,7 +98,7 @@ class WebAPI(View):
|
|||
def post(self, request, keyword):
|
||||
post = request.POST
|
||||
data = {}
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'web_post_permission'):
|
||||
keyword = keyword.lower()
|
||||
if keyword == 'settings' and request.user.usersettings:
|
||||
for s in [a for a in dir(UserSettings) if not a.startswith('__') and not callable(getattr(UserSettings,a))]:
|
||||
|
@ -119,7 +115,7 @@ class WebAPI(View):
|
|||
else:
|
||||
return HttpResponse(form.as_p())
|
||||
elif keyword == 'alert':
|
||||
form = MCMForms.AlertForm(request.POST)
|
||||
form = mcm_forms.AlertForm(request.POST)
|
||||
if form.is_valid():
|
||||
if mcm_api.create_alert(form.cleaned_data['message']):
|
||||
data = {'success': True}
|
||||
|
@ -155,7 +151,7 @@ class PluginAPI(View):
|
|||
|
||||
def get(self, request, keyword):
|
||||
json = {'status': True, 'message': '', 'extra': ''}
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'plugin_get_permission'):
|
||||
get = request.GET
|
||||
keyword = keyword.lower()
|
||||
|
||||
|
@ -163,7 +159,7 @@ class PluginAPI(View):
|
|||
|
||||
def post(self, request, keyword):
|
||||
json = {'status': True, 'message': '', 'extra': ''}
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'plugin_post_permission'):
|
||||
post = request.POST
|
||||
keyword = keyword.lower()
|
||||
if "application" == keyword:
|
||||
|
@ -272,10 +268,13 @@ class PluginAPI(View):
|
|||
player.last_seen = timezone.now().strftime("%Y-%m-%d")
|
||||
player.save()
|
||||
if new_player and ip.associated:
|
||||
associated = []
|
||||
for assoc in ip.associated:
|
||||
if assoc.uuid is not player.uuid and assoc.is_banned:
|
||||
mcm_api.plugin("staff", "Server {0}'s IP matches the banned player {1}".format(player.username, assoc.username))
|
||||
mcm_api.discord_notification("{0}'s IP matches the banned player {1}".format(player.username, assoc.username), ping=True)
|
||||
associated.append(assoc)
|
||||
if associated:
|
||||
mcm_api.plugin("staff", "Server {0}'s IP matches the banned player(s) {1}".format(player.username, ", ".join([assoc.username for assoc in associated])))
|
||||
mcm_api.discord_notification("{0}'s IP matches the banned player(s) {1}".format(player.username, ", ".join([assoc.username for assoc in associated])), ping=True)
|
||||
json['status'] = True
|
||||
json['message'] = "Updated {0}".format(post['username'])
|
||||
elif "register" == keyword:
|
||||
|
@ -284,7 +283,7 @@ class PluginAPI(View):
|
|||
json['status'] = False
|
||||
json['message'] = "You are already registered. To change your password, contact an Admin."
|
||||
else:
|
||||
password = generate_password()
|
||||
password = mcm_api.generate_password()
|
||||
user = User.objects.create_user(username=player.username.lower(), password=password)
|
||||
user.save()
|
||||
player.auth_user = user
|
||||
|
@ -323,7 +322,7 @@ class FormAPI(View):
|
|||
|
||||
def get(self, request, request_model):
|
||||
html = ""
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'form_get_permission'):
|
||||
get = request.GET
|
||||
model = None
|
||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||
|
@ -332,7 +331,7 @@ class FormAPI(View):
|
|||
break
|
||||
if model:
|
||||
form = None
|
||||
for modelform in MCMForms.__all__():
|
||||
for modelform in mcm_forms.__all__():
|
||||
if modelform.Meta.model == model:
|
||||
form = modelform()
|
||||
break
|
||||
|
@ -346,7 +345,7 @@ class FormAPI(View):
|
|||
|
||||
def post(self, request, request_model):
|
||||
html = ""
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'form_post_permission'):
|
||||
post = request.POST
|
||||
model = None
|
||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||
|
@ -355,7 +354,7 @@ class FormAPI(View):
|
|||
break
|
||||
if model:
|
||||
form = None
|
||||
for modelform in MCMForms.__all__():
|
||||
for modelform in mcm_forms.__all__():
|
||||
if modelform.Meta.model == model:
|
||||
form = modelform(post)
|
||||
break
|
||||
|
@ -376,7 +375,7 @@ class ModelAPI(View):
|
|||
|
||||
def get(self, request, request_model):
|
||||
json = []
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'model_get_permission'):
|
||||
get = request.GET
|
||||
model = None
|
||||
for m in apps.get_app_config('minecraft_manager').get_models():
|
||||
|
@ -404,7 +403,7 @@ class StatsAPI(View):
|
|||
|
||||
def get(self, request):
|
||||
json = []
|
||||
if request_allowed(request):
|
||||
if request_allowed(request, 'stats_get_permission'):
|
||||
get = request.GET
|
||||
if 'stat' in get:
|
||||
if 'uuid' in get:
|
||||
|
|
|
@ -47,8 +47,6 @@ Optional
|
|||
|
||||
``SERVER_QUERY_IP`` - The full IP (and port) used to query your server. (This is used to get a player list)
|
||||
|
||||
``API_PASSWORD`` - The password used to validate API requests from unauthenticated sources.
|
||||
|
||||
``COREPROTECT_WEB_URL`` - The URL to your CoreProtect Web UI, if it exists.
|
||||
|
||||
``COREPROTECT_ACTIVITY_URL`` - The URL to your CoreProtect Activity Web UI, if it exists.
|
||||
|
|
|
@ -47,6 +47,7 @@ Add MCM urls to your ``urls.py``
|
|||
Django doesn't provide login/logout templates by default, so MCM has some generic ones if needed.
|
||||
::
|
||||
|
||||
from django.contrib.auth import views as auth_views
|
||||
path('accounts/login/', auth_views.LoginView.as_view(template_name='minecraft_manager/login.html'), name='login'),
|
||||
path('accounts/logout/', auth_views.LogoutView.as_view(template_name='minecraft_manager/logged_out.html'), name='logout'),
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import os, json
|
||||
import os, json, copy
|
||||
from minecraft_manager.models import Player
|
||||
from django.conf import settings
|
||||
|
||||
|
@ -16,9 +16,13 @@ def get_stats():
|
|||
with open(stats_dir + "/" + filename) as json_file:
|
||||
raw = json.load(json_file)['stats']
|
||||
clean = {}
|
||||
for r in raw:
|
||||
if not any(sf.lower() in r.lower() for sf in stats_filter):
|
||||
clean[r] = raw[r]
|
||||
raw_copy = copy.deepcopy(raw)
|
||||
for ra in raw_copy:
|
||||
if not any(sf.lower() in ra.lower() for sf in stats_filter):
|
||||
for r in raw_copy[ra]:
|
||||
if any(sf.lower() in r.lower() for sf in stats_filter):
|
||||
del raw[ra][r]
|
||||
clean[ra] = raw[ra]
|
||||
uuid = filename.replace(".json", "")
|
||||
stats[uuid] = clean
|
||||
return stats
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import os
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from django.conf import settings
|
||||
from minecraft_manager.models import Application, Player, Ticket, Warning, IP
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
def overview_data():
|
||||
data = {}
|
||||
|
||||
# Setup
|
||||
with open(os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json'), encoding='utf-8') as f:
|
||||
bans = json.load(f)
|
||||
|
||||
|
||||
# Totals
|
||||
data['total'] = {
|
||||
'application': {
|
||||
'accepted': Application.objects.filter(accepted=True).count(),
|
||||
'denied': Application.objects.filter(accepted=False).count(),
|
||||
'all': Application.objects.count()
|
||||
},
|
||||
'player': {
|
||||
'banned': len(bans),
|
||||
'unbanned': Player.objects.count() - len(bans),
|
||||
'all': Player.objects.count()
|
||||
},
|
||||
'ticket': {
|
||||
'claimed': Ticket.objects.filter(staff__isnull=False).count(),
|
||||
'unclaimed': Ticket.objects.filter(staff__isnull=True).count(),
|
||||
'resolved': Ticket.objects.filter(resolved=True).count(),
|
||||
'unresolved': Ticket.objects.filter(resolved=False).count(),
|
||||
'all': Ticket.objects.count()
|
||||
},
|
||||
'warning': Warning.objects.count(),
|
||||
'ip': IP.objects.count()
|
||||
}
|
||||
|
||||
# Averages
|
||||
data['average'] = {
|
||||
'age': round(sum([application.age for application in Application.objects.filter(accepted=True)]) /
|
||||
data['total']['application']['accepted'] if data['total']['application']['accepted'] != 0 else 1, 2)
|
||||
}
|
||||
|
||||
# Percentage
|
||||
data['percentage'] = {
|
||||
'accepted': round((data['total']['application']['accepted'] /
|
||||
data['total']['application']['all'] if data['total']['application']['all'] != 0 else 1) * 100, 2),
|
||||
'banned': round((data['total']['player']['banned'] /
|
||||
data['total']['player']['all'] if data['total']['player']['all'] != 0 else 1) * 100, 2),
|
||||
'applied': round((data['total']['application']['all'] / data['total']['player']['all']) * 100, 2)
|
||||
}
|
||||
|
||||
# Unique logins
|
||||
now = datetime.now()
|
||||
day = now - timedelta(days=1)
|
||||
week = now - timedelta(weeks=1)
|
||||
month = now - timedelta(weeks=4)
|
||||
data['unique'] = {
|
||||
'day': Player.objects.filter(last_seen__range=[day, now]).count(),
|
||||
'week': Player.objects.filter(last_seen__range=[week, now]).count(),
|
||||
'month': Player.objects.filter(last_seen__range=[month, now]).count()
|
||||
}
|
||||
|
||||
# Admin
|
||||
data['resolved'] = []
|
||||
for user in User.objects.all().order_by('username'):
|
||||
data['resolved'].append({
|
||||
'active': user.is_active,
|
||||
'username': user.username,
|
||||
'tickets': Ticket.objects.filter(staff=user).count()
|
||||
})
|
||||
|
||||
return data
|
|
@ -6,141 +6,72 @@
|
|||
{% endblock %}
|
||||
{% block section %}
|
||||
<div id="content">
|
||||
{% if request.user.is_staff %}
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-body">
|
||||
<h4><span class="label label-danger">Admin Area</span></h4>
|
||||
<h3>Resolved Tickets</h3>
|
||||
<div class="row">
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3 class="center">Applications{% if form.apps.unanswered > 0 %} <a href="{% url "application" %}?accepted=">({{ form.apps.unanswered }} Unanswered)</a>{% endif %}</h3>
|
||||
<canvas id="appChart" style="width:30em;height:15em" ></canvas>
|
||||
</div>
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3 class="center">Tickets{% if form.tickets.unclaimed > 0 %} <a href="{% url "ticket" %}?claimed=false">({{ form.tickets.unclaimed }} Unclaimed)</a>{% endif %}</h3>
|
||||
<h4></h4>
|
||||
<canvas id="ticketChart" style="width:30em;height:15em" ></canvas>
|
||||
</div>
|
||||
{% for staff in data.resolved %}
|
||||
<div class="col-xs-6 col-md-4">
|
||||
<p><span class="label label-{% if staff.active %}success{% else %}danger{% endif %}">{% if staff.active %}Active{% else %}Inactive{% endif %}</span> {{ staff.username }}: {{ staff.tickets }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-18 col-md-12">
|
||||
<h3 class="center">Totals</h3>
|
||||
<canvas id="totalChart" style="width:30em;height:15em" ></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Applications: {{ data.total.application.all }}</h3>
|
||||
<p>Accepted: {{ data.total.application.accepted }}</p>
|
||||
<p>Denied: {{ data.total.application.denied }}</p>
|
||||
<br/>
|
||||
<h3>Tickets: {{ data.total.ticket.all }}</h3>
|
||||
<p>Claimed: {{ data.total.ticket.claimed }}</p>
|
||||
<p>Unclaimed: {{ data.total.ticket.unclaimed }}</p>
|
||||
<p>Resolved: {{ data.total.ticket.resolved }}</p>
|
||||
<p>Unresolved: {{ data.total.ticket.unresolved }}</p>
|
||||
</div>
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Players: {{ data.total.player.all }}</h3>
|
||||
<p>Not Banned: {{ data.total.player.unbanned }}</p>
|
||||
<p>Banned: {{ data.total.player.banned }}</p>
|
||||
<br/>
|
||||
<h3>Warnings: {{ data.total.warning }}</h3>
|
||||
<br/>
|
||||
<h3>IPs: {{ data.total.ip }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var app_data = {
|
||||
labels: [
|
||||
"Unanswered",
|
||||
"Denied",
|
||||
"Accepted"
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
data: [{{ form.apps.unanswered }}, {{ form.apps.denied }}, {{ form.apps.accepted }}],
|
||||
backgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
],
|
||||
hoverBackgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
var app_options = {
|
||||
animation: {
|
||||
animateRotate: true
|
||||
}
|
||||
}
|
||||
var app_ctx = $("#appChart");
|
||||
var app_chart = new Chart(app_ctx,
|
||||
{
|
||||
type: 'pie',
|
||||
data: app_data,
|
||||
options: app_options
|
||||
}
|
||||
);
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Average Age: {{ data.average.age }}</h3>
|
||||
</div>
|
||||
<div class="col-xs-9 col-md-6">
|
||||
|
||||
var ticket_data = {
|
||||
labels: [
|
||||
"Unclaimed",
|
||||
"Claimed",
|
||||
"Resolved"
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
data: [{{ form.tickets.unclaimed }}, {{ form.tickets.claimed }}, {{ form.tickets.resolved }}],
|
||||
backgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
],
|
||||
hoverBackgroundColor: [
|
||||
"#8080ff",
|
||||
"#ff3333",
|
||||
"#33cc33"
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
var ticket_options = {
|
||||
animation: {
|
||||
animateRotate: true
|
||||
}
|
||||
}
|
||||
var ticket_ctx = $("#ticketChart");
|
||||
var ticket_chart = new Chart(ticket_ctx,
|
||||
{
|
||||
type: 'pie',
|
||||
data: ticket_data,
|
||||
options: ticket_options
|
||||
}
|
||||
);
|
||||
|
||||
var total_data = {
|
||||
labels: [
|
||||
"Applications",
|
||||
"Players",
|
||||
"IPs",
|
||||
"Tickets",
|
||||
"Warnings"
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
label: "Totals",
|
||||
data: [
|
||||
{{ form.counts.applications }},
|
||||
{{ form.counts.players }},
|
||||
{{ form.counts.ips }},
|
||||
{{ form.counts.tickets }},
|
||||
{{ form.counts.warnings }}
|
||||
],
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(255, 206, 86, 0.2)',
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(153, 102, 255, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255,99,132,1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(153, 102, 255, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
var total_ctx = $("#totalChart");
|
||||
var total_chart = new Chart(total_ctx,
|
||||
{
|
||||
type: 'bar',
|
||||
data: total_data
|
||||
}
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Acceptance Rate: {{ data.percentage.accepted }}%</h3>
|
||||
<h3>Application/Player Rate: {{ data.percentage.applied }}%</h3>
|
||||
</div>
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Ban Rate: {{ data.percentage.banned }}%</h3>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Logins Today: {{ data.unique.day }}</h3>
|
||||
<h3>Logins Last Month: {{ data.unique.month }}</h3>
|
||||
</div>
|
||||
<div class="col-xs-9 col-md-6">
|
||||
<h3>Logins Last Week: {{ data.unique.week }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock section %}
|
|
@ -17,7 +17,7 @@ def get_sidebar(current_app, request):
|
|||
|
||||
ret = '<li {}><a href="{}"><span class="glyphicon glyphicon-home"></span> Overview</a></li>'.format('class="active"' if current_app == 'overview' else "", reverse('overview'))
|
||||
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-ban-circle"></span> Bans</a></li>'.format('class="active"' if current_app == 'ban' else '', reverse('ban'))
|
||||
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-inbox"></span> Alerts{}</a></li>'.format('class="active"' if current_app == 'alert' else '', reverse('alert'), unseen_html)
|
||||
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-bell"></span> Alerts{}</a></li>'.format('class="active"' if current_app == 'alert' else '', reverse('alert'), unseen_html)
|
||||
|
||||
# Models
|
||||
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-file"></span> Applications</a></li>'.format('class="active"' if current_app == 'application' else '', reverse('application'))
|
||||
|
|
17
views.py
17
views.py
|
@ -14,6 +14,7 @@ from django.views.generic import View
|
|||
from django.contrib.auth.models import User
|
||||
from minecraft_manager.models import Application as AppModel, Player as PlayerModel, Ticket as TicketModel, Warning as WarningModel, IP as IPModel, Alert as AlertModel, Note as NoteModel, UserSettings as UserSettingsModel
|
||||
from minecraft_manager.forms import WarningForm, NoteForm
|
||||
from minecraft_manager.overview import overview_data
|
||||
import minecraft_manager.api.api as API
|
||||
|
||||
import subprocess
|
||||
|
@ -33,19 +34,9 @@ class Overview(View):
|
|||
request.user.usersettings = UserSettingsModel(auth_user=request.user)
|
||||
request.user.usersettings.last_ip = user_ip
|
||||
request.user.usersettings.save()
|
||||
unanswered_apps = AppModel.objects.filter(accepted=None).count()
|
||||
accepted_apps = AppModel.objects.filter(accepted=True).count()
|
||||
denied_apps = AppModel.objects.filter(accepted=False).count()
|
||||
unclaimed_tickets = TicketModel.objects.filter(staff=None, resolved=False).count()
|
||||
claimed_tickets = TicketModel.objects.filter(staff__isnull=False, resolved=False).count()
|
||||
resolved_tickets = TicketModel.objects.filter(resolved=True).count()
|
||||
counts = {'applications': AppModel.objects.count(), 'players': PlayerModel.objects.count(),
|
||||
"ips": IPModel.objects.count(), "tickets": TicketModel.objects.count(),
|
||||
"warnings": WarningModel.objects.count()}
|
||||
form = {'apps': {'unanswered': unanswered_apps, 'accepted': accepted_apps, 'denied': denied_apps},
|
||||
'tickets': {'unclaimed': unclaimed_tickets, 'claimed': claimed_tickets, 'resolved': resolved_tickets},
|
||||
'counts': counts}
|
||||
return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'form': form})
|
||||
|
||||
|
||||
return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'data': overview_data()})
|
||||
|
||||
|
||||
class CoreProtect(View):
|
||||
|
|
Loading…
Reference in New Issue