Skip to content

Commit

Permalink
Merge branch 'graphing'
Browse files Browse the repository at this point in the history
Conflicts:
	Core/maps.py
  • Loading branch information
ellonweb committed Aug 26, 2011
2 parents e31428a + 3d514a7 commit 7e016ea
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Arthur/graphs/.gitignore
@@ -0,0 +1,2 @@
*
!.gitignore
1 change: 1 addition & 0 deletions Arthur/settings.py
Expand Up @@ -15,6 +15,7 @@
'django.middleware.common.CommonMiddleware',
'Arthur.errors.exceptions',
'Arthur.errors.db',
'Arthur.views.graphs.graphs',
)

APPEND_SLASH = True
Expand Down
10 changes: 10 additions & 0 deletions Arthur/templates/alliance.tpl
Expand Up @@ -116,6 +116,16 @@

<p>&nbsp;</p>

<table cellspacing="1" cellpadding="3" class="black">
<tr class="header"><th colspan="2">Graphs</th></tr>
<tr class="datahigh" align="center">
<td><img src="{%url "allianceG", "values", alliance.name%}" /></td>
<td><img src="{%url "allianceG", "ranks", alliance.name%}" /></td>
</tr>
</table>

<p>&nbsp;</p>

{% call halliance(alliance, history) %}Last 12 Ticks (<a href="{%url "halliance", alliance.name, 72%}">View more</a>){% endcall %}

<p>&nbsp;</p>
Expand Down
10 changes: 10 additions & 0 deletions Arthur/templates/galaxy.tpl
Expand Up @@ -45,6 +45,16 @@

<p>&nbsp;</p>

<table cellspacing="1" cellpadding="3" class="black">
<tr class="header"><th colspan="2">Graphs</th></tr>
<tr class="datahigh" align="center">
<td><img src="{%url "galaxyG", "values", galaxy.x, galaxy.y%}" /></td>
<td><img src="{%url "galaxyG", "ranks", galaxy.x, galaxy.y%}" /></td>
</tr>
</table>

<p>&nbsp;</p>

{% call exiletable(exiles) %}Recent Planet Movements <a href="{% url "galaxy_exiles", galaxy.x, galaxy.y %}">(View more)</a>{% endcall %}

<p>&nbsp;</p>
Expand Down
10 changes: 10 additions & 0 deletions Arthur/templates/planet.tpl
Expand Up @@ -158,6 +158,16 @@

<p>&nbsp;</p>

<table cellspacing="1" cellpadding="3" class="black">
<tr class="header"><th colspan="2">Graphs</th></tr>
<tr class="datahigh" align="center">
<td><img src="{%url "planetG", "values", planet.x, planet.y, planet.z%}" /></td>
<td><img src="{%url "planetG", "ranks", planet.x, planet.y, planet.z%}" /></td>
</tr>
</table>

<p>&nbsp;</p>

{% call hplanet(planet, history) %}Last 12 Ticks (<a href="{%url "hplanet", planet.x, planet.y, planet.z, 72%}">View more</a>){% endcall %}

<p>&nbsp;</p>
Expand Down
1 change: 1 addition & 0 deletions Arthur/views/__init__.py
Expand Up @@ -33,6 +33,7 @@
(r'', include('Arthur.views.exiles')),
(r'', include('Arthur.views.attack')),
(r'', include('Arthur.views.scans')),
(r'', include('Arthur.views.graphs')),
)

from Arthur.views import home
Expand Down
206 changes: 206 additions & 0 deletions Arthur/views/graphs.py
@@ -0,0 +1,206 @@
# This file is part of Merlin/Arthur.
# Merlin/Arthur is the Copyright (C)2011 of Elliot Rosemarine.

# Individual portions may be copyright by individual contributors, and
# are included in this collective work with permission of the copyright
# owners.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

