Fleet-up cleanup (#798)

Add caching and better error handling
Move fleetup templates into fleetup app
Move fleetup urls into fleetup app
Fix button overflow and url
Add manager unit tests
Fix python3 compatibility
This commit is contained in:
Basraah 2017-06-05 08:35:23 +10:00 committed by Adarnof
parent e76b0789f3
commit 5db340c64a
20 changed files with 741 additions and 298 deletions

View File

@ -57,6 +57,7 @@ INSTALLED_APPS = [
'optimer', 'optimer',
'corputils', 'corputils',
'fleetactivitytracking', 'fleetactivitytracking',
'fleetup',
'notifications', 'notifications',
'esi', 'esi',
'permissions_tool', 'permissions_tool',
@ -661,6 +662,10 @@ LOGGING = {
'handlers': ['log_file', 'console', 'notifications'], 'handlers': ['log_file', 'console', 'notifications'],
'level': 'ERROR', 'level': 'ERROR',
}, },
'fleetup': {
'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG',
},
'util': { 'util': {
'handlers': ['log_file', 'console', 'notifications'], 'handlers': ['log_file', 'console', 'notifications'],
'level': 'DEBUG', 'level': 'DEBUG',

View File

@ -14,6 +14,7 @@ TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = [ NOSE_ARGS = [
#'--with-coverage', #'--with-coverage',
#'--cover-package=', #'--cover-package=',
#'--exe', # If your tests need this to be found/run, check they py files are not chmodded +x
] ]
# Celery configuration # Celery configuration
@ -48,6 +49,7 @@ INSTALLED_APPS = [
'optimer', 'optimer',
'corputils', 'corputils',
'fleetactivitytracking', 'fleetactivitytracking',
'fleetup',
'notifications', 'notifications',
'esi', 'esi',
'permissions_tool', 'permissions_tool',

View File

@ -11,7 +11,7 @@ import groupmanagement.views
import optimer.views import optimer.views
import timerboard.views import timerboard.views
import fleetactivitytracking.views import fleetactivitytracking.views
import fleetup.views import fleetup.urls
import srp.views import srp.views
import notifications.views import notifications.views
import hrapplications.views import hrapplications.views
@ -75,12 +75,7 @@ urlpatterns = [
urlpatterns += i18n_patterns( urlpatterns += i18n_patterns(
# Fleetup # Fleetup
url(r'^fleetup/$', fleetup.views.fleetup_view, name='auth_fleetup_view'), url(r'^fleetup/', include(fleetup.urls.urlpatterns)),
url(r'^fleetup/fittings/$', fleetup.views.fleetup_fittings, name='auth_fleetup_fittings'),
url(r'^fleetup/fittings/(?P<fittingnumber>[0-9]+)/$', fleetup.views.fleetup_fitting, name='auth_fleetup_fitting'),
url(r'^fleetup/doctrines/$', fleetup.views.fleetup_doctrines, name='auth_fleetup_doctrines'),
url(r'^fleetup/characters/$', fleetup.views.fleetup_characters, name='auth_fleetup_characters'),
url(r'^fleetup/doctrines/(?P<doctrinenumber>[0-9]+)/$', fleetup.views.fleetup_doctrine, name='auth_fleetup_doctrine'),
# Authentication # Authentication
url(_(r'^login_user/'), authentication.views.login_user, name='auth_login_user'), url(_(r'^login_user/'), authentication.views.login_user, name='auth_login_user'),

View File

@ -1 +1,2 @@
from __future__ import unicode_literals from __future__ import unicode_literals
default_app_config = 'fleetup.apps.FleetupConfig'

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -1,50 +1,94 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.core.cache import cache
from datetime import datetime from datetime import datetime
import logging import logging
import requests import requests
import json import hashlib
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
appkey = settings.FLEETUP_APP_KEY
userid = settings.FLEETUP_USER_ID
apiid = settings.FLEETUP_API_ID
groupid = settings.FLEETUP_GROUP_ID
class FleetUpManager: class FleetUpManager:
APP_KEY = settings.FLEETUP_APP_KEY
USER_ID = settings.FLEETUP_USER_ID
API_ID = settings.FLEETUP_API_ID
GROUP_ID = settings.FLEETUP_GROUP_ID
BASE_URL = "http://api.fleet-up.com/Api.svc/{}/{}/{}".format(APP_KEY, USER_ID, API_ID)
def __init__(self): def __init__(self):
pass pass
@staticmethod @classmethod
def get_fleetup_members(): def _request_cache_key(cls, url):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( h = hashlib.sha1()
apiid) + "/GroupCharacters/" + str(groupid) + "" h.update(url.encode('utf-8'))
return 'FLEETUP_ENDPOINT_' + h.hexdigest()
@classmethod
def _cache_until_seconds(cls, cache_until_json):
# Format comes in like "/Date(1493896236163)/"
try: try:
jsondata = requests.get(url).content epoch_ms = int(cache_until_json[6:-2])
fmembers = json.loads(jsondata.decode()) cache_delta = datetime.fromtimestamp(epoch_ms/1000) - datetime.now()
cache_delta_seconds = cache_delta.total_seconds()
if cache_delta_seconds < 0:
return 0
elif cache_delta_seconds > 3600:
return 3600
else:
return cache_delta_seconds
except TypeError:
logger.debug("Couldn't convert CachedUntil time, defaulting to 600 seconds")
return 600
@classmethod
def get_endpoint(cls, url):
try:
cache_key = cls._request_cache_key(url)
cached = cache.get(cache_key)
if cached:
return cached
r = requests.get(url)
r.raise_for_status()
json = r.json()
if json['Success']:
cache.set(cache_key, json, cls._cache_until_seconds(json['CachedUntilUTC']))
return json
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except requests.HTTPError:
logger.exception("Error accessing Fleetup API")
return None
@classmethod
def get_fleetup_members(cls):
url = "{}/GroupCharacters/{}".format(cls.BASE_URL, cls.GROUP_ID)
try:
fmembers = cls.get_endpoint(url)
if not fmembers:
return None
return {row["UserId"]: {"user_id": row["UserId"], return {row["UserId"]: {"user_id": row["UserId"],
"char_name": row["EveCharName"], "char_name": row["EveCharName"],
"char_id": row["EveCharId"], "char_id": row["EveCharId"],
"corporation": row["Corporation"]} for row in fmembers["Data"]} "corporation": row["Corporation"]} for row in fmembers["Data"]}
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError, TypeError): except (ValueError, UnicodeDecodeError, TypeError):
logger.debug("No fleetup members retrieved.") logger.debug("No fleetup members retrieved.")
return {} return {}
@staticmethod @classmethod
def get_fleetup_operations(): def get_fleetup_operations(cls):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/Operations/{}".format(cls.BASE_URL, cls.GROUP_ID)
apiid) + "/Operations/" + str(groupid) + "" foperations = cls.get_endpoint(url)
try: if foperations is None:
jsondata = requests.get(url).content return None
foperations = json.loads(jsondata.decode())
return {row["StartString"]: {"subject": row["Subject"], return {row["StartString"]: {"subject": row["Subject"],
"start": (datetime.strptime(row["StartString"], "%Y-%m-%d %H:%M:%S")), "start": datetime.strptime(row["StartString"], "%Y-%m-%d %H:%M:%S"),
"end": (datetime.strptime(row["EndString"], "%Y-%m-%d %H:%M:%S")), "end": datetime.strptime(row["EndString"], "%Y-%m-%d %H:%M:%S"),
"operation_id": row["OperationId"], "operation_id": row["OperationId"],
"location": row["Location"], "location": row["Location"],
"location_info": row["LocationInfo"], "location_info": row["LocationInfo"],
@ -52,19 +96,13 @@ class FleetUpManager:
"url": row["Url"], "url": row["Url"],
"doctrine": row["Doctrines"], "doctrine": row["Doctrines"],
"organizer": row["Organizer"]} for row in foperations["Data"]} "organizer": row["Organizer"]} for row in foperations["Data"]}
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError):
logger.debug("No fleetup operations retrieved.")
return {}
@staticmethod @classmethod
def get_fleetup_timers(): def get_fleetup_timers(cls):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/Timers/{}".format(cls.BASE_URL, cls.GROUP_ID)
apiid) + "/Timers/" + str(groupid) + "" ftimers = cls.get_endpoint(url)
try: if not ftimers:
jsondata = requests.get(url).content return None
ftimers = json.loads(jsondata.decode())
return {row["ExpiresString"]: {"solarsystem": row["SolarSystem"], return {row["ExpiresString"]: {"solarsystem": row["SolarSystem"],
"planet": row["Planet"], "planet": row["Planet"],
"moon": row["Moon"], "moon": row["Moon"],
@ -73,47 +111,30 @@ class FleetUpManager:
"timer_type": row["TimerType"], "timer_type": row["TimerType"],
"expires": (datetime.strptime(row["ExpiresString"], "%Y-%m-%d %H:%M:%S")), "expires": (datetime.strptime(row["ExpiresString"], "%Y-%m-%d %H:%M:%S")),
"notes": row["Notes"]} for row in ftimers["Data"]} "notes": row["Notes"]} for row in ftimers["Data"]}
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError, TypeError):
logger.debug("No fleetup timers retrieved.")
return {} return {}
@staticmethod @classmethod
def get_fleetup_doctrines(): def get_fleetup_doctrines(cls):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/Doctrines/{}".format(cls.BASE_URL, cls.GROUP_ID)
apiid) + "/Doctrines/" + str(groupid) + "" fdoctrines = cls.get_endpoint(url)
try: if not fdoctrines:
jsondata = requests.get(url).content return None
fdoctrines = json.loads(jsondata.decode())
return {"fleetup_doctrines": fdoctrines["Data"]} return {"fleetup_doctrines": fdoctrines["Data"]}
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError):
logger.debug("No fleetup doctrines retrieved.")
return {"fleetup_doctrines": []}
@staticmethod @classmethod
def get_fleetup_doctrine(doctrinenumber): def get_fleetup_doctrine(cls, doctrinenumber):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/DoctrineFittings/{}".format(cls.BASE_URL, doctrinenumber)
apiid) + "/DoctrineFittings/%s" % doctrinenumber fdoctrine = cls.get_endpoint(url)
try: if not fdoctrine:
jsondata = requests.get(url).content return None
fdoctrine = json.loads(jsondata.decode())
return {"fitting_doctrine": fdoctrine} return {"fitting_doctrine": fdoctrine}
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError):
logger.warn("Fleetup doctrine number %s not found" % doctrinenumber)
return {"fitting_doctrine": {}}
@staticmethod @classmethod
def get_fleetup_fittings(): def get_fleetup_fittings(cls):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/Fittings/{}".format(cls.BASE_URL, cls.GROUP_ID)
apiid) + "/Fittings/" + str(groupid) + "" ffittings = cls.get_endpoint(url)
try: if not ffittings:
jsondata = requests.get(url).content return None
ffittings = json.loads(jsondata.decode())
return {row["FittingId"]: {"fitting_id": row["FittingId"], return {row["FittingId"]: {"fitting_id": row["FittingId"],
"name": row["Name"], "name": row["Name"],
"icon_id": row["EveTypeId"], "icon_id": row["EveTypeId"],
@ -125,54 +146,39 @@ class FleetUpManager:
"last_update": ( "last_update": (
datetime.strptime(row["LastUpdatedString"], "%Y-%m-%d %H:%M:%S"))} for row in datetime.strptime(row["LastUpdatedString"], "%Y-%m-%d %H:%M:%S"))} for row in
ffittings["Data"]} ffittings["Data"]}
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError, TypeError):
logger.debug("No fleetup fittings retrieved.")
return {}
@staticmethod @classmethod
def get_fleetup_fitting(fittingnumber): def get_fleetup_fitting(cls, fittingnumber):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/Fitting/{}".format(cls.BASE_URL, fittingnumber)
apiid) + "/Fitting/%s" % fittingnumber
try: try:
jsondata = requests.get(url).content ffitting = cls.get_endpoint(url)
ffitting = json.loads(jsondata.decode()) if not ffitting:
return None
return {"fitting_data": ffitting["Data"]} return {"fitting_data": ffitting["Data"]}
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError):
logger.warn("Fleetup fitting number %s not found" % fittingnumber)
except KeyError: except KeyError:
logger.warn("Failed to retrieve fleetup fitting number %s" % fittingnumber) logger.warn("Failed to retrieve fleetup fitting number %s" % fittingnumber)
return {"fitting_data": {}} return {"fitting_data": {}}
@staticmethod @classmethod
def get_fleetup_doctrineid(fittingnumber): def get_fleetup_doctrineid(cls, fittingnumber):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/Fitting/{}".format(cls.BASE_URL, fittingnumber)
apiid) + "/Fitting/%s" % fittingnumber
try: try:
jsondata = requests.get(url).content fdoctrineid = cls.get_endpoint(url)
fdoctrineid = json.loads(jsondata.decode()) if not fdoctrineid:
return None
return fdoctrineid['Data']['Doctrines'][0]['DoctrineId'] return fdoctrineid['Data']['Doctrines'][0]['DoctrineId']
except requests.exceptions.ConnectionError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError):
logger.warn("Fleetup doctrine number not found for fitting number %s" % fittingnumber)
except (KeyError, IndexError): except (KeyError, IndexError):
logger.debug("Fleetup fitting number %s not in a doctrine." % fittingnumber) logger.debug("Fleetup fitting number %s not in a doctrine." % fittingnumber)
return None return {}
@staticmethod @classmethod
def get_fleetup_fitting_eft(fittingnumber): def get_fleetup_fitting_eft(cls, fittingnumber):
url = "http://api.fleet-up.com/Api.svc/" + str(appkey) + "/" + str(userid) + "/" + str( url = "{}/Fitting/{}/eft".format(cls.BASE_URL, fittingnumber)
apiid) + "/Fitting/%s/eft" % fittingnumber
try: try:
jsondata = requests.get(url).content ffittingeft = cls.get_endpoint(url)
ffittingeft = json.loads(jsondata.decode()) if not ffittingeft:
return None
return {"fitting_eft": ffittingeft["Data"]["FittingData"]} return {"fitting_eft": ffittingeft["Data"]["FittingData"]}
except requests.exceptions.ConnectionError: except KeyError:
logger.warn("Can't connect to Fleet-Up API, is it offline?!")
except (ValueError, UnicodeDecodeError):
logger.warn("Fleetup fitting eft not found for fitting number %s" % fittingnumber) logger.warn("Fleetup fitting eft not found for fitting number %s" % fittingnumber)
return {"fitting_eft": {}} return {"fitting_eft": {}}

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -9,30 +9,7 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
{% if perms.auth.corp_stats %} {% if perms.auth.corp_stats %}
<nav class="navbar navbar-default"> {% include "fleetup/menu.html" %}
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Fleet-Up</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{% url 'auth_fleetup_view' %}">{% trans "Ops and Timers" %}</a></li>
<li><a href="{% url 'auth_fleetup_doctrines' %}">{% trans "Doctrines" %}</a></li>
<li><a href="{% url 'auth_fleetup_fittings' %}">{% trans "Fittings" %}</a></li>
<li class="active"><a href="{% url 'auth_fleetup_characters' %}">{% trans "Characters" %} <span class="sr-only">(current)</span></a></li>
<li></li>
</ul>
</div>
</div>
</nav>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">{% trans "Characters registered on Fleet-Up.com" %}</h3> <h3 class="panel-title">{% trans "Characters registered on Fleet-Up.com" %}</h3>

