1
0
mirror of https://github.com/RaidMax/IW4M-Admin.git synced 2025-06-10 23:31:13 -05:00

final changes for 2.0 release

This commit is contained in:
RaidMax
2018-04-21 17:18:20 -05:00
parent a515d9688c
commit c7b4706e78
20 changed files with 230 additions and 87 deletions

View File

@ -36,6 +36,9 @@
<Compile Include="master\context\base.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="master\context\history.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="master\context\__init__.py">
<SubType>Code</SubType>
</Compile>
@ -51,6 +54,9 @@
<Compile Include="Master\resources\authenticate.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="master\resources\history_graph.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\instance.py">
<SubType>Code</SubType>
</Compile>
@ -88,37 +94,12 @@
<Folder Include="master\schema\" />
<Folder Include="Master\resources\" />
<Folder Include="Master\static\" />
<Folder Include="Master\static\content\" />
<Folder Include="Master\static\fonts\" />
<Folder Include="Master\static\scripts\" />
<Folder Include="Master\templates\" />
</ItemGroup>
<ItemGroup>
<None Include="FolderProfile.pubxml" />
<Content Include="master\config\master.json" />
<Content Include="requirements.txt" />
<Content Include="Master\static\content\bootstrap.css" />
<Content Include="Master\static\content\bootstrap.min.css" />
<Content Include="Master\static\content\site.css" />
<Content Include="Master\static\fonts\glyphicons-halflings-regular.eot" />
<Content Include="Master\static\fonts\glyphicons-halflings-regular.svg" />
<Content Include="Master\static\fonts\glyphicons-halflings-regular.ttf" />
<Content Include="Master\static\fonts\glyphicons-halflings-regular.woff" />
<Content Include="Master\static\scripts\bootstrap.js" />
<Content Include="Master\static\scripts\bootstrap.min.js" />
<Content Include="Master\static\scripts\jquery-1.10.2.intellisense.js" />
<Content Include="Master\static\scripts\jquery-1.10.2.js" />
<Content Include="Master\static\scripts\jquery-1.10.2.min.js" />
<Content Include="Master\static\scripts\jquery-1.10.2.min.map" />
<Content Include="Master\static\scripts\jquery.validate-vsdoc.js" />
<Content Include="Master\static\scripts\jquery.validate.js" />
<Content Include="Master\static\scripts\jquery.validate.min.js" />
<Content Include="Master\static\scripts\jquery.validate.unobtrusive.js" />
<Content Include="Master\static\scripts\jquery.validate.unobtrusive.min.js" />
<Content Include="Master\static\scripts\modernizr-2.6.2.js" />
<Content Include="Master\static\scripts\respond.js" />
<Content Include="Master\static\scripts\respond.min.js" />
<Content Include="Master\static\scripts\_references.js" />
<Content Include="Master\templates\index.html" />
<Content Include="Master\templates\layout.html" />
</ItemGroup>

View File

@ -1,11 +1,15 @@
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
from master.context.history import History
from master.schema.instanceschema import InstanceSchema
import time
class Base():
def __init__(self):
self.history = History()
self.instance_list = {}
self.server_list = {}
self.token_list = {}
self.scheduler = BackgroundScheduler()
self.scheduler.start()
@ -16,6 +20,23 @@ class Base():
name='Remove stale instances if no heartbeat in 120 seconds',
replace_existing=True
)
self.scheduler.add_job(
func=self._update_history_count,
trigger=IntervalTrigger(seconds=30),
id='update history',
name='update client and instance count every 30 seconds',
replace_existing=True
)
def _update_history_count(self):
servers = [instance.servers for instance in self.instance_list.values()]
servers = [inner for outer in servers for inner in outer]
client_num = 0
# force it being a number
for server in servers:
client_num += server.clientnum
self.history.add_client_history(client_num)
self.history.add_instance_history(len(self.instance_list))
def _remove_staleinstances(self):
for key, value in list(self.instance_list.items()):
@ -28,9 +49,6 @@ class Base():
def get_instances(self):
return self.instance_list.values()
def get_server_count(self):
return self.server_list.count
def get_instance_count(self):
return self.instance_list.count

View File

@ -0,0 +1,23 @@
import time
from random import randint
class History():
def __init__(self):
self.client_history = list()
self.instance_history = list()
def add_client_history(self, client_num):
if len(self.client_history) > 1440:
self.client_history = self.client_history[1:]
self.client_history.append({
'count' : client_num,
'time' : int(time.time())
})
def add_instance_history(self, instance_num):
if len(self.instance_history) > 1440:
self.instance_history = self.instance_history[1:]
self.instance_history.append({
'count' : instance_num,
'time' : int(time.time())
})

View File