import os
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.ticker import FuncFormatter
from django.conf.urls.defaults import include, patterns, url
from django.http import HttpResponse, HttpResponseNotFound
from Core.db import session
from Core.maps import Galaxy, GalaxyHistory, Planet, PlanetHistory, Alliance, AllianceHistory
from Arthur.loadable import loadable, load

urlpatterns = patterns('',
url(r'^graphs/(?P<type>values|ranks)/', include(patterns('Arthur.views.graphs',
url(r'^(?P<x>\d+)[. :\-](?P<y>\d+)[. :\-](?P<z>\d+)', 'planet', name="planetG"),
url(r'^(?P<x>\d+)[. :\-](?P<y>\d+)', 'galaxy', name="galaxyG"),
url(r'^(?P<name>[^/]+)', 'alliance', name="allianceG"),
))),
)

white = '#ffffff'
black = '#000000'
red = '#ff0000'
green = '#00ff00'
blue = '#0000ff'
yellow = '#ffff00'
magenta = '#ff00ff'
cyan = '#00ffff'
pink = '#ff6666'
bgcolor = '#292D3A'
axcolor = '#373B48'

class graphs(loadable):
_num2short_scale = 1
width = 500
left, right = yellow, green

plot = {'values' : lambda ax, Q: ((ax[1].plot(Q[0],Q[1],yellow)[0], "Size",),
(ax[2].plot(Q[0],Q[2],green)[0], "Score",),
(ax[2].plot(Q[0],Q[3],magenta)[0], "Value",),
),
'ranks' : lambda ax, Q: ((ax[1].plot(Q[0],Q[1],yellow)[0], "Size",),
(ax[2].plot(Q[0],Q[2],green)[0], "Score",),
(ax[2].plot(Q[0],Q[3],magenta)[0], "Value",),
),
}

def process_request(self, request):
if request.path_info == "/draw":
if 'REDIRECT_URL' in request.META and request.META['REDIRECT_URL'].startswith("/graphs"):
request.path_info = request.META['REDIRECT_URL']
del request.META['REDIRECT_URL']

def execute(self, request, user, type, x=None, y=None, z=None, name=None):
width = self.width *(8.0/640)
height = width *(6.0/8.0)
fig = plt.figure(figsize=(width,height,), facecolor=bgcolor, edgecolor=bgcolor)
try:
## Set up the axes
fig.subplots_adjust(left=0.08,right=1-0.08,bottom=0.05,top=1-0.075)
ax = {}

ax[0] = fig.add_subplot(111)
ax[0].yaxis.set_visible(False)
ax[0].set_axis_bgcolor(axcolor)

ax[1] = fig.add_axes(ax[0].get_position(True), sharex=ax[0], frameon=False)
ax[1].yaxis.tick_left()
ax[1].yaxis.set_label_position('left')
ax[1].xaxis.set_visible(False)

ax[2] = fig.add_axes(ax[0].get_position(True), sharex=ax[1], frameon=False)
ax[2].yaxis.tick_right()
ax[2].yaxis.set_label_position('right')
ax[2].xaxis.set_visible(False)

## Load the data
o = self.load(x,y,z,name)
if not o:
return self.error(fig,"Unable to load target x:%s y:%s z:%s name:%s"%(x,y,z,name,))

q = self.query[type].filter_by(current=o)
d = zip(*q.all())

## Plot the data and draw a legend
leg = ax[0].legend(*zip(*self.plot[type](ax,d)), loc='upper left',
ncol=len(d)-1, columnspacing=1,
handlelength=0.1, handletextpad=0.5)
leg.get_frame().set_facecolor(black)
leg.get_frame().set_alpha(0.5)
for t in leg.get_texts():
t.set_color(white)
t.set_fontsize(10)

## Sort out the axes
ax[0].tick_params(labelcolor=white)

ax[1].tick_params(labelcolor=self.left)
ax[1].yaxis.set_major_formatter(FuncFormatter(lambda x,pos:self.num2short(x)))