View File

@ -8,30 +8,7 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<nav class="navbar navbar-default"> {% include "fleetup/menu.html" %}
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Fleet-Up</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{% url 'auth_fleetup_view' %}">{% trans "Ops and Timers" %}</a></li>
<li class="active"><a href="{% url 'auth_fleetup_doctrines' %}">{% trans "Doctrines" %} <span class="sr-only">(current)</span></a></li>
<li><a href="{% url 'auth_fleetup_fittings' %}">{% trans "Fittings" %}</a></li>
{% if perms.auth.corp_stats %}
<li><a href="{% url 'auth_fleetup_characters' %}">{% trans "Characters" %}</a></li>
{% endif %}
<li></li>
</ul>
</div>
</div>
</nav>
<div class="panel"> <div class="panel">
{% for a, j in doctrine.items %} {% for a, j in doctrine.items %}
{% regroup j.Data|dictsort:"Role" by Role as role_list %} {% regroup j.Data|dictsort:"Role" by Role as role_list %}

View File

@ -8,30 +8,7 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<nav class="navbar navbar-default"> {% include "fleetup/menu.html" %}
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Fleet-Up</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{% url 'auth_fleetup_view' %}">{% trans "Ops and Timers" %}</a></li>
<li class="active"><a href="{% url 'auth_fleetup_doctrines' %}">{% trans "Doctrines" %} <span class="sr-only">(current)</span></a></li>
<li><a href="{% url 'auth_fleetup_fittings' %}">{% trans "Fittings" %}</a></li>
{% if perms.auth.corp_stats %}
<li><a href="{% url 'auth_fleetup_characters' %}">{% trans "Characters" %}</a></li>
{% endif %}
<li></li>
</ul>
</div>
</div>
</nav>
<div class="panel"> <div class="panel">
{% if doctrines_list %} {% if doctrines_list %}
{% for a, j in doctrines_list.items %} {% for a, j in doctrines_list.items %}