@ -0,0 +1,46 @@
from flask_restful import Resource
from pygal.style import Style
from master import ctx
import pygal
import timeago
from math import ceil
class HistoryGraph(Resource):
def get(self, history_count):
try:
custom_style = Style(
background='transparent',
plot_background='transparent',
foreground='rgba(109, 118, 126, 0.3)',
foreground_strong='rgba(109, 118, 126, 0.3)',
foreground_subtle='rgba(109, 118, 126, 0.3)',
opacity='0.1',
opacity_hover='0.2',
transition='100ms ease-in',
colors=('#007acc', '#749363')
)
graph = pygal.StackedLine(
interpolate='cubic',
interpolation_precision=3,
#x_labels_major_every=100,
#x_labels_major_count=500,
stroke_style={'width': 0.4},
show_dots=False,
show_legend=False,
fill=True,
style=custom_style,
disable_xml_declaration=True)
instance_count = [history['time'] for history in ctx.history.instance_history][-history_count:]
if len(instance_count) > 0:
graph.x_labels = [ timeago.format(instance_count[0])]
graph.add('Instance Count', [history['count'] for history in ctx.history.instance_history][-history_count:])
graph.add('Client Count', [history['count'] for history in ctx.history.client_history][-history_count:])
return { 'message' : graph.render(),
'data_points' : len(instance_count)
}, 200
except Exception as e:
return { 'message' : str(e) }, 500

View File

@ -4,8 +4,10 @@ from master.resources.null import Null
from master.resources.instance import Instance
from master.resources.authenticate import Authenticate
from master.resources.version import Version
from master.resources.history_graph import HistoryGraph
api.add_resource(Null, '/null')
api.add_resource(Instance, '/instance/', '/instance/<string:id>')
api.add_resource(Version, '/version')
api.add_resource(Authenticate, '/authenticate')
api.add_resource(Authenticate, '/authenticate')
api.add_resource(HistoryGraph, '/history/', '/history/<int:history_count>')

View File

@ -1,5 +1,46 @@
{% extends "layout.html" %}
{% block content %}
<div class="row">
<div class="col-12">
<figure>
<div id="history_graph">{{history_graph|safe}}</div>
<figcaption class="float-right pr-3 mr-4">
<span id="history_graph_zoom_out" class="h4 oi oi-zoom-out text-muted" style="cursor:pointer;"></span>
<span id="history_graph_zoom_in" class="h4 oi oi-zoom-in text-muted" style="cursor:pointer;"></span>
</figcaption>
</figure>
</div>
</div>
{% endblock %}
{% block scripts %}
<script type="text/javascript" src="http://kozea.github.com/pygal.js/latest/pygal-tooltips.min.js"></script>
<script>
let dataPoints = {{data_points}};
let zoomLevel = Math.ceil(dataPoints / 2);
//console.log(dataPoints);
function updateHistoryGraph() {
$.get('/history/' + zoomLevel)
.done(function (content) {
$('#history_graph').html(content.message);
});
}
setInterval(updateHistoryGraph, 30000);
$('#history_graph_zoom_out').click(function () {
// console.log(zoomLevel);
zoomLevel = zoomLevel * 2 < dataPoints ? Math.ceil(zoomLevel * 2) : dataPoints;
updateHistoryGraph();
});
$('#history_graph_zoom_in').click(function () {
// console.log(zoomLevel);
zoomLevel = zoomLevel / 2 > 2 ? Math.ceil(zoomLevel / 2) : 2;
updateHistoryGraph();
});
</script>
{% endblock %}

View File

@ -1,44 +1,32 @@
<!DOCTYPE html>
<html>
<html class="bg-dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IW4MAdmin Master | {{ title }}</title>
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
<script src="/static/scripts/modernizr-2.6.2.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css" integrity="sha256-BJ/G+e+y7bQdrYkS2RBTyNfBHpA9IuGaPmf9htub5MQ=" crossorigin="anonymous" />
<style type="text/css">
.active {
stroke-width: initial !important;
}
.oi:hover {
color: #fff !important;
}
</style>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="/" class="navbar-brand">Application name</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="{{ url_for('home') }}">Home</a></li>
</ul>
</div>
</div>
</div>
<body class="bg-dark">
<div class="container body-content">
<div class="container body-content bg-dark">
{% block content %}{% endblock %}
<hr />
<footer>
</footer>
</div>
<script src="/static/scripts/jquery-1.10.2.js"></script>
<script src="/static/scripts/bootstrap.js"></script>
<script src="/static/scripts/respond.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@ -5,13 +5,14 @@ Routes and views for the flask application.
from datetime import datetime
from flask import render_template
from master import app
from master.resources.history_graph import HistoryGraph
@app.route('/')
@app.route('/home')
def home():
"""Renders the home page."""
_history_graph = HistoryGraph().get(500)
return render_template(
'index.html',
title='Home Page',
year=datetime.now().year,
)
title='API Overview',
history_graph = _history_graph[0]['message'],
data_points = _history_graph[0]['data_points']
)