ax[2].tick_params(labelcolor=self.right)
ax[2].yaxis.set_major_formatter(FuncFormatter(lambda x,pos:self.num2short(x)))

if type == "ranks":
# for ranks, invert axes, 0 at the top
ax[0].set_ylim(top=0, bottom=ax[0].get_ylim()[1])
ax[1].set_ylim(top=0, bottom=ax[1].get_ylim()[1])
ax[2].set_ylim(top=0, bottom=ax[2].get_ylim()[1])
else:
# for values, scale all the way down to 0
ax[0].set_ylim(bottom=0)
ax[1].set_ylim(bottom=0)
ax[2].set_ylim(bottom=0)

## Fix some odd behaviour
ax[0].set_xlim(d[0][0], d[0][-1]) #align first tick to left
ax[2].axvline(x=d[0][0], color=black) #fix gfx glitch on left yaxis

## Title
title = self.title(o) + (" Rank" if type == "ranks" else "") + " History"
fig.suptitle(title, color=white, fontsize=18)

return self.render(fig, self.cache(request, type))
finally:
plt.close(fig)

def cache(self, request, type):
path = "Arthur"+request.path_info
dir = os.path.dirname(path)
if not os.path.exists(dir):
try:
os.makedirs(dir)
except OSError:
return ""
return path

def render(self, fig, path=""):
canvas = FigureCanvas(fig)

try:
with open(path, "wb") as file:
canvas.print_png(file)
except IOError:
pass

response = HttpResponse(content_type='image/png')
canvas.print_png(response)
return response

def error(self, fig, msg):
fig.suptitle(msg, color=white)
return HttpResponseNotFound(self.render(fig), content_type='image/png')

@load
class planet(graphs):
load = staticmethod(lambda x, y, z, name: Planet.load(x,y,z))
title = staticmethod(lambda o: "%s:%s:%s" %(o.x,o.y,o.z,))
query = {'values' : session.query(PlanetHistory.tick, PlanetHistory.size, PlanetHistory.score, PlanetHistory.value),
'ranks' : session.query(PlanetHistory.tick, PlanetHistory.size_rank, PlanetHistory.score_rank, PlanetHistory.value_rank),
}

@load
class galaxy(graphs):
load = staticmethod(lambda x, y, z, name: Galaxy.load(x,y))
title = staticmethod(lambda o: "%s:%s" %(o.x,o.y,))
query = {'values' : session.query(GalaxyHistory.tick, GalaxyHistory.size, GalaxyHistory.score, GalaxyHistory.value),
'ranks' : session.query(GalaxyHistory.tick, GalaxyHistory.size_rank, GalaxyHistory.score_rank, GalaxyHistory.value_rank),
}

@load
class alliance(graphs):
load = staticmethod(lambda x, y, z, name: Alliance.load(name, exact=True))
title = staticmethod(lambda o: "%s" %(o.name,))
query = {'values' : session.query(AllianceHistory.tick, AllianceHistory.size, AllianceHistory.score, AllianceHistory.members),
'ranks' : session.query(AllianceHistory.tick, AllianceHistory.size_rank, AllianceHistory.score_rank, AllianceHistory.points_rank),
}
plot = {'values' : lambda ax, Q: ((ax[1].plot(Q[0],Q[1],yellow)[0], "Size",),
(ax[2].plot(Q[0],Q[2],green)[0], "Score",),
(ax[0].plot(Q[0],Q[3],pink)[0], "Members",),
),
'ranks' : lambda ax, Q: ((ax[1].plot(Q[0],Q[1],yellow)[0], "Size",),
(ax[2].plot(Q[0],Q[2],green)[0], "Score",),
(ax[0].plot(Q[0],Q[3],cyan)[0], "Points",),
),
}
5 changes: 3 additions & 2 deletions Core/loadable.py
Expand Up @@ -53,14 +53,15 @@ def url(self, text, user):
else:
return text