View File

@ -8,30 +8,7 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<nav class="navbar navbar-default"> {% include "fleetup/menu.html" %}
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">{% trans "Fleet-Up" %}</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{% url 'auth_fleetup_view' %}">{% trans "Ops and Timers" %}</a></li>
<li><a href="{% url 'auth_fleetup_doctrines' %}">{% trans "Doctrines" %}</a></li>
<li class="active"><a href="{% url 'auth_fleetup_fittings' %}">{% trans "Fittings" %} <span class="sr-only">(current)</span></a></li>
{% if perms.auth.corp_stats %}
<li><a href="{% url 'auth_fleetup_characters' %}">{% trans "Characters" %}</a></li>
{% endif %}
<li></li>
</ul>
</div>
</div>
</nav>
<div class="tab-content"> <div class="tab-content">
<div id="fit" class="tab-pane fade in active"> <div id="fit" class="tab-pane fade in active">
<div class="col-lg-3"> <div class="col-lg-3">
@ -56,8 +33,8 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-8 col-lg-offset-7"> <div class="pull-right">
<a class="btn btn-primary" href="/fleetup/doctrines/{{ doctrin.DoctrineId }}/">{% trans "See doctrine" %}</a> <a class="btn btn-primary" href="{% url 'auth_fleetup_doctrine' doctrin.DoctrineId %}">{% trans "See doctrine" %}</a>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -140,7 +117,7 @@
<div class="panel-body"> <div class="panel-body">
{% for data in fitting_eft.items %} {% for data in fitting_eft.items %}
{% autoescape off %} {% autoescape off %}
<pre>{{ fitting_eft.fitting_eft }}</pre> <textarea class="form-control" rows="25" spellcheck="false" onclick="this.focus();this.select()" readonly>{{ fitting_eft.fitting_eft }}</textarea>
{% endautoescape %} {% endautoescape %}
{% endfor %} {% endfor %}
</div> </div>

