Merge branch 'esi' of https://github.com/r4stl1n/allianceauth into multitenant

This commit is contained in:
Adarnof 2016-12-20 06:54:40 +00:00
commit 3cdbff6b36
39 changed files with 1357 additions and 1295 deletions

View File

@ -61,8 +61,9 @@ INSTALLED_APPS = [
'corputils',
'fleetactivitytracking',
'notifications',
'eve_sso',
'esi',
'geelweb.django.navhelper',
'bootstrap_pagination',
]
MIDDLEWARE = [
@ -229,9 +230,9 @@ SITE_NAME = os.environ.get('AA_SITE_NAME', 'Alliance Auth')
# Callback URL should be http://mydomain.com/sso/callback
# Leave callback blank to hide SSO button on login page
###################
EVE_SSO_CLIENT_ID = os.environ.get('AA_EVE_SSO_CLIENT_ID', '')
EVE_SSO_CLIENT_SECRET = os.environ.get('AA_EVE_SSO_CLIENT_SECRET', '')
EVE_SSO_CALLBACK_URL = os.environ.get('AA_EVE_SSO_CALLBACK_URL', '')
ESI_SSO_CLIENT_ID = os.environ.get('AA_EVE_SSO_CLIENT_ID', '')
ESI_SSO_CLIENT_SECRET = os.environ.get('AA_EVE_SSO_CLIENT_SECRET', '')
ESI_SSO_CALLBACK_URL = os.environ.get('AA_EVE_SSO_CALLBACK_URL', '')
#########################
# Default Group Settings
@ -346,6 +347,19 @@ REJECT_OLD_APIS = 'True' == os.environ.get('AA_REJECT_OLD_APIS', 'False')
REJECT_OLD_APIS_MARGIN = os.environ.get('AA_REJECT_OLD_APIS_MARGIN', 50)
API_SSO_VALIDATION = 'True' == os.environ.get('AA_API_SSO_VALIDATION', 'False')
#######################
# EVE Provider Settings
#######################
# EVEONLINE_CHARACTER_PROVIDER - Name of default data source for getting eve character data
# EVEONLINE_CORP_PROVIDER - Name of default data source for getting eve corporation data
# EVEONLINE_ALLIANCE_PROVIDER - Name of default data source for getting eve alliance data
#
# Available soruces are 'esi' and 'xml'
#######################
EVEONLINE_CHARACTER_PROVIDER = os.environ.get('AA_EVEONLINE_CHARACTER_PROVIDER', 'esi')
EVEONLINE_CORP_PROVIDER = os.environ.get('AA_EVEONLINE_CORP_PROVIDER', 'esi')
EVEONLINE_ALLIANCE_PROVIDER = os.environ.get('AA_EVEONLINE_ALLIANCE_PROVIDER', 'esi')
#####################
# Alliance Market
#####################

View File

@ -10,13 +10,13 @@ import services.views
import groupmanagement.views
import optimer.views
import timerboard.views
import corputils.views
import fleetactivitytracking.views
import fleetup.views
import srp.views
import notifications.views
import hrapplications.views
import eve_sso.urls
import corputils.urls
import esi.urls
# Functional/Untranslated URL's
urlpatterns = [
@ -27,8 +27,11 @@ urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
# SSO
url (r'^sso/', include(eve_sso.urls, namespace='eve_sso')),
url (r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'),
url(r'^sso/', include(esi.urls, namespace='esi')),
url(r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'),
# Corputils
url(r'^corpstats/', include(corputils.urls, namespace='corputils')),
# Index
url(_(r'^$'), authentication.views.index_view, name='auth_index'),
@ -144,14 +147,6 @@ urlpatterns = [
# User viewed/translated URLS
urlpatterns += i18n_patterns(
# corputils
url(r'^corputils/$', corputils.views.corp_member_view, name='auth_corputils'),
url(r'^corputils/(?P<corpid>[0-9]+)/$', corputils.views.corp_member_view, name='auth_corputils_corp_view'),
url(r'^corputils/(?P<corpid>[0-9]+)/(?P<year>[0-9]+)/(?P<month>[0-9]+)/$', corputils.views.corp_member_view,
name='auth_corputils_month'),
url(r'^corputils/search/$', corputils.views.corputils_search, name="auth_corputils_search"),
url(r'^corputils/search/(?P<corpid>[0-9]+)/$', corputils.views.corputils_search, name='auth_corputils_search_corp'),
# Fleetup
url(r'^fleetup/$', fleetup.views.fleetup_view, name='auth_fleetup_view'),
url(r'^fleetup/fittings/$', fleetup.views.fleetup_fittings, name='auth_fleetup_fittings'),
@ -245,12 +240,6 @@ urlpatterns += i18n_patterns(
# Teamspeak Urls
url(r'verify_teamspeak3/$', services.views.verify_teamspeak3, name='auth_verify_teamspeak3'),
# corputils
url(_(r'^corputils/$'), corputils.views.corp_member_view, name='auth_corputils'),
url(_(r'^corputils/(?P<corpid>[0-9]+)/$'), corputils.views.corp_member_view, name='auth_corputils_corp_view'),
url(_(r'^corputils/search/$'), corputils.views.corputils_search, name="auth_corputils_search"),
url(_(r'^corputils/search/(?P<corpid>[0-9]+)/$'), corputils.views.corputils_search, name='auth_corputils_search_corp'),
# Timer URLS
url(_(r'^timers/$'), timerboard.views.timer_view, name='auth_timer_view'),
url(_(r'^add_timer/$'), timerboard.views.add_timer_view, name='auth_add_timer_view'),

View File

@ -21,5 +21,5 @@ def states(request):
def sso(request):
return {
'EVE_SSO_CALLBACK_URL': settings.EVE_SSO_CALLBACK_URL,
'EVE_SSO_CALLBACK_URL': settings.ESI_SSO_CALLBACK_URL,
}

View File

@ -10,7 +10,7 @@ from authentication.models import AuthServicesInfo
from authentication.forms import LoginForm, RegistrationForm
from django.contrib.auth.models import User
from django.contrib import messages
from eve_sso.decorators import token_required
from esi.decorators import token_required
import logging
logger = logging.getLogger(__name__)
@ -102,13 +102,14 @@ def help_view(request):
return render(request, 'registered/help.html')
@token_required(new=True)
def sso_login(request, tokens=[]):
token = tokens[0]
def sso_login(request, token):
try:
char = EveCharacter.objects.get(character_id=token.character_id)
if char.user:
if char.user.is_active:
login(request, char.user)
token.user = char.user
token.save()
return redirect(dashboard_view)
else:
messages.error(request, 'Your account has been disabled.')

View File

@ -1 +1,5 @@
from __future__ import unicode_literals
from corputils.models import CorpStats
from django.contrib import admin
admin.site.register(CorpStats)

View File

@ -1,8 +0,0 @@
from __future__ import unicode_literals
from django import forms
from django.utils.translation import ugettext_lazy as _
class CorputilsSearchForm(forms.Form):
search_string = forms.CharField(max_length=254, required=True, label="",
widget=forms.TextInput(attrs={'placeholder': _('Search characters...')}))

42
corputils/managers.py Normal file
View File

@ -0,0 +1,42 @@
from django.db import models
from authentication.models import AuthServicesInfo
from eveonline.models import EveCharacter
class CorpStatsQuerySet(models.QuerySet):
def visible_to(self, user):
# superusers get all visible
if user.is_superuser:
return self
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
try:
char = EveCharacter.objects.get(character_id=auth.main_char_id)
# build all accepted queries
queries = []
if user.has_perm('corputils.view_corp_corpstats'):
queries.append(models.Q(corp__corporation_id=char.corporation_id))
if user.has_perm('corputils.view_alliance_corpstats'):
queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id))
if user.has_perm('corputils.view_blue_corpstats'):
queries.append(models.Q(corp__is_blue=True))
# filter based on queries
if queries:
query = queries.pop()
for q in queries:
query |= q
return self.filter(query)
else:
# not allowed to see any
return self.none()
except EveCharacter.DoesNotExist:
return self.none()
class CorpStatsManager(models.Manager):
def get_queryset(self):
return CorpStatsQuerySet(self.model, using=self._db)
def visible_to(self, user):
return self.get_queryset().visible_to(user)

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-12-14 21:36
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('esi', '0002_scopes_20161208'),
('eveonline', '0004_eveapikeypair_sso_verified'),
]
operations = [
migrations.CreateModel(
name='CorpStats',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('last_update', models.DateTimeField(auto_now=True)),
('_members', models.TextField(default='{}')),
('corp', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveCorporationInfo')),
('token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='esi.Token')),
],
options={
'default_permissions': ('add', 'change', 'remove', 'view_corp', 'view_alliance', 'view_blue'),
'verbose_name': 'corp stats',
'verbose_name_plural': 'corp stats',
'permissions': (('corp_apis', 'Can view API keys of members of their corporation.'), ('alliance_apis', 'Can view API keys of members of their alliance.'), ('blue_apis', 'Can view API keys of members of blue corporations.')),
},
),
]

View File

@ -0,0 +1,125 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-12-14 21:48
from __future__ import unicode_literals
from django.db import migrations
PERMISSIONS = {
'user': [
'corp_apis',
'alliance_apis',
],
'corpstats': {
'corp_apis': 'Can view API keys of members of their corporation.',
'alliance_apis': 'Can view API keys of members of their alliance.',
'blue_apis': 'Can view API keys of members of blue corporations.',
'view_corp_corpstats': 'Can view_corp corpstats',
'view_alliance_corpstats': 'Can view_alliance corpstats',
'view_blue_corpstats': 'Can view_blue corpstats',
}
}
def user_permissions_dict(apps):
Permission = apps.get_model('auth', 'Permission')
ContentType = apps.get_model('contenttypes', 'ContentType')
User = apps.get_model('auth', 'User')
CorpStats = apps.get_model('corputils', 'CorpStats')
user_ct = ContentType.objects.get_for_model(User)
corpstats_ct = ContentType.objects.get_for_model(CorpStats)
return {
'user': {x: Permission.objects.get_or_create(name=x, codename=x, content_type=user_ct)[0] for x in PERMISSIONS['user']},
'corpstats': {x: Permission.objects.get_or_create(codename=x, content_type=corpstats_ct)[0] for x, y in PERMISSIONS['corpstats'].items()},
}
def users_with_permission(apps, perm):
User = apps.get_model('auth', 'User')
return User.objects.filter(user_permissions=perm.pk)
def groups_with_permission(apps, perm):
Group = apps.get_model('auth', 'Group')
return Group.objects.filter(permissions=perm.pk)
def forward(apps, schema_editor):
perm_dict = user_permissions_dict(apps)
corp_users = users_with_permission(apps, perm_dict['user']['corp_apis'])
for u in corp_users:
u.user_permissions.add(perm_dict['corpstats']['corp_apis'].pk)
u.user_permissions.add(perm_dict['corpstats']['view_corp_corpstats'].pk)
alliance_users = users_with_permission(apps, perm_dict['user']['alliance_apis'])
for u in alliance_users:
u.user_permissions.add(perm_dict['corpstats']['alliance_apis'].pk)
u.user_permissions.add(perm_dict['corpstats']['view_alliance_corpstats'].pk)
corp_groups = groups_with_permission(apps, perm_dict['user']['corp_apis'])
for g in corp_groups:
g.permissions.add(perm_dict['corpstats']['corp_apis'].pk)
g.permissions.add(perm_dict['corpstats']['view_corp_corpstats'].pk)
alliance_groups = groups_with_permission(apps, perm_dict['user']['alliance_apis'])
for g in alliance_groups:
g.permissions.add(perm_dict['corpstats']['alliance_apis'].pk)
g.permissions.add(perm_dict['corpstats']['view_alliance_corpstats'].pk)
for name, perm in perm_dict['user'].items():
perm.delete()
def reverse(apps, schema_editor):
perm_dict = user_permissions_dict(apps)
corp_users = users_with_permission(apps, perm_dict['corpstats']['view_corp_corpstats'])
corp_api_users = users_with_permission(apps, perm_dict['corpstats']['corp_apis'])
corp_us = corp_users | corp_api_users
for u in corp_us.distinct():
u.user_permissions.add(perm_dict['user']['corp_apis'].pk)
for u in corp_users:
u.user_permissions.remove(perm_dict['corpstats']['view_corp_corpstats'].pk)
for u in corp_api_users:
u.user_permissions.remove(perm_dict['corpstats']['corp_apis'].pk)
alliance_users = users_with_permission(apps, perm_dict['corpstats']['view_alliance_corpstats'])
alliance_api_users = users_with_permission(apps, perm_dict['corpstats']['alliance_apis'])
alliance_us = alliance_users | alliance_api_users
for u in alliance_us.distinct():
u.user_permissions.add(perm_dict['user']['alliance_apis'].pk)
for u in alliance_users:
u.user_permissions.remove(perm_dict['corpstats']['view_alliance_corpstats'].pk)
for u in alliance_api_users:
u.user_permissions.remove(perm_dict['corpstats']['alliance_apis'].pk)
corp_groups = groups_with_permission(apps, perm_dict['corpstats']['view_corp_corpstats'])
corp_api_groups = groups_with_permission(apps, perm_dict['corpstats']['corp_apis'])
corp_gs = corp_groups | corp_api_groups
for g in corp_groups.distinct():
g.permissions.add(perm_dict['user']['corp_apis'].pk)
for g in corp_groups:
g.permissions.remove(perm_dict['corpstats']['view_corp_corpstats'].pk)
for g in corp_api_groups:
g.permissions.remove(perm_dict['corpstats']['corp_apis'].pk)
alliance_groups = groups_with_permission(apps, perm_dict['corpstats']['view_alliance_corpstats'])
alliance_api_groups = groups_with_permission(apps, perm_dict['corpstats']['alliance_apis'])
alliance_gs = alliance_groups | alliance_api_groups
for g in alliance_gs.distinct():
g.permissions.add(perm_dict['user']['alliance_apis'].pk)
for g in alliance_groups:
g.permissions.remove(perm_dict['corpstats']['view_alliance_corpstats'].pk)
for g in alliance_api_groups:
g.permissions.remove(perm_dict['corpstats']['alliance_apis'].pk)
class Migration(migrations.Migration):
dependencies = [
('corputils', '0001_initial'),
('authentication', '0005_delete_perms'),
('auth', '0008_alter_user_username_max_length'),
]
operations = [
migrations.RunPython(forward, reverse),
]

View File

View File

@ -1 +1,174 @@
from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible
from django.db import models
from eveonline.models import EveCorporationInfo, EveCharacter, EveApiKeyPair
from esi.models import Token
from esi.errors import TokenError
from notifications import notify
from authentication.models import AuthServicesInfo
from bravado.exception import HTTPForbidden
from corputils.managers import CorpStatsManager
from operator import attrgetter
import json
import logging
logger = logging.getLogger(__name__)
@python_2_unicode_compatible
class CorpStats(models.Model):
token = models.ForeignKey(Token, on_delete=models.CASCADE)
corp = models.OneToOneField(EveCorporationInfo)
last_update = models.DateTimeField(auto_now=True)
_members = models.TextField(default='{}')
class Meta:
permissions = (
('corp_apis', 'Can view API keys of members of their corporation.'),
('alliance_apis', 'Can view API keys of members of their alliance.'),
('blue_apis', 'Can view API keys of members of blue corporations.'),
)
default_permissions = (
'add',
'change',
'remove',
'view_corp',
'view_alliance',
'view_blue',
)
verbose_name = "corp stats"
verbose_name_plural = "corp stats"
objects = CorpStatsManager()
def __str__(self):
return "%s for %s" % (self.__class__.__name__, self.corp)
def update(self):
try:
c = self.token.get_esi_client()
assert c.Character.get_characters_character_id(character_id=self.token.character_id).result()['corporation_id'] == int(self.corp.corporation_id)
members = c.Corporation.get_corporations_corporation_id_members(corporation_id=self.corp.corporation_id).result()
member_ids = [m['character_id'] for m in members]
member_names = c.Character.get_characters_names(character_ids=member_ids).result()
member_list = {m['character_id']:m['character_name'] for m in member_names}
self.members = member_list
self.save()
except TokenError as e:
logger.warning("%s failed to update: %s" % (self, e))
if self.token.user:
notify(self.token.user, "%s failed to update with your ESI token." % self, message="Your token has expired or is no longer valid. Please add a new one to create a new CorpStats.", level="error")
self.delete()
except HTTPForbidden as e:
logger.warning("%s failed to update: %s" % (self, e))
if self.token.user:
notify(self.token.user, "%s failed to update with your ESI token." % self, message="%s: %s" % (e.status_code, e.message), level="error")
self.delete()
except AssertionError:
logger.warning("%s token character no longer in corp." % self)
if self.token.user:
notify(self.token.user, "%s cannot update with your ESI token." % self, message="%s cannot update with your ESI token as you have left corp." % self, level="error")
self.delete()
@property
def members(self):
return json.loads(self._members)
@members.setter
def members(self, dict):
self._members = json.dumps(dict)
@property
def member_ids(self):
return [id for id, name in self.members.items()]
@property
def member_names(self):
return [name for id, name in self.members.items()]
def show_apis(self, user):
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
if auth.main_char_id:
try:
char = EveCharacter.objects.get(character_id=auth.main_char_id)
if char.corporation_id == self.corp.corporation_id and user.has_perm('corputils.corp_apis'):
return True
if self.corp.alliance and char.alliance_id == self.corp.alliance.alliance_id and user.has_perm('corputils.alliance_apis'):
return True
if user.has_perm('corputils.blue_apis') and self.corp.is_blue:
return True
except EveCharacter.DoesNotExist:
pass
return user.is_superuser
def entered_apis(self):
return EveCharacter.objects.filter(character_id__in=self.member_ids).exclude(api_id__isnull=True).count()
def member_count(self):
return len(self.members)
@python_2_unicode_compatible
class MemberObject(object):
def __init__(self, character_id, character_name, show_apis=False):
self.character_id = character_id
self.character_name = character_name
try:
char = EveCharacter.objects.get(character_id=character_id)
auth = AuthServicesInfo.objects.get(user=char.user)
try:
self.main = EveCharacter.objects.get(character_id=auth.main_char_id)
except EveCharacter.DoesNotExist:
self.main = None
api = EveApiKeyPair.objects.get(api_id=char.api_id)
self.registered = True
if show_apis:
self.api = api
else:
self.api = None
except (EveCharacter.DoesNotExist, AuthServicesInfo.DoesNotExist):
self.main = None
self.api = None
self.registered = False
except EveApiKeyPair.DoesNotExist:
self.api = None
self.registered = False
def __str__(self):
return self.character_name
def portrait_url(self, size=32):
return "https://image.eveonline.com/Character/%s_%s.jpg" % (self.character_id, size)
def get_member_objects(self, user):
show_apis = self.show_apis(user)
return sorted([CorpStats.MemberObject(id, name, show_apis=show_apis) for id, name in self.members.items()], key=attrgetter('character_name'))
def can_update(self, user):
return user.is_superuser or user == self.token.user
@python_2_unicode_compatible
class ViewModel(object):
def __init__(self, corpstats, user):
self.corp = corpstats.corp
self.members = corpstats.get_member_objects(user)
self.can_update = corpstats.can_update(user)
self.total_members = len(self.members)
self.registered_members = corpstats.entered_apis()
self.show_apis = corpstats.show_apis(user)
self.last_updated = corpstats.last_update
def __str__(self):
return str(self.corp)
def corp_logo(self, size=128):
return "https://image.eveonline.com/Corporation/%s_%s.png" % (self.corp.corporation_id, size)
def alliance_logo(self, size=128):
if self.corp.alliance:
return "https://image.eveonline.com/Alliance/%s_%s.png" % (self.corp.alliance.alliance_id, size)
else:
return "https://image.eveonline.com/Alliance/1_%s.png" % size
def get_view_model(self, user):
return CorpStats.ViewModel(self, user)

View File

@ -0,0 +1,39 @@
{% extends 'public/base.html' %}
{% load i18n %}
{% block title %}{% trans "Corporation Member Data" %}{% endblock %}
{% block page_title %}{% trans "Corporation Member Data" %}{% endblock %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Corporation Member Data" %}</h1>
<div class="col-lg-10 col-lg-offset-1 container">
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Corporations<span class="caret"></span></a>
<ul class="dropdown-menu">
{% for corpstat in available %}
<li>
<a href="{% url 'corputils:view_corp' corpstat.corp.corporation_id %}">{{ corpstat.corp.corporation_name }}</a>
</li>
{% endfor %}
</ul>
</li>
{% if perms.corputils.add_corpstats %}
<li>
<a href="{% url 'corputils:add' %}">Add</a>
</li>
{% endif %}
</ul>
<form class="navbar-form navbar-right" role="search" action="{% url 'corputils:search' %}" method="GET">
<div class="form-group">
<input type="text" class="form-control" name="search_string" placeholder="{% if search_string %}{{ search_string }}{% else %}Search characters...{% endif %}">
</div>
</form>
</div>
</nav>
{% block member_data %}{% endblock %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,92 @@
{% extends 'corputils/base.html' %}
{% load i18n %}
{% load humanize %}
{% load bootstrap_pagination %}
{% block member_data %}
{% if corpstats %}
<div class="row">
<div class="col-lg-12 text-center">
<table class="table">
<tr>
<td class="text-center col-lg-6 {% if corpstats.corp.alliance %}{% else %}col-lg-offset-3{% endif %}"><img class="ra-avatar" src="{{ corpstats.corp_logo }}"></td>
{% if corpstats.corp.alliance %}
<td class="text-center col-lg-6"><img class="ra-avatar" src="{{ corpstats.alliance_logo }}"></td>
{% endif %}
</tr>
<tr>
<td class="text-center"><h4>{{ corpstats.corp.corporation_name }}</h4></td>
{% if corpstats.corp.alliance %}
<td class="text-center"><h4>{{ corpstats.corp.alliance.alliance_name }}</h4></td>
{% endif %}
</tr>
</table>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<b>{% trans "API Index:" %}</b>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="{{ corpstats.registered_members }}" aria-valuemin="0" aria-valuemax="{{ corpstats.total_members }}" style="width: {% widthratio corpstats.registered_members corpstats.total_members 100 %}%;">
{{ corpstats.registered_members }}/{{ corpstats.total_members }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading clearfix">
<div class="panel-title pull-left">
<h4>Members</h4>
</div>
<div class="panel-title pull-right">
Last update: {{ corpstats.last_updated|naturaltime }}
{% if corpstats.can_update %}
<a class="btn btn-success" type="button" href="{% url 'corputils:update' corpstats.corp.corporation_id %}" title="Update Now">
<span class="glyphicon glyphicon-refresh"></span>
</a>
{% endif %}
</div>
</div>
<div class="panel-body">
<div class="text-center">
{% bootstrap_paginate members range=10 %}
</div>
<div class="table-responsive">
<table class="table table-hover">
<tr>
<th></th>
<th class="text-center">Character</th>
{% if corpstats.show_apis %}
<th class="text-center">API</th>
{% endif %}
<th class="text-center">zKillboard</th>
<th class="text-center">Main Character</th>
<th class="text-center">Main Corporation</th>
<th class="text-center">Main Alliance</th>
</tr>
{% for member in members %}
<tr {% if not member.registered %}class="danger"{% endif %}>
<td><img src="{{ member.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ member.character_name }}</td>
{% if corpstats.show_apis %}
{% if member.api %}
<td class="text-center"><a href="{{ JACK_KNIFE_URL }}?usid={{ member.api.api_id }}&apik={{ member.api.api_key }}" target="_blank" class="label label-primary">{{ member.api.api_id }}</td>
{% else %}
<td></td>
{% endif %}
{% endif %}
<td class="text-center"><a href="https://zkillboard.com/character/{{ member.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a></td>
<td class="text-center">{{ member.main.character_name }}</td>
<td class="text-center">{{ member.main.corporation_name }}</td>
<td class="text-center">{{ member.main.alliance_name }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,43 @@
{% extends "corputils/base.html" %}
{% load i18n %}
{% load bootstrap_pagination %}
{% block member_data %}
<div class="panel panel-default">
<div class="panel-heading clearfix">
<div class="panel-title pull-left">Search Results</div>
</div>
<div class="panel-body">
<div class="text-center">
{% bootstrap_paginate results range=10 %}
</div>
<table class="table table-hover">
<tr>
<th class="text-center"></th>
<th class="text-center">Character</th>
<th class="text-center">Corporation</th>
<th class="text-center">API</th>
<th class="text-center">zKillboard</th>
<th class="text-center">Main Character</th>
<th class="text-center">Main Corporation</th>
<th class="text-center">Main Alliance</th>
</tr>
{% for result in results %}
<tr {% if not result.1.registered %}class="danger"{% endif %}>
<td class="text-center"><img src="{{ result.1.portrait_url }}" class="img-circle"></td>
<td class="text-center">{{ result.1.character_name }}</td>
<td class="text-center">{{ result.0.corp.corporation_name }}</td>
{% if result.1.api %}
<td class="text-center"><a href="{{ JACK_KNIFE_URL }}?usid={{ result.1.api.api_id }}&apik={{ result.1.api.api_key }}" target="_blank" class="label label-primary">{{ result.1.api.api_id }}</td>
{% else %}
<td></td>
{% endif %}
<td class="text-center"><a href="https://zkillboard.com/character/{{ result.1.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a></td>
<td class="text-center">{{ result.1.main.character_name }}</td>
<td class="text-center">{{ result.1.main.corporation_name }}</td>
<td class="text-center">{{ result.1.main.alliance_name }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% endblock %}

11
corputils/urls.py Normal file
View File

@ -0,0 +1,11 @@
from django.conf.urls import url
import corputils.views
app_name='corputils'
urlpatterns = [
url(r'^$', corputils.views.corpstats_view, name='view'),
url(r'^add/$', corputils.views.corpstats_add, name='add'),
url(r'^(?P<corp_id>(\d)*)/$', corputils.views.corpstats_view, name='view_corp'),
url(r'^(?P<corp_id>(\d)+)/update/$', corputils.views.corpstats_update, name='update'),
url(r'^search/$', corputils.views.corpstats_search, name='search'),
]

View File

@ -1,325 +1,124 @@
from __future__ import unicode_literals
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.db import IntegrityError
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.conf import settings
from eveonline.models import EveCharacter, EveCorporationInfo
from corputils.models import CorpStats
from esi.decorators import token_required
from collections import namedtuple
MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20))
from authentication.models import AuthServicesInfo
from services.managers.eve_api_manager import EveApiManager
from services.managers.evewho_manager import EveWhoManager
from eveonline.models import EveCorporationInfo
from eveonline.models import EveAllianceInfo
from eveonline.models import EveCharacter
from eveonline.models import EveApiKeyPair
from fleetactivitytracking.models import Fat
from corputils.forms import CorputilsSearchForm
from evelink.api import APIError
import logging
import datetime
logger = logging.getLogger(__name__)
class Player(object):
def __init__(self, main, user, maincorp, maincorpid, altlist, apilist, n_fats):
self.main = main
self.user = user
self.maincorp = maincorp
self.maincorpid = maincorpid
self.altlist = altlist
self.apilist = apilist
self.n_fats = n_fats
def first_day_of_next_month(year, month):
if month == 12:
return datetime.datetime(year + 1, 1, 1)
else:
return datetime.datetime(year, month + 1, 1)
def first_day_of_previous_month(year, month):
if month == 1:
return datetime.datetime(year - 1, 12, 1)
else:
return datetime.datetime(year, month - 1, 1)
def get_page(model_list, page_num):
p = Paginator(model_list, MEMBERS_PER_PAGE)
try:
members = p.page(page_num)
except PageNotAnInteger:
members = p.page(1)
except EmptyPage:
members = p.page(p.num_pages)
return members
def access_corpstats_test(user):
return user.has_perm('corputils.view_corp_corpstats') or user.has_perm('corputils.view_alliance_corpstats') or user.has_perm('corputils.view_blue_corpstats')
@login_required
def corp_member_view(request, corpid=None, year=datetime.date.today().year, month=datetime.date.today().month):
year = int(year)
month = int(month)
start_of_month = datetime.datetime(year, month, 1)
start_of_next_month = first_day_of_next_month(year, month)
start_of_previous_month = first_day_of_previous_month(year, month)
logger.debug("corp_member_view called by user %s" % request.user)
@user_passes_test(access_corpstats_test)
@permission_required('corputils.add_corpstats')
@token_required(scopes='esi-corporations.read_corporation_membership.v1')
def corpstats_add(request, token):
try:
user_main = EveCharacter.objects.get(
character_id=AuthServicesInfo.objects.get_or_create(user=request.user)[0].main_char_id)
user_corp_id = user_main.corporation_id
except (ValueError, EveCharacter.DoesNotExist):
user_corp_id = settings.CORP_ID
if not settings.IS_CORP:
alliance = EveAllianceInfo.objects.get(alliance_id=settings.ALLIANCE_ID)
alliancecorps = EveCorporationInfo.objects.filter(alliance=alliance)
membercorplist = [(int(membercorp.corporation_id), str(membercorp.corporation_name)) for membercorp in
alliancecorps]
membercorplist.sort(key=lambda tup: tup[1])
membercorp_id_list = [int(membercorp.corporation_id) for membercorp in alliancecorps]
bluecorps = EveCorporationInfo.objects.filter(is_blue=True)
bluecorplist = [(int(bluecorp.corporation_id), str(bluecorp.corporation_name)) for bluecorp in bluecorps]
bluecorplist.sort(key=lambda tup: tup[1])
bluecorp_id_list = [int(bluecorp.corporation_id) for bluecorp in bluecorps]
if not (user_corp_id in membercorp_id_list or user_corp_id not in bluecorp_id_list):
user_corp_id = None
if not corpid:
if settings.IS_CORP:
corpid = settings.CORP_ID
elif user_corp_id:
corpid = user_corp_id
if EveCharacter.objects.filter(character_id=token.character_id).exists():
corp_id = EveCharacter.objects.get(character_id=token.character_id).corporation_id
else:
corpid = membercorplist[0][0]
corp = EveCorporationInfo.objects.get(corporation_id=corpid)
if request.user.has_perm('auth.alliance_apis') or (request.user.has_perm('auth.corp_apis') and user_corp_id == corpid):
logger.debug("Retreiving and sending API-information")
if settings.IS_CORP:
try:
member_list = EveApiManager.get_corp_membertracking(settings.CORP_API_ID, settings.CORP_API_VCODE)
except APIError:
logger.debug("Corp API does not have membertracking scope, using EveWho data instead.")
member_list = EveWhoManager.get_corporation_members(corpid)
else:
member_list = EveWhoManager.get_corporation_members(corpid)
characters_with_api = {}
characters_without_api = {}
num_registered_characters = 0
for char_id, member_data in member_list.items():
try:
char = EveCharacter.objects.get(character_id=char_id)
char_owner = char.user
try:
if not char_owner:
raise AttributeError("Character has no assigned user.")
mainid = int(AuthServicesInfo.objects.get_or_create(user=char_owner)[0].main_char_id)
mainchar = EveCharacter.objects.get(character_id=mainid)
mainname = mainchar.character_name
maincorp = mainchar.corporation_name
maincorpid = mainchar.corporation_id
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
except (ValueError, EveCharacter.DoesNotExist, EveApiKeyPair.DoesNotExist):
logger.debug("No main character seem to be set for character %s" % char.character_name)
mainname = "User: " + char_owner.username
mainchar = char
maincorp = "Not set."
maincorpid = None
api_pair = None
except AttributeError:
logger.debug("No associated user for character %s" % char.character_name)
mainname = None
mainchar = char
maincorp = None
maincorpid = None
try:
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
except EveApiKeyPair.DoesNotExist:
api_pair = None
num_registered_characters += 1
characters_with_api.setdefault(mainname, Player(main=mainchar,
user=char_owner,
maincorp=maincorp,
maincorpid=maincorpid,
altlist=[],
apilist=[],
n_fats=0)
).altlist.append(char)
if api_pair:
characters_with_api[mainname].apilist.append(api_pair)
except EveCharacter.DoesNotExist:
characters_without_api.update({member_data["name"]: member_data["id"]})
for char in EveCharacter.objects.filter(corporation_id=corpid):
if not int(char.character_id) in member_list:
logger.debug("Character '%s' does not exist in EveWho dump." % char.character_name)
char_owner = char.user
try:
if not char_owner:
raise AttributeError("Character has no assigned user.")
mainid = int(AuthServicesInfo.objects.get_or_create(user=char_owner)[0].main_char_id)
mainchar = EveCharacter.objects.get(character_id=mainid)
mainname = mainchar.character_name
maincorp = mainchar.corporation_name
maincorpid = mainchar.corporation_id
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
except (ValueError, EveCharacter.DoesNotExist, EveApiKeyPair.DoesNotExist):
logger.debug("No main character seem to be set for character %s" % char.character_name)
mainname = "User: " + char_owner.username
mainchar = char
maincorp = "Not set."
maincorpid = None
api_pair = None
except AttributeError:
logger.debug("No associated user for character %s" % char.character_name)
mainname = None
mainchar = char
maincorp = None
maincorpid = None
try:
api_pair = EveApiKeyPair.objects.get(api_id=char.api_id)
except EveApiKeyPair.DoesNotExist:
api_pair = None
num_registered_characters += 1
characters_with_api.setdefault(mainname, Player(main=mainchar,
user=char_owner,
maincorp=maincorp,
maincorpid=maincorpid,
altlist=[],
apilist=[],
n_fats=0)
).altlist.append(char)
if api_pair:
characters_with_api[mainname].apilist.append(api_pair)
n_unacounted = corp.member_count - (num_registered_characters + len(characters_without_api))
for mainname, player in characters_with_api.items():
fats_this_month = Fat.objects.filter(user=player.user).filter(
fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lt=start_of_next_month)
characters_with_api[mainname].n_fats = len(fats_this_month)
if start_of_next_month > datetime.datetime.now():
start_of_next_month = None
if not settings.IS_CORP:
context = {"membercorplist": membercorplist,
"corp": corp,
"characters_with_api": sorted(characters_with_api.items()),
'n_registered': num_registered_characters,
'n_unacounted': n_unacounted,
"characters_without_api": sorted(characters_without_api.items()),
"search_form": CorputilsSearchForm()}
else:
logger.debug("corp_member_view running in corportation mode")
context = {"corp": corp,
"characters_with_api": sorted(characters_with_api.items()),
'n_registered': num_registered_characters,
'n_unacounted': n_unacounted,
"characters_without_api": sorted(characters_without_api.items()),
"search_form": CorputilsSearchForm()}
context["next_month"] = start_of_next_month
context["previous_month"] = start_of_previous_month
context["this_month"] = start_of_month
return render(request, 'registered/corputils.html', context=context)
else:
logger.warn('User %s (%s) not authorized to view corp stats for corp id %s' % (request.user, user_corp_id, corpid))
return redirect("auth_dashboard")
def can_see_api(user, character):
if user.has_perm('auth.alliance_apis'):
return True
try:
user_main = EveCharacter.objects.get(
character_id=AuthServicesInfo.objects.get_or_create(user=user)[0].main_char_id)
if user.has_perm('auth.corp_apis') and user_main.corporation_id == character.corporation_id:
return True
except EveCharacter.DoesNotExist:
return False
return False
corp_id = token.get_esi_client().Character.get_characters_character_id(character_id=token.character_id).result()['corporation_id']
corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
cs = CorpStats.objects.create(token=token, corp=corp)
cs.update()
assert cs.pk # ensure update was succesful
if CorpStats.objects.filter(pk=cs.pk).visible_to(request.user).exists():
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
except EveCorporationInfo.DoesNotExist:
messages.error(request, 'Unrecognized corporation. Please ensure it is a member of the alliance or a blue.')
except IntegrityError:
messages.error(request, 'Selected corp already has a statistics module.')
except AssertionError:
messages.error(request, 'Failed to gather corporation statistics with selected token.')
return redirect('corputils:view')
@login_required
def corputils_search(request, corpid=settings.CORP_ID):
logger.debug("corputils_search called by user %s" % request.user)
@user_passes_test(access_corpstats_test)
def corpstats_view(request, corp_id=None):
corpstats = None
show_apis = False
corp = EveCorporationInfo.objects.get(corporation_id=corpid)
# get requested model
if corp_id:
corp = get_object_or_404(EveCorporationInfo, corporation_id=corp_id)
corpstats = get_object_or_404(CorpStats, corp=corp)
authorized = False
try:
user_main = EveCharacter.objects.get(
character_id=AuthServicesInfo.objects.get_or_create(user=request.user)[0].main_char_id)
if request.user.has_perm('auth.alliance_apis') or (
request.user.has_perm('auth.corp_apis') and (user_main.corporation_id == corpid)):
logger.debug("Retreiving and sending API-information")
authorized = True
except (ValueError, EveCharacter.DoesNotExist):
if request.user.has_perm('auth.alliance_apis'):
logger.debug("Retrieving and sending API-information")
authorized = True
# get available models
available = CorpStats.objects.visible_to(request.user)
if authorized:
if request.method == 'POST':
form = CorputilsSearchForm(request.POST)
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
if form.is_valid():
# Really dumb search and only checks character name
# This can be improved but it does the job for now
searchstring = form.cleaned_data['search_string']
logger.debug("Searching for player with character name %s for user %s" % (searchstring, request.user))
# ensure we can see the requested model
if corpstats and not corpstats in available:
raise PermissionDenied('You do not have permission to view the selected corporation statistics module.')
member_list = {}
if settings.IS_CORP:
member_list = EveApiManager.get_corp_membertracking(settings.CORP_API_ID, settings.CORP_API_VCODE)
if not member_list:
logger.debug('Unable to fetch members from API. Pulling from EveWho')
member_list = EveWhoManager.get_corporation_members(corpid)
# get default model if none requested
if not corp_id and available.count() == 1:
corpstats = available[0]
SearchResult = namedtuple('SearchResult',
['name', 'id', 'main', 'api_registered', 'character', 'apiinfo'])
context = {
'available': available,
}
searchresults = []
for memberid, member_data in member_list.items():
if searchstring.lower() in member_data["name"].lower():
try:
char = EveCharacter.objects.get(character_name=member_data["name"])
user = char.user
mainid = int(AuthServicesInfo.objects.get_or_create(user=user)[0].main_char_id)
main = EveCharacter.objects.get(character_id=mainid)
if can_see_api(request.user, char):
api_registered = True
apiinfo = EveApiKeyPair.objects.get(api_id=char.api_id)
else:
api_registered = False
apiinfo = None
except EveCharacter.DoesNotExist:
api_registered = False
char = None
main = ""
apiinfo = None
# paginate
members = []
if corpstats:
page = request.GET.get('page', 1)
members = get_page(corpstats.get_member_objects(request.user), page)
searchresults.append(SearchResult(name=member_data["name"], id=memberid, main=main,
api_registered=api_registered,
character=char, apiinfo=apiinfo))
if corpstats:
context.update({
'corpstats': corpstats.get_view_model(request.user),
'members': members,
})
logger.info("Found %s members for user %s matching search string %s" % (
len(searchresults), request.user, searchstring))
return render(request, 'corputils/corpstats.html', context=context)
context = {'corp': corp, 'results': searchresults, 'search_form': CorputilsSearchForm(),
"year": datetime.datetime.now().year, "month": datetime.datetime.now().month}
return render(request, 'registered/corputilssearchview.html',
context=context)
else:
logger.debug("Form invalid - returning for user %s to retry." % request.user)
context = {'corp': corp, 'members': None, 'search_form': CorputilsSearchForm()}
return render(request, 'registered/corputilssearchview.html', context=context)
else:
logger.debug("Returning empty search form for user %s" % request.user)
return redirect("auth_corputils")
@login_required
@user_passes_test(access_corpstats_test)
def corpstats_update(request, corp_id):
corp = get_object_or_404(EveCorporationInfo, corporation_id=corp_id)
corpstats = get_object_or_404(CorpStats, corp=corp)
if corpstats.can_update(request.user):
corpstats.update()
else:
logger.warn('User %s not authorized to view corp stats for corp ID %s' % (request.user, corpid))
return redirect("auth_dashboard")
raise PermissionDenied('You do not have permission to update member data for the selected corporation statistics module.')
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
@login_required
@user_passes_test(access_corpstats_test)
def corpstats_search(request):
results = []
search_string = request.GET.get('search_string', None)
if search_string:
has_similar = CorpStats.objects.filter(_members__icontains=search_string).visible_to(request.user)
for corpstats in has_similar:
similar = [(member_id, corpstats.members[member_id]) for member_id in corpstats.members if search_string.lower() in corpstats.members[member_id].lower()]
for s in similar:
results.append((corpstats, CorpStats.MemberObject(s[0], s[1], show_apis=corpstats.show_apis(request.user))))
page = request.GET.get('page', 1)
results = sorted(results, key=lambda x: x[1].character_name)
results_page = get_page(results, page)
context = {
'available': CorpStats.objects.visible_to(request.user),
'results': results_page,
'search_string': search_string,
}
return render(request, 'corputils/search.html', context=context)
return redirect('corputils:view')

View File

@ -3,71 +3,48 @@ from eveonline.models import EveCharacter
from eveonline.models import EveApiKeyPair
from eveonline.models import EveAllianceInfo
from eveonline.models import EveCorporationInfo
from eveonline.providers import eve_adapter_factory, EveXmlProvider
from services.managers.eve_api_manager import EveApiManager
import logging
logger = logging.getLogger(__name__)
adapter = eve_adapter_factory()
class EveManager:
def __init__(self):
pass
@staticmethod
def create_character(character_id, character_name, corporation_id,
corporation_name, corporation_ticker, alliance_id,
alliance_name, user, api_id):
logger.debug("Creating model for character %s id %s" % (character_name, character_id))
if not EveCharacter.objects.filter(character_id=character_id).exists():
eve_char = EveCharacter()
eve_char.character_id = character_id
eve_char.character_name = character_name
eve_char.corporation_id = corporation_id
eve_char.corporation_name = corporation_name
eve_char.corporation_ticker = corporation_ticker
eve_char.alliance_id = alliance_id
eve_char.alliance_name = alliance_name
eve_char.user = user
eve_char.api_id = api_id
eve_char.save()
logger.info("Created new character model %s for user %s" % (eve_char, user))
else:
logger.warn("Attempting to create existing character model with id %s" % character_id)
def create_character(id, user, api_id):
return EveManager.create_character_obj(adapter.get_character(id), user, api_id)
@staticmethod
def create_characters_from_list(chars, user, api_id):
logger.debug("Creating characters from batch: %s" % chars.result)
for char in chars.result:
if not EveManager.check_if_character_exist(chars.result[char]['name']):
EveManager.create_character(chars.result[char]['id'],
chars.result[char]['name'],
chars.result[char]['corp']['id'],
chars.result[char]['corp']['name'],
EveApiManager.get_corporation_ticker_from_id(
chars.result[char]['corp']['id']),
chars.result[char]['alliance']['id'],
chars.result[char]['alliance']['name'],
user, api_id)
def create_character_obj(character, user, api_id):
EveCharacter.objects.create(
character_id = character.id,
character_name = character.name,
corporation_id = character.corp.id,
corporation_name = character.corp.name,
alliance_id = character.alliance.id,
alliance_name = character.alliance.name,
user = user,
api_id = api_id,
)
@staticmethod
def update_characters_from_list(chars):
logger.debug("Updating characters from list: %s" % chars.result)
for char in chars.result:
if EveManager.check_if_character_exist(chars.result[char]['name']):
eve_char = EveManager.get_character_by_character_name(chars.result[char]['name'])
logger.debug("Got existing character model %s" % eve_char)
eve_char.corporation_id = chars.result[char]['corp']['id']
eve_char.corporation_name = chars.result[char]['corp']['name']
eve_char.corporation_ticker = EveApiManager.get_corporation_ticker_from_id(
chars.result[char]['corp']['id'])
eve_char.alliance_id = chars.result[char]['alliance']['id']
eve_char.alliance_name = chars.result[char]['alliance']['name']
eve_char.save()
logger.info("Updated character model %s" % eve_char)
else:
logger.warn(
"Attempting to update non-existing character model with name %s" % chars.result[char]['name'])
def update_character(id):
return EveManager.update_character_obj(adapter.get_character(id))
@staticmethod
def update_character_obj(char):
model = EveCharacter.objects.get(character_id=char.id)
model.character_name = char.name
model.corporation_id = char.corp.id
model.corporation_name = char.corp.name
model.alliance_id = char.alliance.id
model.alliance_name = char.alliance.name
model.save()
@staticmethod
def create_api_keypair(api_id, api_key, user_id):
@ -83,64 +60,80 @@ class EveManager:
logger.warn("Attempting to create existing api keypair with id %s" % api_id)
@staticmethod
def create_alliance_info(alliance_id, alliance_name, alliance_ticker, alliance_executor_corp_id,
alliance_member_count, is_blue):
logger.debug("Creating alliance info for alliance %s id %s" % (alliance_name, alliance_id))
if not EveManager.check_if_alliance_exists_by_id(alliance_id):
alliance_info = EveAllianceInfo()
alliance_info.alliance_id = alliance_id
alliance_info.alliance_name = alliance_name
alliance_info.alliance_ticker = alliance_ticker
alliance_info.executor_corp_id = alliance_executor_corp_id
alliance_info.member_count = alliance_member_count
alliance_info.is_blue = is_blue
alliance_info.save()
logger.info("Created alliance model for %s" % alliance_info)
else:
logger.warn("Attempting to create existing alliance model with id %s" % alliance_id)
def create_alliance(id, is_blue=False):
return EveManager.create_alliance_obj(adapter.get_alliance(id), is_blue=is_blue)
@staticmethod
def update_alliance_info(alliance_id, alliance_executor_corp_id, alliance_member_count, is_blue):
logger.debug("Updating alliance model with id %s" % alliance_id)
if EveManager.check_if_alliance_exists_by_id(alliance_id):
alliance_info = EveAllianceInfo.objects.get(alliance_id=alliance_id)
alliance_info.executor_corp_id = alliance_executor_corp_id
alliance_info.member_count = alliance_member_count
alliance_info.is_blue = is_blue
alliance_info.save()
logger.debug("Updated alliance model %s" % alliance_info)
else:
logger.warn("Attempting to update non-existing alliance model with id %s" % alliance_id)
def create_alliance_obj(alliance, is_blue=False):
EveAllianceInfo.objects.create(
alliance_id = alliance.id,
alliance_name = alliance.name,
alliance_ticker = alliance.ticker,
executor_corp_id = alliance.executor_corp_id,
is_blue = is_blue,
)
@staticmethod
def create_corporation_info(corp_id, corp_name, corp_ticker, corp_member_count, is_blue, alliance):
logger.debug("Creating corp info for corp %s id %s" % (corp_name, corp_id))
if not EveManager.check_if_corporation_exists_by_id(corp_id):
corp_info = EveCorporationInfo()
corp_info.corporation_id = corp_id
corp_info.corporation_name = corp_name
corp_info.corporation_ticker = corp_ticker
corp_info.member_count = corp_member_count
corp_info.is_blue = is_blue
if alliance:
corp_info.alliance = alliance
corp_info.save()
logger.info("Created corp model for %s" % corp_info)
else:
logger.warn("Attempting to create existing corp model with id %s" % corp_id)
def update_alliance(id, is_blue=None):
return EveManager.update_alliance_obj(adapter.get_alliance(id), is_blue=is_blue)
@staticmethod
def update_corporation_info(corp_id, corp_member_count, alliance, is_blue):
logger.debug("Updating corp model with id %s" % corp_id)
if EveManager.check_if_corporation_exists_by_id(corp_id):
corp_info = EveCorporationInfo.objects.get(corporation_id=corp_id)
corp_info.member_count = corp_member_count
corp_info.alliance = alliance
corp_info.is_blue = is_blue
corp_info.save()
logger.debug("Updated corp model %s" % corp_info)
else:
logger.warn("Attempting to update non-existant corp model with id %s" % corp_id)
def update_alliance_obj(alliance, is_blue=None):
model = EveAllianceInfo.objects.get(alliance_id=alliance.id)
model.executor_corp_id = alliance.executor_corp_id
model.is_blue = model.is_blue if is_blue == None else is_blue
model.save()
@staticmethod
def populate_alliance(id):
alliance_model = EveAllianceInfo.objects.get(alliance_id=id)
alliance = adapter.get_alliance(id)
for corp_id in alliance.corp_ids:
if not EveCorporationInfo.objects.filter(corporation_id=corp_id).exists():
EveManager.create_corporation(corp_id, is_blue=alliance_model.is_blue)
EveCorporationInfo.objects.filter(corporation_id__in=alliance.corp_ids).update(alliance=alliance_model)
EveCorporationInfo.objects.filter(alliance=alliance_model).exclude(corporation_id__in=alliance.corp_ids).update(alliance=None)
@staticmethod
def create_corporation(id, is_blue=False):
return EveManager.create_corporation_obj(adapter.get_corp(id), is_blue=is_blue)
@staticmethod
def create_corporation_obj(corp, is_blue=False):
try:
alliance = EveAllianceInfo.objects.get(alliance_id=corp.alliance_id)
except EveAllianceInfo.DoesNotExist:
alliance = None
EveCorporationInfo.objects.create(
corporation_id = corp.id,
corporation_name = corp.name,
corporation_ticker = corp.ticker,
member_count = corp.members,
alliance = alliance,
is_blue = is_blue,
)
@staticmethod
def update_corporation(id, is_blue=None):
return EveManager.update_corporation_obj(adapter.get_corp(id), is_blue=is_blue)
@staticmethod
def update_corporation_obj(corp, is_blue=None):
model = EveCorporationInfo.objects.get(corporation_id=corp.id)
model.member_count = corp.members
try:
model.alliance = EveAllianceInfo.objects.get(alliance_id=corp.alliance_id)
except EveAllianceInfo.DoesNotExist:
model.alliance = None
model.is_blue = model.is_blue if is_blue == None else is_blue
model.save()
@staticmethod
def get_characters_from_api(api):
char_result = EveApiManager.get_characters_from_api(api.api_id, api.api_key).result
provider = EveXmlProvider(adapter=adapter)
return [provider._build_character(result) for id, result in char_result.items()]
@staticmethod
def get_api_key_pairs(user):

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-12-16 23:22
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('eveonline', '0004_eveapikeypair_sso_verified'),
]
operations = [
migrations.RemoveField(
model_name='eveallianceinfo',
name='member_count',
),
]

View File

@ -38,7 +38,6 @@ class EveAllianceInfo(models.Model):
alliance_ticker = models.CharField(max_length=254)
executor_corp_id = models.CharField(max_length=254)
is_blue = models.BooleanField(default=False)
member_count = models.IntegerField()
def __str__(self):
return self.alliance_name

279
eveonline/providers.py Normal file
View File

@ -0,0 +1,279 @@
from django.utils.encoding import python_2_unicode_compatible
from esi.clients import esi_client_factory
from django.conf import settings
from bravado.exception import HTTPNotFound, HTTPUnprocessableEntity
import evelink
@python_2_unicode_compatible
class ObjectNotFound(Exception):
def __init__(self, id, type):
self.id = id
self.type = type
def __str__(self):
return '%s with ID %s not found.' % (self.type, self.id)
@python_2_unicode_compatible
class Entity(object):
def __init__(self, id, name):
self.id = id
self.name = name
def __str__(self):
return self.name
def __repr__(self):
return "<{} ({}): {}>".format(self.__class__.__name__, self.id, self.name)
def __bool__(self):
return bool(self.id)
def __eq__(self, other):
return self.id == other.id
class Corporation(Entity):
def __init__(self, provider, id, name, ticker, ceo_id, members, alliance_id):
super(Corporation, self).__init__(id, name)
self.provider = provider
self.ticker = ticker
self.ceo_id = ceo_id
self.members = members
self.alliance_id = alliance_id
@property
def alliance(self):
if self.alliance_id:
return self.provider.get_alliance(self.alliance_id)
return Entity(None, None)
@property
def ceo(self):
return self.provider.get_character(self.ceo_id)
class Alliance(Entity):
def __init__(self, provider, id, name, ticker, corp_ids, executor_corp_id):
super(Alliance, self).__init__(id, name)
self.provider = provider
self.ticker = ticker
self.corp_ids = corp_ids
self.executor_corp_id = executor_corp_id
def corp(self, id):
assert id in self.corp_ids
return self.provider.get_corp(id)
@property
def corps(self):
return sorted([self.corp(id) for id in self.corp_ids], key=lambda x: x.name)
@property
def executor_corp(self):
return self.provider.get_corp(self.executor_corp_id)
class Character(Entity):
def __init__(self, provider, id, name, corp_id, alliance_id):
super(Character, self).__init__(id, name)
self.provider = provider
self.corp_id = corp_id
self.alliance_id = alliance_id
@property
def corp(self):
return self.provider.get_corp(self.corp_id)
@property
def alliance(self):
if self.alliance_id:
return self.provider.get_alliance(self.alliance_id)
return Entity(None, None)
class EveProvider:
def get_alliance(self, alliance_id):
"""
:return: an Alliance object for the given ID
"""
raise NotImplementedError()
def get_corp(self, corp_id):
"""
:return: a Corporation object for the given ID
"""
raise NotImplementedError()
def get_character(self, corp_id):
"""
:return: a Character object for the given ID
"""
raise NotImplementedError()
@python_2_unicode_compatible
class EveSwaggerProvider(EveProvider):
def __init__(self, token=None, adapter=None):
self.client = esi_client_factory(token=token)
self.adapter = adapter or self
def __str__(self):
return 'esi'
def get_alliance(self, id):
try:
data = self.client.Alliance.get_alliances_alliance_id(alliance_id=id).result()
corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=id).result()
model = Alliance(
self.adapter,
id,
data['alliance_name'],
data['ticker'],
corps,
data['executor_corp'],
)
return model
except HTTPNotFound:
raise ObjectNotFound(id, 'alliance')
def get_corp(self, id):
try:
data = self.client.Corporation.get_corporations_corporation_id(corporation_id=id).result()
model = Corporation(
self.adapter,
id,
data['corporation_name'],
data['ticker'],
data['ceo_id'],
data['member_count'],
data['alliance_id'] if 'alliance_id' in data else None,
)
return model
except HTTPNotFound:
raise ObjectNotFound(id, 'corporation')
def get_character(self, id):
try:
data = self.client.Character.get_characters_character_id(character_id=id).result()
alliance_id = self.adapter.get_corp(data['corporation_id']).alliance_id
model = Character(
self.adapter,
id,
data['name'],
data['corporation_id'],
alliance_id,
)
return model
except (HTTPNotFound, HTTPUnprocessableEntity):
raise ObjectNotFound(id, 'character')
@python_2_unicode_compatible
class EveXmlProvider(EveProvider):
def __init__(self, api_key=None, adapter=None):
"""
:param api_key: eveonline.EveApiKeyPair
"""
self.api = evelink.api.API(api_key=(api_key.api_id, api_key.api_key)) if api_key else evelink.api.API()
self.adapter = adapter or self
def __str__(self):
return 'xml'
def get_alliance(self, id):
api = evelink.eve.EVE(api=self.api)
alliances = api.alliances().result
try:
results = alliances[int(id)]
except KeyError:
raise ObjectNotFound(id, 'alliance')
model = Alliance(
self.adapter,
id,
results['name'],
results['ticker'],
results['member_corps'],
results['executor_id'],
)
return model
def get_corp(self, id):
api = evelink.corp.Corp(api=self.api)
try:
corpinfo = api.corporation_sheet(corp_id=int(id)).result
except evelink.api.APIError as e:
if int(e.code) == 523:
raise ObjectNotFound(id, 'corporation')
raise e
model = Corporation(
self.adapter,
id,
corpinfo['name'],
corpinfo['ceo']['id'],
corpinfo['members']['current'],
corpinfo['ticker'],
corpinfo['alliance']['id'] if corpinfo['alliance'] else None,
)
return model
def _build_character(self, result):
return Character(
self.adapter,
result['id'],
result['name'],
result['corp']['id'],
result['alliance']['id'],
)
def get_character(self, id):
api = evelink.eve.EVE(api=self.api)
try:
charinfo = api.character_info_from_id(id).result
except evelink.api.APIError as e:
if int(e.code) == 105:
raise ObjectNotFound(id, 'character')
raise e
return self._build_character(charinfo)
class EveAdapter(EveProvider):
"""
Redirects queries to appropriate data source.
"""
def __init__(self, char_provider, corp_provider, alliance_provider):
self.char_provider = char_provider
self.corp_provider = corp_provider
self.alliance_provider = alliance_provider
self.char_provider.adapter = self
self.corp_provider.adapter = self
self.alliance_provider.adapter = self
def __repr__(self):
return "<{} (char:{}, corp:{}, alliance:{})>".format(self.__class__.__name__, str(self.char_provider), str(self.corp_provider), str(self.alliance_provider))
def get_character(self, id):
return self.char_provider.get_character(id)
def get_corp(self, id):
return self.corp_provider.get_corp(id)
def get_alliance(self, id):
return self.alliance_provider.get_alliance(id)
def eve_adapter_factory(character_source=settings.EVEONLINE_CHARACTER_PROVIDER, corp_source=settings.EVEONLINE_CORP_PROVIDER, alliance_source=settings.EVEONLINE_ALLIANCE_PROVIDER, api_key=None, token=None):
sources = [character_source, corp_source, alliance_source]
providers = []
xml = EveXmlProvider(api_key=api_key)
esi = EveSwaggerProvider(token=token)
for source in sources:
if source == 'xml':
providers.append(xml)
elif source == 'esi':
providers.append(esi)
else:
raise ValueError('Unrecognized data source "%s"' % source)
return EveAdapter(providers[0], providers[1], providers[2])

View File

@ -12,6 +12,7 @@ from services.managers.eve_api_manager import EveApiManager
from eveonline.models import EveCharacter
from eveonline.models import EveCorporationInfo
from eveonline.models import EveAllianceInfo
from eveonline.providers import eve_adapter_factory
from authentication.tasks import set_state
import logging
import evelink
@ -26,21 +27,16 @@ def refresh_api(api):
try:
EveApiManager.validate_api(api.api_id, api.api_key, api.user)
# Update characters
characters = EveApiManager.get_characters_from_api(api.api_id, api.api_key)
EveManager.update_characters_from_list(characters)
new_character = False
for char in characters.result:
# Ensure we have a model for all characters on key
if not EveManager.check_if_character_exist(characters.result[char]['name']):
logger.debug(
"API key %s has a new character on the account: %s" % (api.api_id, characters.result[char]['name']))
new_character = True
if new_character:
logger.debug("Creating new character %s from api key %s" % (characters.result[char]['name'], api.api_id))
EveManager.create_characters_from_list(characters, api.user, api.api_id)
characters = EveManager.get_characters_from_api(api)
for c in characters:
try:
EveManager.update_character_obj(c)
except EveCharacter.DoesNotExist:
logger.debug("API key %s has a new character on the account: %s" % (api.api_id, c))
EveManager.create_character_obj(c, api.user, api.api_id)
current_chars = EveCharacter.objects.filter(api_id=api.api_id)
for c in current_chars:
if not int(c.character_id) in characters.result:
if not int(c.character_id) in [c.id for c in characters]:
logger.info("Character %s no longer found on API ID %s" % (c, api.api_id))
c.delete()
except evelink.api.APIError as e:
@ -105,65 +101,14 @@ def run_api_refresh():
refresh_user_apis.delay(u)
def populate_alliance(id, blue=False):
logger.debug("Populating alliance model with id %s blue %s" % (id, blue))
alliance_info = EveApiManager.get_alliance_information(id)
if not alliance_info:
raise ValueError("Supplied alliance id %s is invalid" % id)
if not EveAllianceInfo.objects.filter(alliance_id=id).exists():
EveManager.create_alliance_info(alliance_info['id'], alliance_info['name'], alliance_info['ticker'],
alliance_info['executor_id'], alliance_info['member_count'], blue)
alliance = EveAllianceInfo.objects.get(alliance_id=id)
for member_corp in alliance_info['member_corps']:
if EveCorporationInfo.objects.filter(corporation_id=member_corp).exists():
corp = EveCorporationInfo.objects.get(corporation_id=member_corp)
if corp.alliance != alliance:
corp.alliance = alliance
corp.save()
else:
logger.info("Creating new alliance member corp id %s" % member_corp)
corpinfo = EveApiManager.get_corporation_information(member_corp)
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
corpinfo['members']['current'], blue, alliance)
@task
def update_corp(id):
EveManager.update_corporation(id)
@task
def update_alliance(id):
alliance = EveAllianceInfo.objects.get(alliance_id=id)
corps = EveCorporationInfo.objects.filter(alliance=alliance)
logger.debug("Updating alliance %s with %s member corps" % (alliance, len(corps)))
allianceinfo = EveApiManager.get_alliance_information(alliance.alliance_id)
if allianceinfo:
EveManager.update_alliance_info(allianceinfo['id'], allianceinfo['executor_id'],
allianceinfo['member_count'], alliance.is_blue)
for corp in corps:
if corp.corporation_id in allianceinfo['member_corps'] is False:
logger.info("Corp %s no longer in alliance %s" % (corp, alliance))
corp.alliance = None
corp.save()
populate_alliance(alliance.alliance_id, blue=alliance.is_blue)
elif EveApiManager.check_if_alliance_exists(alliance.alliance_id) is False:
logger.info("Alliance %s has closed. Deleting model" % alliance)
alliance.delete()
@task
def update_corp(id):
corp = EveCorporationInfo.objects.get(corporation_id=id)
logger.debug("Updating corp %s" % corp)
corpinfo = EveApiManager.get_corporation_information(corp.corporation_id)
if corpinfo:
alliance = None
if EveAllianceInfo.objects.filter(alliance_id=corpinfo['alliance']['id']).exists():
alliance = EveAllianceInfo.objects.get(alliance_id=corpinfo['alliance']['id'])
EveManager.update_corporation_info(corpinfo['id'], corpinfo['members']['current'], alliance, corp.is_blue)
elif EveApiManager.check_if_corp_exists(corp.corporation_id) is False:
logger.info("Corp %s has closed. Deleting model" % corp)
corp.delete()
# Run Every 2 hours
EveManager.update_alliance(id)
EveManager.populate_alliance(id)
@periodic_task(run_every=crontab(minute=0, hour="*/2"))
@ -172,80 +117,36 @@ def run_corp_update():
logger.warn("Aborted updating corp and alliance models: API server unreachable")
return
standing_level = 'alliance'
alliance_id = settings.ALLIANCE_ID
# get corp info for owning corp if required
if settings.IS_CORP:
standing_level = 'corp'
if EveCorporationInfo.objects.filter(corporation_id=settings.CORP_ID).exists():
update_corp(settings.CORP_ID)
else:
EveManager.create_corporation(settings.CORP_ID)
alliance_id = eve_adapter_factory().get_corp(settings.CORP_ID).alliance_id
# get and create alliance info for owning alliance if required
if alliance_id:
if EveAllianceInfo.objects.filter(alliance_id=alliance_id).exists():
logger.debug("Updating existing owner alliance model with id %s" % alliance_id)
update_alliance(alliance_id)
else:
EveManager.create_alliance(id)
EveManager.populate_alliance(id)
# update existing corp models
for corp in EveCorporationInfo.objects.all():
update_corp.delay(corp.corporation_id)
# update existing alliance models
for alliance in EveAllianceInfo.objects.all():
update_alliance.delay(alliance.alliance_id)
try:
# get corp info for owning corp if required
ownercorpinfo = {}
if settings.IS_CORP:
standing_level = 'corp'
logger.debug("Getting information for owning corp with id %s" % settings.CORP_ID)
ownercorpinfo = EveApiManager.get_corporation_information(settings.CORP_ID)
if not ownercorpinfo:
logger.error("Failed to retrieve corp info for owning corp id %s - bad corp id?" % settings.CORP_ID)
return
# check if we need to update an alliance model
alliance_id = ''
if ownercorpinfo and ownercorpinfo['alliance']['id']:
alliance_id = ownercorpinfo['alliance']['id']
elif settings.IS_CORP is False:
alliance_id = settings.ALLIANCE_ID
# get and create alliance info for owning alliance if required
alliance = None
if alliance_id:
logger.debug("Getting information for owning alliance with id %s" % alliance_id)
ownerallianceinfo = EveApiManager.get_alliance_information(alliance_id)
if not ownerallianceinfo:
logger.error("Failed to retrieve corp info for owning alliance id %s - bad alliance id?" % alliance_id)
return
if EveAllianceInfo.objects.filter(alliance_id=ownerallianceinfo['id']).exists():
logger.debug("Updating existing owner alliance model with id %s" % alliance_id)
EveManager.update_alliance_info(ownerallianceinfo['id'], ownerallianceinfo['executor_id'],
ownerallianceinfo['member_count'], False)
else:
populate_alliance(alliance_id)
alliance = EveAllianceInfo.objects.get(alliance_id=alliance_id)
# create corp info for owning corp if required
if ownercorpinfo:
if EveCorporationInfo.objects.filter(corporation_id=ownercorpinfo['id']).exists():
logger.debug("Updating existing owner corp model with id %s" % ownercorpinfo['id'])
EveManager.update_corporation_info(ownercorpinfo['id'], ownercorpinfo['members']['current'], alliance,
False)
else:
logger.info("Creating model for owning corp with id %s" % ownercorpinfo['id'])
EveManager.create_corporation_info(ownercorpinfo['id'], ownercorpinfo['name'], ownercorpinfo['ticker'],
ownercorpinfo['members']['current'], False, alliance)
# validate and create corp models for member corps of owning alliance
if alliance:
current_corps = EveCorporationInfo.objects.filter(alliance=alliance)
for corp in current_corps:
if corp.corporation_id in ownerallianceinfo['member_corps'] is False:
logger.info("Corp %s is no longer in owning alliance %s - updating model." % (corp, alliance))
corp.alliance = None
corp.save()
for member_corp in ownerallianceinfo['member_corps']:
if EveCorporationInfo.objects.filter(corporation_id=member_corp).exists():
corp = EveCorporationInfo.objects.get(corporation_id=member_corp)
if corp.alliance == alliance is not True:
logger.info("Associating corp %s with owning alliance %s" % (corp, alliance))
corp.alliance = alliance
corp.save()
else:
corpinfo = EveApiManager.get_corporation_information(member_corp)
logger.info("Creating model for owning alliance member corp with id %s" % corpinfo['id'])
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
corpinfo['members']['current'], False, alliance)
# update existing corp models
for corp in EveCorporationInfo.objects.all():
update_corp.delay(corp.corporation_id)
# update existing alliance models
for alliance in EveAllianceInfo.objects.all():
update_alliance.delay(alliance.alliance_id)
# create standings
standings = EveApiManager.get_corp_standings()
if standings:
@ -262,7 +163,7 @@ def run_corp_update():
alliance.is_blue = True
alliance.save()
else:
populate_alliance(standing, blue=True)
EveManager.create_alliance(standing, blue=True)
elif EveApiManager.check_if_id_is_corp(standing):
logger.debug("Standing %s is a corp" % standing)
if EveCorporationInfo.objects.filter(corporation_id=standing).exists():
@ -273,13 +174,7 @@ def run_corp_update():
corp.save()
else:
logger.info("Creating model for blue corp with id %s" % standing)
corpinfo = EveApiManager.get_corporation_information(standing)
corp_alliance = None
if EveAllianceInfo.objects.filter(alliance_id=corpinfo['alliance']['id']).exists():
logger.debug("New corp model for standing %s has existing alliance model" % standing)
corp_alliance = EveAllianceInfo.objects.get(alliance_id=corpinfo['alliance']['id'])
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
corpinfo['members']['current'], True, corp_alliance)
EveManager.create_corporation(standing, blue=True)
# update alliance standings
for alliance in EveAllianceInfo.objects.filter(is_blue=True):
@ -310,43 +205,43 @@ def run_corp_update():
logger.info("Corp %s is no longer blue" % corp)
corp.is_blue = False
corp.save()
except evelink.api.APIError as e:
logger.error("Model update failed with error code %s" % e.code)
# delete unnecessary alliance models
for alliance in EveAllianceInfo.objects.filter(is_blue=False):
logger.debug("Checking to delete alliance %s" % alliance)
if not settings.IS_CORP:
if not alliance.alliance_id == settings.ALLIANCE_ID:
logger.info("Deleting unnecessary alliance model %s" % alliance)
alliance.delete()
# delete unnecessary alliance models
for alliance in EveAllianceInfo.objects.filter(is_blue=False):
logger.debug("Checking to delete alliance %s" % alliance)
if not settings.IS_CORP:
if not alliance.alliance_id == settings.ALLIANCE_ID:
logger.info("Deleting unnecessary alliance model %s" % alliance)
alliance.delete()
else:
if not alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists():
logger.info("Deleting unnecessary alliance model %s" % alliance)
alliance.delete()
# delete unnecessary corp models
for corp in EveCorporationInfo.objects.filter(is_blue=False):
logger.debug("Checking to delete corp %s" % corp)
if not settings.IS_CORP:
if corp.alliance:
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
if not corp.alliance.alliance_id == settings.ALLIANCE_ID:
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
if not alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists():
logger.info("Deleting unnecessary alliance model %s" % alliance)
alliance.delete()
# delete unnecessary corp models
for corp in EveCorporationInfo.objects.filter(is_blue=False):
logger.debug("Checking to delete corp %s" % corp)
if not settings.IS_CORP:
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
if corp.corporation_id != settings.CORP_ID:
logger.debug("Corp %s is not owning corp" % corp)
if corp.alliance:
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
if not corp.alliance.alliance_id == settings.ALLIANCE_ID:
if not corp.alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists():
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
if corp.corporation_id != settings.CORP_ID:
logger.debug("Corp %s is not owning corp" % corp)
if corp.alliance:
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
if not corp.alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists():
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
logger.debug("Corp %s is owning corp" % corp)
except evelink.api.APIError as e:
logger.error("Model update failed with error code %s" % e.code)
logger.debug("Corp %s is owning corp" % corp)

View File

@ -12,7 +12,7 @@ from authentication.models import AuthServicesInfo
from authentication.tasks import set_state
from eveonline.tasks import refresh_api
from eve_sso.decorators import token_required
from esi.decorators import token_required
from django.conf import settings
import logging
@ -46,9 +46,8 @@ def add_api_key(request):
api_key.save()
owner = request.user
# Grab characters associated with the key pair
characters = EveApiManager.get_characters_from_api(form.cleaned_data['api_id'],
form.cleaned_data['api_key'])
EveManager.create_characters_from_list(characters, owner, form.cleaned_data['api_id'])
characters = EveManager.get_characters_from_api(api_key)
[EveManager.create_character_obj(c, owner, api_key.api_id) for c in characters if not EveCharacter.objects.filter(character_id=c.id).exists()]
logger.info("Successfully processed api add form for user %s" % request.user)
if not settings.API_SSO_VALIDATION:
messages.success(request, 'Added API key %s to your account.' % form.cleaned_data['api_id'])
@ -70,7 +69,7 @@ def add_api_key(request):
@login_required
@token_required(new=True)
def api_sso_validate(request, tokens, api_id):
def api_sso_validate(request, token, api_id):
logger.debug('api_sso_validate called by user %s for api %s' % (request.user, api_id))
api = get_object_or_404(EveApiKeyPair, api_id=api_id)
if api.user and api.user != request.user:
@ -81,7 +80,6 @@ def api_sso_validate(request, tokens, api_id):
logger.debug('API %s has already been verified.' % api_id)
messages.info(request, 'API %s has already been verified' % api_id)
return redirect('auth_api_key_management')
token = tokens[0]
logger.debug('API %s has not been verified. Checking if token for %s matches.' % (api_id, token.character_name))
characters = EveApiManager.get_characters_from_api(api.api_id, api.api_key).result
if token.character_id in characters:

View File

@ -0,0 +1,25 @@
{% extends 'public/base.html' %}
{% block title %}Fleet Participation{% endblock %}
{% block page_title %}Fleet Participation{% endblock %}
<div class="col-lg-12">
<h1 class="page-header text-center">Character not found!</h1>
<div class="col-lg-12 container" id="example">
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">{{ character_name }}</div>
<div class="panel-body">
<div class="col-lg-2 col-sm-2">
<img class="ra-avatar img-responsive" src="https://image.eveonline.com/Character/{{ character_id }}_128.jpg">
</div>
<div class="col-lg-10 col-sm-2">
<div class="alert alert-danger" role="alert">Character not registered!</div>
This character is not part of any registered API-key. You must go to <a href=" {% url 'auth_api_key_management' %}">API key management</a> and add an API with the character on before being able to click fleet attendance links.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,63 @@
{% extends "public/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% load bootstrap_pagination %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}{% trans "Fatlink view" %}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% blocktrans %}Edit fatlink "{{ fatlink }}"{% endblocktrans %}
<div class="text-right">
<form>
<button type="submit" onclick="return confirm('Are you sure?')" class="btn btn-danger" name="deletefat" value="True">
{% trans "Delete fat" %}
</button>
</form>
</div>
</h1>
<div class="panel panel-default">
<div class="panel-heading">{% trans "Registered characters" %}</div>
<div class="panel-body">
<div class="text-center">
{% bootstrap_paginate registered_fats range=10 %}
</div>
<table class="table table-responsive table-hover">
<tr>
<th class="text-center">{% trans "User" %}</th>
<th class="text-center">{% trans "Character" %}</th>
<th class="text-center">{% trans "System" %}</th>
<th class="text-center">{% trans "Ship" %}</th>
<th class="text-center">{% trans "Eve Time" %}</th>
<th></th>
</tr>
{% for fat in registered_fats %}
<tr>
<td class="text-center">{{ fat.user }}</td>
<td class="text-center">{{ fat.character.character_name }}</td>
{% if fat.station != "No Station" %}
<td class="text-center">Docked in {{ fat.system }}</td>
{% else %}
<td class="text-center">{{ fat.system }}</td>
{% endif %}
<td class="text-center">{{ fat.shiptype }}</td>
<td class="text-center">{{ fat.fatlink.fatdatetime }}</td>
<td class="text-center">
<form>
<button type="submit" class="btn btn-warning" name="removechar" value="{{ fat.character.character_id }}">
<span class="glyphicon glyphicon-remove"></span>
</button>
</form>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
<script src="/static/js/dateformat.js"></script>
{% endblock content %}

View File

@ -6,16 +6,17 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required
from django.core.exceptions import ValidationError
from django.utils import timezone
from django.contrib import messages
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from eveonline.models import EveCharacter
from eveonline.models import EveCorporationInfo
from eveonline.managers import EveManager
from fleetactivitytracking.forms import FatlinkForm
from fleetactivitytracking.models import Fatlink, Fat
from slugify import slugify
from esi.decorators import token_required
from collections import OrderedDict
from slugify import slugify
import string
import random
@ -25,15 +26,27 @@ import logging
logger = logging.getLogger(__name__)
FATS_PER_PAGE = int(getattr(settings, 'FATS_PER_PAGE', 20))
def get_page(model_list, page_num):
p = Paginator(model_list, FATS_PER_PAGE)
try:
fats = p.page(page_num)
except PageNotAnInteger:
fatss = p.page(1)
except EmptyPage:
fatss = p.page(p.num_pages)
return fats
class CorpStat(object):
def __init__(self, corp_id, corp=None, blue=False):
def __init__(self, corp_id, start_of_month, start_of_next_month, corp=None):
if corp:
self.corp = corp
else:
self.corp = EveCorporationInfo.objects.get(corporation_id=corp_id)
self.n_fats = 0
self.blue = blue
self.n_fats = Fat.objects.filter(character__corporation_id=self.corp.corporation_id).filter(fatlink__fatdatetime__gte=start_of_month).filter(fatlink__fatdatetime__lte=start_of_next_month).count()
self.blue = self.corp.is_blue
def avg_fat(self):
return "%.2f" % (float(self.n_fats) / float(self.corp.member_count))
@ -69,7 +82,7 @@ def fatlink_view(request):
else:
context = {'user': user, 'fats': latest_fats}
return render(request, 'registered/fatlinkview.html', context=context)
return render(request, 'fleetactivitytracking/fatlinkview.html', context=context)
@login_required
@ -81,37 +94,38 @@ def fatlink_statistics_view(request, year=datetime.date.today().year, month=date
start_of_next_month = first_day_of_next_month(year, month)
start_of_previous_month = first_day_of_previous_month(year, month)
fatStats = OrderedDict()
fat_stats = {}
# get FAT stats for member corps
if settings.IS_CORP:
fatStats[settings.CORP_NAME] = CorpStat(settings.CORP_ID)
fat_stats[settings.CORP_ID] = CorpStat(settings.CORP_ID, start_of_month, start_of_next_month)
else:
alliance_corps = EveCorporationInfo.objects.filter(alliance__alliance_id=settings.ALLIANCE_ID)
for corp in alliance_corps:
fatStats[corp.corporation_name] = CorpStat(corp.corporation_id, corp=corp)
fat_stats[corp.corporation_id] = CorpStat(corp.corporation_id, start_of_month, start_of_next_month)
fatlinks_in_span = Fatlink.objects.filter(fatdatetime__gte=start_of_month).filter(
fatdatetime__lt=start_of_next_month)
# get FAT stats for corps not in alliance
fats_in_span = Fat.objects.filter(fatlink__fatdatetime__gte=start_of_month).filter(
fatlink__fatdatetime__lt=start_of_next_month).exclude(character__corporation_id__in=fat_stats)
for fatlink in fatlinks_in_span:
fats_in_fatlink = Fat.objects.filter(fatlink=fatlink)
for fat in fats_in_fatlink:
fatStats.setdefault(fat.character.corporation_name,
CorpStat(fat.character.corporation_id, blue=True)
).n_fats += 1
for fat in fats_in_span:
if not fat.character.corporation_id in fat_stats:
fat_stats[fat.character.corporation_id] = CorpStat(fat.character.corporation_id, start_of_month, start_of_next_month)
fatStatsList = [fatStat for corp_name, fatStat in fatStats.items()]
fatStatsList.sort(key=lambda stat: stat.corp.corporation_name)
fatStatsList.sort(key=lambda stat: (stat.n_fats, stat.n_fats / stat.corp.member_count), reverse=True)
# collect and sort stats
stat_list = [fat_stats[x] for x in fat_stats]
stat_list.sort(key=lambda stat: stat.corp.corporation_name)
stat_list.sort(key=lambda stat: (stat.n_fats, stat.n_fats / stat.corp.member_count), reverse=True)
if datetime.datetime.now() > start_of_next_month:
context = {'fatStats': fatStatsList, 'month': start_of_month.strftime("%B"), 'year': year,
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
'previous_month': start_of_previous_month, 'next_month': start_of_next_month}
else:
context = {'fatStats': fatStatsList, 'month': start_of_month.strftime("%B"), 'year': year,
context = {'fatStats': stat_list, 'month': start_of_month.strftime("%B"), 'year': year,
'previous_month': start_of_previous_month}
return render(request, 'registered/fatlinkstatisticsview.html', context=context)
return render(request, 'fleetactivitytracking/fatlinkstatisticsview.html', context=context)
@login_required
@ -139,7 +153,7 @@ def fatlink_personal_statistics_view(request, year=datetime.date.today().year, m
else:
context = {'user': user, 'monthlystats': monthlystats, 'year': year, 'previous_year': year - 1}
return render(request, 'registered/fatlinkpersonalstatisticsview.html', context=context)
return render(request, 'fleetactivitytracking/fatlinkpersonalstatisticsview.html', context=context)
@login_required
@ -173,55 +187,58 @@ def fatlink_monthly_personal_statistics_view(request, year, month, char_id=None)
context["created_fats"] = created_fats
context["n_created_fats"] = len(created_fats)
return render(request, 'registered/fatlinkpersonalmonthlystatisticsview.html', context=context)
return render(request, 'fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html', context=context)
@login_required
def click_fatlink_view(request, hash, fatname):
# Take IG-header data and register the fatlink if not existing already.
# use obj, created = Fat.objects.get_or_create()
# onload="CCPEVE.requestTrust('http://www.mywebsite.com')"
@token_required(scopes=['esi-location.read_location.v1', 'esi-location.read_ship_type.v1', 'esi-universe.read_structures.v1'])
def click_fatlink_view(request, token, hash, fatname):
try:
fatlink = Fatlink.objects.filter(hash=hash)[0]
if 'HTTP_EVE_TRUSTED' in request.META and request.META['HTTP_EVE_TRUSTED'] == "Yes":
# Retrieve the latest fatlink using the hash.
try:
fatlink = Fatlink.objects.filter(hash=hash)[0]
if (timezone.now() - fatlink.fatdatetime) < datetime.timedelta(seconds=(fatlink.duration * 60)):
if (timezone.now() - fatlink.fatdatetime) < datetime.timedelta(seconds=(fatlink.duration * 60)):
character = EveManager.get_character_by_id(token.character_id)
character = EveManager.get_character_by_id(request.META['HTTP_EVE_CHARID'])
if character:
fat = Fat()
fat.system = request.META['HTTP_EVE_SOLARSYSTEMNAME']
if 'HTTP_EVE_STATIONNAME' in request.META:
fat.station = request.META['HTTP_EVE_STATIONNAME']
else:
fat.station = "No Station"
fat.shiptype = request.META['HTTP_EVE_SHIPTYPENAME']
fat.fatlink = fatlink
fat.character = character
fat.user = character.user
try:
fat.full_clean()
fat.save()
context = {'trusted': True, 'registered': True}
except ValidationError as e:
messages = []
for errorname, message in e.message_dict.items():
messages.append(message[0].decode())
context = {'trusted': True, 'errormessages': messages}
if character:
# get data
c = token.get_esi_client()
location = c.Location.get_characters_character_id_location(character_id=token.character_id).result()
ship = c.Location.get_characters_character_id_ship(character_id=token.character_id).result()
location['solar_system_name'] = c.Universe.get_universe_systems_system_id(system_id=location['solar_system_id']).result()['solar_system_name']
if location['structure_id']:
location['station_name'] = c.Universe.get_universe_structures_structure_id(structure_id=location['structure_id']).result()['name']
elif location['station_id']:
location['station_name'] = c.Universe.get_universe_stations_station_id(station_id=location['station_id']).result()['station_name']
else:
context = {'character_id': request.META['HTTP_EVE_CHARID'],
'character_name': request.META['HTTP_EVE_CHARNAME']}
return render(request, 'public/characternotexisting.html', context=context)
location['station_name'] = "No Station"
ship['ship_type_name'] = c.Universe.get_universe_types_type_id(type_id=ship['ship_type_id']).result()['type_name']
fat = Fat()
fat.system = location['solar_system_name']
fat.station = location['station_name']
fat.shiptype = ship['ship_type_name']
fat.fatlink = fatlink
fat.character = character
fat.user = character.user
try:
fat.full_clean()
fat.save()
messages.success(request, 'Fleet participation registered.')
except ValidationError as e:
err_messages = []
for errorname, message in e.message_dict.items():
err_messages.append(message[0].decode())
messages.error(request, ' '.join(err_messages))
else:
context = {'trusted': True, 'expired': True}
except ObjectDoesNotExist:
context = {'trusted': True}
else:
context = {'trusted': False, 'fatname': fatname}
return render(request, 'public/clickfatlinkview.html', context=context)
context = {'character_id': token.character_id,
'character_name': token.character_name}
return render(request, 'fleetactivitytracking/characternotexisting.html', context=context)
else:
messages.error(request, 'FAT link has expired.')
except (ObjectDoesNotExist, KeyError):
messages.error(request, 'Invalid FAT link.')
return redirect('auth_fatlink_view')
@login_required
@ -250,7 +267,7 @@ def create_fatlink_view(request):
for errorname, message in e.message_dict.items():
messages.append(message[0].decode())
context = {'form': form, 'errormessages': messages}
return render(request, 'registered/fatlinkformatter.html', context=context)
return render(request, 'fleetactivitytracking/fatlinkformatter.html', context=context)
else:
form = FatlinkForm()
context = {'form': form, 'badrequest': True}
@ -263,7 +280,7 @@ def create_fatlink_view(request):
context = {'form': form}
return render(request, 'registered/fatlinkformatter.html', context=context)
return render(request, 'fleetactivitytracking/fatlinkformatter.html', context=context)
@login_required
@ -271,24 +288,26 @@ def create_fatlink_view(request):
def modify_fatlink_view(request, hash=""):
logger.debug("modify_fatlink_view called by user %s" % request.user)
if not hash:
return redirect('/fat/')
return redirect('auth_fatlink_view')
fatlink = Fatlink.objects.filter(hash=hash)[0]
if request.GET.get('removechar'):
if request.GET.get('removechar', None):
character_id = request.GET.get('removechar')
character = EveCharacter.objects.get(character_id=character_id)
logger.debug("Removing character %s from fleetactivitytracking %s" % (character.character_name, fatlink.name))
Fat.objects.filter(fatlink=fatlink).filter(character=character).delete()
if request.GET.get('deletefat'):
if request.GET.get('deletefat', None):
logger.debug("Removing fleetactivitytracking %s" % fatlink.name)
fatlink.delete()
return redirect('/fat/')
return redirect('auth_fatlink_view')
registered_fats = Fat.objects.filter(fatlink=fatlink).order_by('character')
registered_fats = Fat.objects.filter(fatlink=fatlink).order_by('character__character_name')
context = {'fatlink': fatlink, 'registered_fats': registered_fats}
fat_page = get_page(registered_fats, request.GET.get('page', 1))
return render(request, 'registered/fatlinkmodify.html', context=context)
context = {'fatlink': fatlink, 'registered_fats': fat_page}
return render(request, 'fleetactivitytracking/fatlinkmodify.html', context=context)

View File

@ -15,6 +15,7 @@ redis
django>=1.10,<2.0
django-bootstrap-form
django-navhelper
django-bootstrap-pagination
# awating release for fix to celery/django-celery#447
# django-celery
@ -23,4 +24,4 @@ git+https://github.com/celery/django-celery
git+git://github.com/nikdoof/python-ts3.git
git+https://github.com/pyghassen/openfire-restapi
git+https://github.com/adarnof/adarnauth-eve-sso
git+https://github.com/adarnof/adarnauth-esi

View File

@ -319,7 +319,6 @@ class EveApiManager:
def validate_api(api_id, api_key, user):
try:
info = EveApiManager.get_api_info(api_id, api_key).result
chars = EveApiManager.get_characters_from_api(api_id, api_key).result
except evelink.api.APIError as e:
if int(e.code) == 222:
raise EveApiManager.ApiInvalidError(api_id)
@ -329,6 +328,7 @@ class EveApiManager:
auth, c = AuthServicesInfo.objects.get_or_create(user=user)
states = [auth.state]
from authentication.tasks import determine_membership_by_character # circular import issue
chars = info['characters']
for char in chars:
evechar = EveCharacter()
evechar.character_name = chars[char]['name']

View File

@ -1,25 +0,0 @@
from __future__ import unicode_literals
import requests
import json
class EveWhoManager:
def __init__(self):
pass
@staticmethod
def get_corporation_members(corpid):
url = "http://evewho.com/api.php?type=corplist&id=%s" % corpid
jsondata = requests.get(url).content
data = json.loads(jsondata.decode())
members = {}
page_count = 0
while len(data["characters"]):
for row in data["characters"]:
members[int(row["character_id"])] = {"name": row["name"], "id": int(row["character_id"])}
page_count += 1
jsondata = requests.get(url + "&page=%i" % page_count).content
data = json.loads(jsondata.decode())
return members

View File

@ -12,7 +12,7 @@
<meta name="description" content="">
<meta name="author" content="">
<title>{% block title %}Empty title{% endblock title %}</title>
<title>{% block title %}Alliance Auth{% endblock title %}</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
@ -153,9 +153,9 @@
</li>
{% endif %}
{% if perms.auth.corp_apis or perms.auth.alliance_apis %}
{% if perms.corputils.view_corp_corpstats or perms.corputils.view_alliance_corpstats or perms.corputils.view_blue_corpstats %}
<li>
<a class="{% navactive request 'auth_corputils auth_corputils_search auth_corputils_corp_view auth_corputils_month' %}" href="{% url 'auth_corputils' %}">
<a class="{% navactive request 'corputils:view corputils:search' %}" href="{% url 'corputils:view' %}">
<i class="fa fa-share-alt fa-fw grayiconecolor"></i>{% trans " Corporation Stats" %}
</a>
</li>

View File

@ -1,90 +0,0 @@
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Fleet participation</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom Fonts -->
<link href="{% static 'css/font-awesome.min.css' %}" rel="stylesheet" type="text/css">
<link href="{% static 'css/sb-admin-2.css' %}" rel="stylesheet">
{% block extra_css %}{% endblock extra_css %}
<style>
.grayiconecolor {
color: #505050;
}
</style>
</head>
<body onload=CCPEVE.requestTrust('{{ DOMAIN }}')>
<div id="wrapper">
<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="navbar-header ">
<a class="navbar-brand " href="/dashboard/">
<div class="fa fa-cog fa-spin"></div>
{% if IS_CORP %}
{{ CORP_NAME }}
{% else %}
{{ ALLIANCE_NAME }}
{% endif %}
</a>
</div>
<!-- /.navbar-header -->
<ul class="nav navbar-top-links navbar-right">
{% if user.is_authenticated %}
<li><a href="{% url 'auth_logout_user' %}">Logout</a></li>
{% else %}
<li><a href="{% url 'auth_login_user' %}">Login</a></li>
{% endif %}
</ul>
<!-- /.navbar-static-side -->
</nav>
</div>
<div class="col-lg-12">
<h1 class="page-header text-center">Character not found!</h1>
<div class="col-lg-12 container" id="example">
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">{{ character_name }}</div>
<div class="panel-body">
<div class="col-lg-2 col-sm-2">
<img class="ra-avatar img-responsive" src="https://image.eveonline.com/Character/{{ character_id }}_128.jpg">
</div>
<div class="col-lg-10 col-sm-2">
<div class="alert alert-danger" role="alert">Character not registered!</div>
This character is not part of any registered API-key. You must go to <a href=" {% url 'auth_api_key_management' %}">API key management</a> and add an API with the character on before being able to click fleet attendance links.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/jquery.datetimepicker.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
</body>
</html>

View File

@ -1,96 +0,0 @@
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Fleet participation</title>
<!-- Bootstrap Core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom Fonts -->
<link href="{% static 'css/font-awesome.min.css' %}" rel="stylesheet" type="text/css">
<link href="{% static 'css/sb-admin-2.css' %}" rel="stylesheet">
{% block extra_css %}{% endblock extra_css %}
<style>
.grayiconecolor {
color: #505050;
}
</style>
</head>
<body onload=CCPEVE.requestTrust('{{ DOMAIN }}')>
<div id="wrapper">
<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="navbar-header ">
<a class="navbar-brand " href="/dashboard/">
<div class="fa fa-cog fa-spin"></div>
{% if IS_CORP %}
{{ CORP_NAME }}
{% else %}
{{ ALLIANCE_NAME }}
{% endif %}
</a>
</div>
<!-- /.navbar-header -->
<ul class="nav navbar-top-links navbar-right">
{% if user.is_authenticated %}
<li><a href="{% url 'auth_logout_user' %}">Logout</a></li>
{% else %}
<li><a href="{% url 'auth_login_user' %}">Login</a></li>
{% endif %}
</ul>
<!-- /.navbar-static-side -->
</nav>
</div>
<div class="col-lg-12">
{% if registered %}<h1 class="page-header text-center">Fleet registered!</h1> {% elif expired%}<h1 class="page-header text-center">This link has expired.</h1> {% elif errormessages%}<h1 class="page-header text-center">Something horrible happened. Shoot your FC!</h1>{% else %}<h1 class="page-header text-center">Invalid link.</h1>{% endif %}
<div class="col-lg-12 container" id="example">
{% for message in errormessages %}
<div class="alert alert-danger" role="alert">{{ message }}</div>
{% endfor %}
{% if trusted %}
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">Fleet stats</div>
<div class="panel-body">
<div class="col-lg-2 col-sm-2">
<img class="ra-avatar img-responsive" src="https://image.eveonline.com/{% if IS_CORP %}Corporation/{{ CORPORATION_ID }}{% else %}Alliance/{{ ALLIANCE_ID }}{% endif %}_128.png">
</div>
<div class="col-lg-7 col-sm-2">
</div>
</div>
</div>
</div>
</div>
{% else %}
<div class="alert alert-danger" role="alert">This page requires trust to operate.</div>
{% endif %}
</div>
</div>
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/jquery.datetimepicker.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
</body>
</html>

View File

@ -1,229 +0,0 @@
{% extends "public/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}{% trans "Corporation Member Tracking" %}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Corporation Member Data" %}</h1>
<div class="col-lg-12 container" id="example">
{% if corp %}
<div class="row">
<div class="col-lg-12">
<div class="panel panel-default">
<div class="panel-heading">{% trans "Corporation" %}</div>
<div class="panel-body">
<div class="col-lg-5 col-sm-2">
<img class="ra-avatar img-responsive" src="https://image.eveonline.com/Corporation/{{ corp.corporation_id }}_128.png">
</div>
<div class="col-lg-7 col-sm-2">
<h4 class="">{{ corp.corporation_name }}</h4>
<p>{% trans "Ticker:" %} {{ corp.corporation_ticker }}</p>
<p>{% trans "Member count:" %} {{ corp.member_count }}</p>
<p>{% trans "Player count:" %} {{characters_with_api|length}}</p>
<p>{% trans "Unregistered characters:" %} {{characters_without_api|length|add:n_unacounted}}</p>
</div>
<div class="col-lg-12 col-sm-5">
<b>{% trans "API Index:" %}</b>
<div class="progress">
<div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="{{ n_registered }}" aria-valuemin="0" aria-valuemax="{{ corp.member_count }}" style="width: {% widthratio characters_with_api|length corp.member_count 100 %}%;">
{{n_registered}}/{{ corp.member_count }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav navbar-wide">
{% if membercorplist and perms.auth.alliance_apis %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{% trans "Alliance corporations" %} <span class="caret"></span></a>
<ul class="dropdown-menu scrollable">
{% for membercorpid, membercorpname in membercorplist %}
<li>
<a href="{% url 'auth_corputils_corp_view' membercorpid %}">{{ membercorpname }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endif %}
<li style="float: right">
<p class="navbar-form">
Statistics for:
<a href="{% url 'auth_corputils_month' corp.corporation_id previous_month|date:"Y" previous_month|date:"m" %}">
<i class="fa fa-arrow-circle-left fa-fw grayiconecolor"></i>
</a>
{{ this_month|date:"M" }}, {{ this_month|date:"Y" }}
{% if next_month %}
<a href="{% url 'auth_corputils_month' corp.corporation_id next_month|date:"Y" next_month|date:"m" %}" >
<i class="fa fa-arrow-circle-right fa-fw grayiconecolor"></i>
</a>
<a href="{% url 'auth_corputils' %}" >
<i class="fa fa-angle-double-right fa-fw grayiconecolor"></i>
</a>
{% endif %}
</p>
</li>
<li style="float: right">
<p class="navbar-btn">
<a href="https://zkillboard.com/corporation/{{ corp.corporation_id }}/" class="btn btn-default" target="_blank">{{ corp.corporation_name }} {% trans "Killboard" %}</a>
</p>
</li>
<li style="float: right">
<form class="navbar-form navbar-left" role="search" action={% url 'auth_corputils_search' %}{{ corp.corporation_id }}/ method="POST">
<div class="form-group">
{% csrf_token %}
{{ search_form.as_table }}
</div>
<button class="btn btn-default" type="submit">{% trans "Search" %}</button>
</form>
</li>
</ul>
</div>
</nav>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#gotapi">{% blocktrans %}Registered Main Characters{% endblocktrans %} <b>({{characters_with_api|length}})</b></a></li>
<li><a data-toggle="tab" href="#noapi">{% blocktrans %}Characters without API{% endblocktrans %} <b>({{characters_without_api|length|add:n_unacounted}})</b></a></li>
</ul>
<div class="tab-content">
<div id="gotapi" class="tab-pane fade in active">
{% if characters_with_api %}
<div class="panel-body">
<div class="table-responsive">
<table class="table table-condensed table-hover table-striped">
<tr>
<th class="col-md-1"></th>
<th class="col-md-2">{% trans "Main character" %}</th>
<th class="col-md-2">{% trans "Main corporation" %}</th>
<th class="col-md-2">{% trans "Character list" %}</th>
<th class="col-md-1">{% trans "Fats" %}</th>
{% if perms.auth.fleetactivitytracking_statistics %}
<th class="col-md-3">{% trans "Killboard" %}</th>
<th class="col-md-2">{% trans "Fleet statistics" %}</th>
{% else %}
<th class="col-md-3">{% trans "Killboard" %}</th>
{% endif %}
<th class="col-md-2">{% trans "API JackKnife" %}</th>
</tr>
{% for maincharname, player in characters_with_api %}
<tr >
<td>
<img src="http://image.eveonline.com/Character/{{ player.main.character_id }}_32.jpg" class="img-circle">
</td>
<td>
<p>{{ maincharname }}</p>
</td>
<td>
{% if not corp.corporation_name == player.maincorp%}
<span class="label label-danger">
{{ player.maincorp }}
</span>
{% else %}
<span class="label label-success">
{{ player.maincorp }}
</span>
{% endif %}
</td>
<td>
{% for char in player.altlist %}
<p>{{ char.character_name }}</p>
{% endfor %}
</td>
<td>
{{ player.n_fats }}
</td>
<td>
{% for char in player.altlist %}
<p><a href="https://zkillboard.com/character/{{ char.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a></p>
{% endfor %}
</td>
{% if perms.auth.fleetactivitytracking %}
<td>
<a href="{% url 'auth_fatlink_view_user_statistics_month' player.main.character_id this_month|date:"Y" this_month|date:"m" %}">
<button type="button" class="btn btn-primary">Statistics
</button>
</a>
</td>
{% endif %}
<td>
{% for apiinfo in player.apilist %}
<p>
<a href="{{ JACK_KNIFE_URL }}?usid={{ apiinfo.api_id }}&apik={{ apiinfo.api_key }}" target="_blank" class="btn btn-primary">
{% trans "API JackKnife" %}
</a>
</p>
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% else %}
<div class="alert alert-danger" role="alert">
<h3>{% blocktrans %}Seems there are no characters in {{ corp.corporation_name }} tied to a registered API!{% endblocktrans %}</h3>
</div>
{% endif %}
</div>
<div id="noapi" class="tab-pane fade">
{% if characters_without_api %}
<div class="panel-body">
<div class="table-responsive">
{% if 0 < n_unacounted %}
<div class="alert alert-danger" role="alert">
<h3>{% blocktrans %}There are atleast {{ n_unacounted }} characters not accounted for in EveWho.{% endblocktrans %}</h3>
</div>
{% endif %}
<table class="table table-condensed table-hover table-striped">
<tr>
<th class="col-md-1"></th>
<th class="col-md-2">{% trans "Character" %}</th>
<th class="col-md-5">{% trans "Killboard" %}</th>
</tr>
{% for character_name, character_id in characters_without_api %}
<tr>
<td>
<img src="http://image.eveonline.com/Character/{{ character_id }}_32.jpg" class="img-circle">
</td>
<td>
<p>{{ character_name }}</p>
</td>
<td>
<a href="https://zkillboard.com/character/{{ character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% else %}
<div class="alert alert-success" role="alert">
<h3>{% blocktrans %}Good job! Every character in {{ corp.corporation_name }} seem to be tied to an API!{% endblocktrans %}</h3>
</div>
{% endif %}
</div>
</div>
{% else %}
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
<div class="alert alert-danger text-center" role="alert">{% trans "No corporation model found. Contact your admin." %}</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock content %}

View File

@ -1,93 +0,0 @@
{% extends "public/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}{% trans "Corporation Member Tracking" %}{% endblock page_title %}
{% block extra_css %}{% endblock extra_css %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Member Search Results" %}</h1>
<h2 class="text-center"><a href="{% url 'auth_corputils_corp_view' corp.corporation_id %}">{{ corp.corporation_name }}</a></h2>
<div class="container-fluid">
<div class="panel panel-default">
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav navbar-wide">
<li style="float: right">
<form class="navbar-form navbar-right" role="search" action="{% url 'auth_corputils_search_corp' corp.corporation_id %}" method="POST">
<div class="form-group">
{% csrf_token %}
{{ search_form|bootstrap }}
</div>
<button class="btn btn-default" type="submit">{% trans "Search" %}</button>
</form>
</li>
</ul>
</div>
</nav>
<div class="panel-body">
<table class="table table-condensed table-hover table-striped">
<tr>
<th class="col-md-1"></th>
<th class="col-md-2">{% trans "Character" %}</th>
<th class="col-md-2">{% trans "Main character" %}</th>
{% if perms.auth.fleetactivitytracking%}
<th class="col-md-5">{% trans "Killboard" %}</th>
<th class="col-md-2">{% trans "Fleet statistics" %}</th>
{% else %}
<th class="col-md-5">{% trans "Killboard" %}</th>
{% endif %}
<th class="col-md-2">{% trans "API JackKnife" %}</th>
</tr>
{% for result in results %}
<tr >
<td>
<img src="http://image.eveonline.com/Character/{{ result.id }}_32.jpg" class="img-circle">
</td>
<td>{{ result.name }}</td>
<td>
{% if result.api_registered%}
{{ result.main.character_name }}
{% else %}
<span class="label label-danger">{% trans "No API registered!" %}</span>
{% endif %}
</td>
<td>
<p><a href="https://zkillboard.com/character/{{ result.char.character_id }}/" class="label label-danger" target="_blank">{% trans "Killboard" %}</a></p>
</td>
{% if perms.auth.fleetactivitytracking %}
{% if result.main %}
<td>
<a href="{% url 'auth_fatlink_view_user_statistics_month' result.main.character_id year month%}">
<button type="button" class="btn btn-primary">Statistics
</button>
</a>
</td>
{% else %}
<td></td>
{% endif %}
{% endif %}
<td>
{% if result.api_registered %}
<a href="{{ JACK_KNIFE_URL }}?usid={{ result.apiinfo.api_id }}&apik={{ result.apiinfo.api_key }}"
target="_blank">
<button type="button" class="btn btn-primary">{% trans "API JackKnife" %}
</button>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
{% endblock content %}

View File

@ -1,55 +0,0 @@
{% extends "public/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}{% trans "Fatlink view" %}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% blocktrans %}Edit fatlink "{{ fatlink.name }}"{% endblocktrans %}
<div class="text-right">
<form>
<button type="submit" onclick="return confirm('Are you sure?')" class="btn btn-danger" name="deletefat" value="True">
{% trans "Delete fat" %}
</button>
</form>
</div>
</h1>
<h4><b>{% trans "Registered characters" %}</b></h4>
<table class="table table-responsive table-bordered">
<tr>
<th class="text-center">{% trans "User" %}</th>
<th class="text-center">{% trans "Character" %}</th>
<th class="text-center">{% trans "System" %}</th>
<th class="text-center">{% trans "Ship" %}</th>
<th class="text-center">{% trans "Eve Time" %}</th>
<th></th>
</tr>
{% for fat in registered_fats %}
<tr>
<td class="text-center">{{ fat.user }}</td>
<td class="text-center">{{ fat.character.character_name }}</td>
{% if fat.station != "No Station" %}
<td class="text-center">Docked in {{ fat.system }}</td>
{% else %}
<td class="text-center">{{ fat.system }}</td>
{% endif %}
<td class="text-center">{{ fat.shiptype }}</td>
<td class="text-center">{{ fat.fatlink.fatdatetime }}</td>
<td class="text-center">
<form>
<button type="submit" class="btn btn-warning" name="removechar" value="{{ fat.character.character_id }}"><span
class="glyphicon glyphicon-remove"></span></button>
</form>
</td>
</tr>
{% endfor %}
</table>
</div>
<script src="/static/js/dateformat.js"></script>
{% endblock content %}