_num2short_scale = 10
def num2short(self,num):
prefix = ("","-",)[num<0]
num = abs(num)
flt2int = lambda x: int(x) if x.is_integer() else x
try:
if num/10000000 >= 1:
if num/(1000000 * self._num2short_scale) >= 1:
return prefix+ str(flt2int(round(num/1000000.0,1)))+"m"
elif num/10000 >= 1:
elif num/(1000 * self._num2short_scale) >= 1:
return prefix+ str(flt2int(round(num/1000.0,1)))+"k"
else:
return prefix+ str(flt2int(round(num)))
Expand Down
4 changes: 2 additions & 2 deletions Core/maps.py
Expand Up @@ -931,7 +931,7 @@ def history(self, tick):
return self.history_loader.filter_by(tick=tick).first()

@staticmethod
def load(name, alias=True, active=True):
def load(name, alias=True, active=True, exact=False):
if not name:
return None

Expand All @@ -947,7 +947,7 @@ def load(name, alias=True, active=True):
Q = session.query(Alliance).filter_by(active=True)
for filter in filters:
alliance = Q.filter(filter).first()
if alliance is not None:
if alliance is not None or exact is True:
break

if alliance is not None or active == True:
Expand Down
22 changes: 22 additions & 0 deletions README
Expand Up @@ -14,6 +14,20 @@ Additional Arthur requirements:
Django
Jinja2 2.6

Additional Graphing requirements:
numpy 1.1
matplotlib 1.0

Additional POSIX environment (e.g. Linux) requirements:
Create and chmod 777 these directories:
/var/www/.matplotlib
/merlin/Arthur/graphs
Create and chmod 666 these files:
/merlin/dumplog.txt
/merlin/errorlog.txt
/merlin/scanlog.txt
/merlin/arthurlog.txt

Setting up Git: (Consult Git's website for full instructions)
1) Navigate to the parent directory where you wish to install Merlin
2) Use Git to download the code and create a branch to track your changes:
Expand Down Expand Up @@ -77,6 +91,7 @@ Configuring Excalibur:
permission and insert the following commands:
cd /path/to/merlin/
python excalibur.py
Make sure to sudo your crontab

Configuring Apache and running Arthur: (Consult their respective websites for full instructions)
14) You should download the mod_wsgi .so file to your Apache's /modules/
Expand All @@ -98,6 +113,13 @@ Configuring Apache and running Arthur: (Consult their respective websites for fu
Order allow,deny
Allow from all
</Directory>

Alias /graphs/ F:/Code/Git/merlin/Arthur/graphs/
<Directory F:/Code/Git/merlin/Arthur/graphs/>
Order allow,deny
Allow from all
ErrorDocument 404 /draw
</Directory>
Make sure you edit all the paths!
17) Open the arthur.wsgi file and edit the two paths in that file.
Commit your changes!
Expand Down
4 changes: 2 additions & 2 deletions arthur.wsgi
Expand Up @@ -26,5 +26,5 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'Arthur.settings'

import Arthur

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
from django.core.handlers.wsgi import WSGIRequest, WSGIHandler
application = WSGIHandler()
13 changes: 12 additions & 1 deletion excalibur.py
Expand Up @@ -19,7 +19,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

import datetime, re, sys, time, traceback, urllib2
import datetime, re, sys, time, traceback, urllib2, shutil
from sqlalchemy.sql import text, bindparam
from Core.config import Config
from Core.paconf import PA
Expand Down Expand Up @@ -1012,3 +1012,14 @@ def load_planet_id_search():
t1=time.time()-t_start
excaliburlog("Total penis time: %.3f seconds" % (t1,))
session.close()

# Clean tick dependant graph cache
try:
t_start=time.time()
shutil.rmtree("Arthur/graphs/values/")
shutil.rmtree("Arthur/graphs/ranks/")
except OSError:
pass
finally:
t1=time.time()-t_start
excaliburlog("Clean tick dependant graph cache in %.3f seconds" % (t1,))

0 comments on commit 7e016ea

Please sign in to comment.