View File

@ -8,30 +8,7 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<nav class="navbar navbar-default"> {% include "fleetup/menu.html" %}
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">{% trans "Fleet-Up" %}</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{% url 'auth_fleetup_view' %}">{% trans "Ops and Timers" %}</a></li>
<li><a href="{% url 'auth_fleetup_doctrines' %}">{% trans "Doctrines" %}</a></li>
<li class="active"><a href="{% url 'auth_fleetup_fittings' %}">{% trans "Fittings" %} <span class="sr-only">(current)</span></a></li>
{% if perms.auth.corp_stats %}
<li><a href="{% url 'auth_fleetup_characters' %}">{% trans "Characters" %}</a></li>
{% endif %}
<li></li>
</ul>
</div>
</div>
</nav>
<div class="panel"> <div class="panel">
{% if fitting_list %} {% if fitting_list %}
<table class="table table-condensed table-hover table-striped"> <table class="table table-condensed table-hover table-striped">

View File

@ -8,30 +8,7 @@
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<nav class="navbar navbar-default"> {% include "fleetup/menu.html" %}
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">{% trans "Fleet-Up" %}</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">{% trans "Ops and Timers" %} <span class="sr-only">(current)</span></a></li>
<li><a href="{% url 'auth_fleetup_doctrines' %}">{% trans "Doctrines" %}</a></li>
<li><a href="{% url 'auth_fleetup_fittings' %}">{% trans "Fittings" %}</a></li>
{% if perms.auth.human_resources %}
<li><a href="{% url 'auth_fleetup_characters' %}">{% trans "Characters" %}</a></li>
{% endif %}
<li></li>
</ul>
</div>
</div>
</nav>
<div class="panel"> <div class="panel">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#operations">{% trans "Operations" %}</a></li> <li class="active"><a data-toggle="tab" href="#operations">{% trans "Operations" %}</a></li>

View File

@ -0,0 +1,26 @@
{% load i18n %}
{% load navactive %}
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Fleet-Up</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="{% navactive request 'auth_fleetup_view' %}"><a href="{% url 'auth_fleetup_view' %}">{% trans "Ops and Timers" %}</a></li>
<li class="{% navactive request 'auth_fleetup_doctrines auth_fleetup_doctrine' %}"><a href="{% url 'auth_fleetup_doctrines' %}">{% trans "Doctrines" %}</a></li>
<li class="{% navactive request 'auth_fleetup_fittings auth_fleetup_fitting' %}"><a href="{% url 'auth_fleetup_fittings' %}">{% trans "Fittings" %}</a></li>
{% if perms.auth.corp_stats %}
<li class="{% navactive request 'auth_fleetup_characters' %}"><a href="{% url 'auth_fleetup_characters' %}">{% trans "Characters" %}</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>

0
fleetup/tests.py → fleetup/tests/__init__.py Executable file → Normal file
View File

View File

@ -0,0 +1,509 @@
from __future__ import unicode_literals
try:
# Py3
from unittest import mock
except ImportError:
# Py2
import mock
import requests_mock
import json
import datetime
from django.test import TestCase
from fleetup.managers import FleetUpManager
class FleetupManagerTestCase(TestCase):
def setUp(self):
pass
def test__request_cache_key(self):
cache_key = FleetUpManager._request_cache_key('testurl')
self.assertEqual('FLEETUP_ENDPOINT_a39562b6ef5b858220be13d2adb61d3f10cf8d61',
cache_key)
@mock.patch('fleetup.managers.cache')
@requests_mock.Mocker()
def test_get_endpoint(self, cache, m):
url = "http://example.com/test/endpoint/"
json_data = {'data': "123456", 'CachedUntilUTC': '/Date(1493896236163)/', 'Success': True}
m.register_uri('GET', url,
text=json.dumps(json_data))
cache.get.return_value = None # No cached value
# Act
result = FleetUpManager.get_endpoint(url)
# Assert
self.assertTrue(cache.get.called)
self.assertTrue(cache.set.called)
args, kwargs = cache.set.call_args
self.assertDictEqual(json_data, args[1])
self.assertDictEqual(json_data, result)
@mock.patch('fleetup.managers.cache')
@requests_mock.Mocker()
def test_get_endpoint_error(self, cache, m):
url = "http://example.com/test/endpoint/"
json_data = {'data': [], 'Success': False}
m.register_uri('GET', url,
text=json.dumps(json_data),
status_code=400)
cache.get.return_value = None # No cached value
# Act
result = FleetUpManager.get_endpoint(url)
# Assert
self.assertTrue(cache.get.called)
self.assertFalse(cache.set.called)
self.assertIsNone(result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_members(self, get_endpoint):
get_endpoint.return_value = {"Data": [
{
'UserId': 1234,
'EveCharName': 'test_name',
'EveCharId': 5678,
'Corporation': 'test_corporation',
}
]}
# Act
result = FleetUpManager.get_fleetup_members()
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0],
FleetUpManager.BASE_URL + '/GroupCharacters/' +
FleetUpManager.GROUP_ID)
expected_result = {
1234: {
'user_id': 1234,
'char_name': 'test_name',
'char_id': 5678,
'corporation': 'test_corporation',
}
}
self.assertDictEqual(expected_result, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_members()
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': []}
# Act
result = FleetUpManager.get_fleetup_members()
# Assert
self.assertDictEqual({}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_operations(self, get_endpoint):
get_endpoint.return_value = {"Data": [
{
'Subject': 'test_operation',
'StartString': '2017-05-06 11:11:11',
'EndString': '2017-05-06 12:12:12',
'OperationId': 1234,
'Location': 'Jita',
'LocationInfo': '4-4',
'Details': 'This is a test operation',
'Url': 'http://example.com/1234',
'Doctrines': 'Foxcats',
'Organizer': 'Example FC'
}
]}
# Act
result = FleetUpManager.get_fleetup_operations()
self.maxDiff = None
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0],
FleetUpManager.BASE_URL + '/Operations/' +
FleetUpManager.GROUP_ID)
expected_result = {
'2017-05-06 11:11:11': {
'subject': 'test_operation',
'start': datetime.datetime(2017, 5, 6, 11, 11, 11),
'end': datetime.datetime(2017, 5, 6, 12, 12, 12),
'operation_id': 1234,
'location': 'Jita',
'location_info': '4-4',
'details': 'This is a test operation',
'url': 'http://example.com/1234',
'doctrine': 'Foxcats',
'organizer': 'Example FC'
}
}
self.assertDictEqual(expected_result, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_operations()
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': []}
# Act
result = FleetUpManager.get_fleetup_operations()
# Assert
self.assertDictEqual({}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_timers(self, get_endpoint):
get_endpoint.return_value = {"Data": [
{
'ExpiresString': '2017-05-06 11:11:11',
'SolarSystem': 'Jita',
'Planet': '4',
'Moon': '4',
'Owner': 'Caldari Navy',
'Type': 'Caldari Station',
'TimerType': 'Armor',
'Notes': 'Burn Jita?'
}
]}
# Act
result = FleetUpManager.get_fleetup_timers()
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0],
FleetUpManager.BASE_URL + '/Timers/' +
FleetUpManager.GROUP_ID)
expected_result = {
'2017-05-06 11:11:11': {
'expires': datetime.datetime(2017, 5, 6, 11, 11, 11),
'solarsystem': 'Jita',
'planet': '4',
'moon': '4',
'owner': 'Caldari Navy',
'type': 'Caldari Station',
'timer_type': 'Armor',
'notes': 'Burn Jita?'
}
}
self.assertDictEqual(expected_result, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_timers()
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': []}
# Act
result = FleetUpManager.get_fleetup_timers()
# Assert
self.assertDictEqual({}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_doctrines(self, get_endpoint):
get_endpoint.return_value = {"Data": [
{
'TestData': True
}
]}
# Act
result = FleetUpManager.get_fleetup_doctrines()
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0],
FleetUpManager.BASE_URL + '/Doctrines/' +
FleetUpManager.GROUP_ID)
expected_result = {
'fleetup_doctrines': [{
'TestData': True
}]
}
self.assertDictEqual(expected_result, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_doctrines()
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': []}
# Act
result = FleetUpManager.get_fleetup_doctrines()
# Assert
self.assertDictEqual({"fleetup_doctrines": []}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_doctrine(self, get_endpoint):
get_endpoint.return_value = {"Data": [
{
'TestData': True
}
]}
# Act
result = FleetUpManager.get_fleetup_doctrine(1234)
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0],
FleetUpManager.BASE_URL + '/DoctrineFittings/1234')
expected_result = {
'fitting_doctrine': {'Data': [{
'TestData': True
}]}
}
self.assertDictEqual(expected_result, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_doctrine(1234)
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': []}
# Act
result = FleetUpManager.get_fleetup_doctrine(1234)
# Assert
self.assertDictEqual({"fitting_doctrine": {'Data': []}}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_fittings(self, get_endpoint):
get_endpoint.return_value = {"Data": [
{
'FittingId': 1234,
'Name': 'Foxcat',
'EveTypeId': 17726,
'HullType': 'Battleship',
'ShipType': 'Apocalypse Navy Issue',
'EstPrice': 500000000,
'Faction': 'Amarr',
'Categories': ["Armor", "Laser"],
'LastUpdatedString': '2017-05-06 11:11:11',
}
]}
# Act
result = FleetUpManager.get_fleetup_fittings()
# Asset
self.assertTrue(get_endpoint.called)
expected_result = {
1234: {
'fitting_id': 1234,
'name': 'Foxcat',
'icon_id': 17726,
'hull': 'Battleship',
'shiptype': 'Apocalypse Navy Issue',
'estimated': 500000000,
'faction': 'Amarr',
'categories': ["Armor", "Laser"],
'last_update': datetime.datetime(2017, 5, 6, 11, 11, 11)
}
}
self.assertDictEqual(expected_result, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_fittings()
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': []}
# Act
result = FleetUpManager.get_fleetup_fittings()
# Assert
self.assertDictEqual({}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_fitting(self, get_endpoint):
get_endpoint.return_value = {"Data":
{
'FittingData': [{}]
}
}
# Act
result = FleetUpManager.get_fleetup_fitting(1234)
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0], FleetUpManager.BASE_URL + '/Fitting/1234')
expected_result = {
'fitting_data': {
'FittingData': [{}]
}
}
self.assertDictEqual(expected_result, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_fitting(1234)
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': {}}
# Act
result = FleetUpManager.get_fleetup_fitting(1234)
# Assert
self.assertDictEqual({"fitting_data": {}}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_doctrineid(self, get_endpoint):
get_endpoint.return_value = {
"Data": {
'Doctrines': [{'DoctrineId': 4567}]
}
}
# Act
result = FleetUpManager.get_fleetup_doctrineid(1234)
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0], FleetUpManager.BASE_URL + '/Fitting/1234')
self.assertEqual(4567, result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_doctrineid(1234)
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': {}}
# Act
result = FleetUpManager.get_fleetup_doctrineid(1234)
# Assert
self.assertDictEqual({}, result)
@mock.patch('fleetup.managers.FleetUpManager.get_endpoint')
def test_get_fleetup_fitting_eft(self, get_endpoint):
get_endpoint.return_value = {
"Data": {
'FittingData': '[Apocalypse Navy Issue, Foxcat]'
}
}
# Act
result = FleetUpManager.get_fleetup_fitting_eft(1234)
# Asset
self.assertTrue(get_endpoint.called)
args, kwargs = get_endpoint.call_args
self.assertEqual(args[0], FleetUpManager.BASE_URL + '/Fitting/1234/eft')
self.assertDictEqual({"fitting_eft": '[Apocalypse Navy Issue, Foxcat]'},
result)
# Test None response
# Arrange
get_endpoint.return_value = None
# Act
result = FleetUpManager.get_fleetup_fitting_eft(1234)
# Assert
self.assertIsNone(result)
# Test Empty response
# Arrange
get_endpoint.return_value = {'Data': {}}
# Act
result = FleetUpManager.get_fleetup_fitting_eft(1234)
# Assert
self.assertDictEqual({"fitting_eft": {}}, result)

13
fleetup/urls.py Normal file
View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.fleetup_view, name='auth_fleetup_view'),
url(r'^fittings/$', views.fleetup_fittings, name='auth_fleetup_fittings'),
url(r'^fittings/(?P<fittingnumber>[0-9]+)/$', views.fleetup_fitting, name='auth_fleetup_fitting'),
url(r'^doctrines/$', views.fleetup_doctrines, name='auth_fleetup_doctrines'),
url(r'^characters/$', views.fleetup_characters, name='auth_fleetup_characters'),
url(r'^doctrines/(?P<doctrinenumber>[0-9]+)/$', views.fleetup_doctrine, name='auth_fleetup_doctrine'),
]

38
fleetup/views.py Executable file → Normal file
View File

@ -4,6 +4,8 @@ from django.shortcuts import render
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.template.defaulttags import register from django.template.defaulttags import register
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from fleetup.managers import FleetUpManager from fleetup.managers import FleetUpManager
from authentication.decorators import members_and_blues from authentication.decorators import members_and_blues
@ -23,14 +25,20 @@ def fleetup_view(request):
logger.debug("fleetup_view called by user %s" % request.user) logger.debug("fleetup_view called by user %s" % request.user)
operations_list = FleetUpManager.get_fleetup_operations() operations_list = FleetUpManager.get_fleetup_operations()
if operations_list is None:
messages.add_message(request, messages.ERROR, _("Failed to get operations list, contact your administrator"))
operations_list = {}
timers_list = FleetUpManager.get_fleetup_timers() timers_list = FleetUpManager.get_fleetup_timers()
if timers_list is None:
messages.add_message(request, messages.ERROR, _("Failed to get timers list, contact your administrator"))
timers_list = {}
now = datetime.datetime.now().strftime('%H:%M:%S') now = datetime.datetime.now().strftime('%H:%M:%S')
context = {"timers_list": sorted(timers_list.items()), context = {"timers_list": sorted(timers_list.items()),
"operations_list": sorted(operations_list.items()), "operations_list": sorted(operations_list.items()),
"now": now} "now": now}
return render(request, 'registered/fleetup.html', context=context) return render(request, 'fleetup/index.html', context=context)
@login_required @login_required
@ -39,10 +47,13 @@ def fleetup_characters(request):
logger.debug("fleetup_characters called by user %s" % request.user) logger.debug("fleetup_characters called by user %s" % request.user)
member_list = FleetUpManager.get_fleetup_members() member_list = FleetUpManager.get_fleetup_members()
if member_list is None:
messages.add_message(request, messages.ERROR, _("Failed to get member list, contact your administrator"))
member_list = {}
context = {"member_list": sorted(member_list.items())} context = {"member_list": sorted(member_list.items())}
return render(request, 'registered/fleetupcharacters.html', context=context) return render(request, 'fleetup/characters.html', context=context)
@login_required @login_required
@ -50,8 +61,13 @@ def fleetup_characters(request):
def fleetup_fittings(request): def fleetup_fittings(request):
logger.debug("fleetup_fittings called by user %s" % request.user) logger.debug("fleetup_fittings called by user %s" % request.user)
fitting_list = FleetUpManager.get_fleetup_fittings() fitting_list = FleetUpManager.get_fleetup_fittings()
if fitting_list is None:
messages.add_message(request, messages.ERROR, _("Failed to get fitting list, contact your administrator"))
fitting_list = {}
context = {"fitting_list": sorted(fitting_list.items())} context = {"fitting_list": sorted(fitting_list.items())}
return render(request, 'registered/fleetupfittingsview.html', context=context) return render(request, 'fleetup/fittingsview.html', context=context)
@login_required @login_required
@ -62,10 +78,15 @@ def fleetup_fitting(request, fittingnumber):
fitting_data = FleetUpManager.get_fleetup_fitting(fittingnumber) fitting_data = FleetUpManager.get_fleetup_fitting(fittingnumber)
doctrinenumber = FleetUpManager.get_fleetup_doctrineid(fittingnumber) doctrinenumber = FleetUpManager.get_fleetup_doctrineid(fittingnumber)
doctrines_list = FleetUpManager.get_fleetup_doctrine(doctrinenumber) doctrines_list = FleetUpManager.get_fleetup_doctrine(doctrinenumber)
if fitting_eft is None or fitting_data is None or doctrinenumber is None:
messages.add_message(request, messages.ERROR, _("There was an error getting some of the data for this fitting. "
"Contact your administrator"))
context = {"fitting_eft": fitting_eft, context = {"fitting_eft": fitting_eft,
"fitting_data": fitting_data, "fitting_data": fitting_data,
"doctrines_list": doctrines_list} "doctrines_list": doctrines_list}
return render(request, 'registered/fleetupfitting.html', context=context) return render(request, 'fleetup/fitting.html', context=context)
@login_required @login_required
@ -73,8 +94,11 @@ def fleetup_fitting(request, fittingnumber):
def fleetup_doctrines(request): def fleetup_doctrines(request):
logger.debug("fleetup_doctrines called by user %s" % request.user) logger.debug("fleetup_doctrines called by user %s" % request.user)
doctrines_list = FleetUpManager.get_fleetup_doctrines() doctrines_list = FleetUpManager.get_fleetup_doctrines()
if doctrines_list is None:
messages.add_message(request, messages.ERROR, _("Failed to get doctrines list, contact your administrator"))
context = {"doctrines_list": doctrines_list} context = {"doctrines_list": doctrines_list}
return render(request, 'registered/fleetupdoctrinesview.html', context=context) return render(request, 'fleetup/doctrinesview.html', context=context)
@login_required @login_required
@ -82,5 +106,7 @@ def fleetup_doctrines(request):
def fleetup_doctrine(request, doctrinenumber): def fleetup_doctrine(request, doctrinenumber):
logger.debug("fleetup_doctrine called by user %s" % request.user) logger.debug("fleetup_doctrine called by user %s" % request.user)
doctrine = FleetUpManager.get_fleetup_doctrine(doctrinenumber) doctrine = FleetUpManager.get_fleetup_doctrine(doctrinenumber)
if doctrine is None:
messages.add_message(request, messages.ERROR, _("Failed to get doctine, contact your administrator"))
context = {"doctrine": doctrine} context = {"doctrine": doctrine}
return render(request, 'registered/fleetupdoctrine.html', context=context) return render(request, 'fleetup/doctrine.html', context=context)

View File

@ -7,3 +7,4 @@ nose>=1.3.7
django-nose>=1.4.4 django-nose>=1.4.4
coverage>=4.3.1 coverage>=4.3.1
coveralls>=1.1 coveralls>=1.1
requests-mock>=1.2.0