mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-12 22:10:16 +02:00
Pull corp memebrship data from ESI
This commit is contained in:
parent
32009fd3ff
commit
27fc8a373f
@ -63,6 +63,7 @@ INSTALLED_APPS = [
|
|||||||
'notifications',
|
'notifications',
|
||||||
'esi',
|
'esi',
|
||||||
'geelweb.django.navhelper',
|
'geelweb.django.navhelper',
|
||||||
|
'bootstrap_pagination',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -10,12 +10,12 @@ import services.views
|
|||||||
import groupmanagement.views
|
import groupmanagement.views
|
||||||
import optimer.views
|
import optimer.views
|
||||||
import timerboard.views
|
import timerboard.views
|
||||||
import corputils.views
|
|
||||||
import fleetactivitytracking.views
|
import fleetactivitytracking.views
|
||||||
import fleetup.views
|
import fleetup.views
|
||||||
import srp.views
|
import srp.views
|
||||||
import notifications.views
|
import notifications.views
|
||||||
import hrapplications.views
|
import hrapplications.views
|
||||||
|
import corputils.urls
|
||||||
import esi.urls
|
import esi.urls
|
||||||
|
|
||||||
# Functional/Untranslated URL's
|
# Functional/Untranslated URL's
|
||||||
@ -27,8 +27,11 @@ urlpatterns = [
|
|||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
|
|
||||||
# SSO
|
# SSO
|
||||||
url (r'^sso/', include(esi.urls, namespace='esi')),
|
url(r'^sso/', include(esi.urls, namespace='esi')),
|
||||||
url (r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'),
|
url(r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'),
|
||||||
|
|
||||||
|
# Corputils
|
||||||
|
url(r'^corpstats/', include(corputils.urls, namespace='corputils')),
|
||||||
|
|
||||||
# Index
|
# Index
|
||||||
url(_(r'^$'), authentication.views.index_view, name='auth_index'),
|
url(_(r'^$'), authentication.views.index_view, name='auth_index'),
|
||||||
@ -144,14 +147,6 @@ urlpatterns = [
|
|||||||
# User viewed/translated URLS
|
# User viewed/translated URLS
|
||||||
urlpatterns += i18n_patterns(
|
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
|
# Fleetup
|
||||||
url(r'^fleetup/$', fleetup.views.fleetup_view, name='auth_fleetup_view'),
|
url(r'^fleetup/$', fleetup.views.fleetup_view, name='auth_fleetup_view'),
|
||||||
url(r'^fleetup/fittings/$', fleetup.views.fleetup_fittings, name='auth_fleetup_fittings'),
|
url(r'^fleetup/fittings/$', fleetup.views.fleetup_fittings, name='auth_fleetup_fittings'),
|
||||||
@ -245,12 +240,6 @@ urlpatterns += i18n_patterns(
|
|||||||
# Teamspeak Urls
|
# Teamspeak Urls
|
||||||
url(r'verify_teamspeak3/$', services.views.verify_teamspeak3, name='auth_verify_teamspeak3'),
|
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
|
# Timer URLS
|
||||||
url(_(r'^timers/$'), timerboard.views.timer_view, name='auth_timer_view'),
|
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'),
|
url(_(r'^add_timer/$'), timerboard.views.add_timer_view, name='auth_add_timer_view'),
|
||||||
|
@ -1 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
from corputils.models import CorpStats
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
admin.site.register(CorpStats)
|
||||||
|
43
corputils/managers.py
Normal file
43
corputils/managers.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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('corpstats.corp_apis'):
|
||||||
|
queries.append(models.Q(corp__corporation_id=char.corporation_id))
|
||||||
|
if user.has_perm('corpstats.alliance_apis'):
|
||||||
|
queries.append(models.Q(corp__alliance_id=char.alliance_id))
|
||||||
|
if user.has_perm('corpstats.blue_apis'):
|
||||||
|
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)
|
33
corputils/migrations/0001_initial.py
Normal file
33
corputils/migrations/0001_initial.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.1 on 2016-12-13 06:23
|
||||||
|
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', 'delete', 'view'),
|
||||||
|
'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.')),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
19
corputils/migrations/0002_auto_20161213_0637.py
Normal file
19
corputils/migrations/0002_auto_20161213_0637.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.1 on 2016-12-13 06:37
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('corputils', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='corpstats',
|
||||||
|
options={'default_permissions': ('add', 'delete', 'view'), '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.')), 'verbose_name': 'Corp stats'},
|
||||||
|
),
|
||||||
|
]
|
0
corputils/migrations/__init__.py
Normal file
0
corputils/migrations/__init__.py
Normal file
@ -1 +1,164 @@
|
|||||||
from __future__ import unicode_literals
|
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',
|
||||||
|
'delete',
|
||||||
|
'view',
|
||||||
|
)
|
||||||
|
verbose_name = "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(id=auth.main_char_id)
|
||||||
|
if char.corporation_id == self.corp.corporation_id and user.has_perm('corputils.corp_apis'):
|
||||||
|
return True
|
||||||
|
elif char.alliance_id == self.corp.alliance_id and user.has_perm('corputils.alliance_apis'):
|
||||||
|
return True
|
||||||
|
elif 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
|
||||||
|
if show_apis:
|
||||||
|
self.api = EveApiKeyPair.objects.get(api_id=char.api_id)
|
||||||
|
except (EveCharacter.DoesNotExist, AuthServicesInfo.DoesNotExist):
|
||||||
|
self.main = None
|
||||||
|
self.api = None
|
||||||
|
except EveApiKeyPair.DoesNotExist:
|
||||||
|
self.api = None
|
||||||
|
|
||||||
|
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)
|
||||||
|
39
corputils/templates/corputils/base.html
Normal file
39
corputils/templates/corputils/base.html
Normal 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 %}
|
||||||
|
|
92
corputils/templates/corputils/corpstats.html
Normal file
92
corputils/templates/corputils/corpstats.html
Normal 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">{{ corpstats.corp.corporation_name }}</td>
|
||||||
|
{% if corpstats.corp.alliance %}
|
||||||
|
<td class="text-center">{{ corpstats.corp.alliance.alliance_name }}</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>
|
||||||
|
<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 %}
|
43
corputils/templates/corputils/search.html
Normal file
43
corputils/templates/corputils/search.html
Normal 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>
|
||||||
|
<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
11
corputils/urls.py
Normal 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'),
|
||||||
|
]
|
@ -1,325 +1,112 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
|
from django.contrib import messages
|
||||||
from collections import namedtuple
|
from django.core.exceptions import PermissionDenied
|
||||||
|
from django.db import IntegrityError
|
||||||
from authentication.models import AuthServicesInfo
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
from services.managers.eve_api_manager import EveApiManager
|
from django.conf import settings
|
||||||
from services.managers.evewho_manager import EveWhoManager
|
from eveonline.models import EveCharacter, EveCorporationInfo
|
||||||
from eveonline.models import EveCorporationInfo
|
from corputils.models import CorpStats
|
||||||
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 corputils.forms import CorputilsSearchForm
|
||||||
from evelink.api import APIError
|
from esi.decorators import token_required
|
||||||
|
|
||||||
import logging
|
MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20))
|
||||||
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
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def corp_member_view(request, corpid=None, year=datetime.date.today().year, month=datetime.date.today().month):
|
@permission_required('corputils.view_corpstats')
|
||||||
year = int(year)
|
@permission_required('corputils.add_corpstats')
|
||||||
month = int(month)
|
@token_required(scopes='esi-corporations.read_corporation_membership.v1')
|
||||||
start_of_month = datetime.datetime(year, month, 1)
|
def corpstats_add(request, token):
|
||||||
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)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_main = EveCharacter.objects.get(
|
if EveCharacter.objects.filter(character_id=token.character_id).exists():
|
||||||
character_id=AuthServicesInfo.objects.get_or_create(user=request.user)[0].main_char_id)
|
corp_id = EveCharacter.objects.get(character_id=token.character_id).corporation_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
|
|
||||||
else:
|
else:
|
||||||
corpid = membercorplist[0][0]
|
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)
|
||||||
corp = EveCorporationInfo.objects.get(corporation_id=corpid)
|
CorpStats.objects.create(token=token, corp=corp)
|
||||||
if request.user.has_perm('auth.alliance_apis') or (request.user.has_perm('auth.corp_apis') and user_corp_id == corpid):
|
except EveCorporationInfo.DoesNotExist:
|
||||||
logger.debug("Retreiving and sending API-information")
|
messages.error(request, 'Unrecognized corporation. Please ensure it is a member of the alliance or a blue.')
|
||||||
|
except IntegrityError:
|
||||||
if settings.IS_CORP:
|
messages.error(request, 'Selected corp already has a statistics module.')
|
||||||
try:
|
return redirect('corputils:view')
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def corputils_search(request, corpid=settings.CORP_ID):
|
@permission_required('corputils.view_corpstats')
|
||||||
logger.debug("corputils_search called by user %s" % request.user)
|
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
|
# get available models
|
||||||
try:
|
available = CorpStats.objects.visible_to(request.user)
|
||||||
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
|
|
||||||
|
|
||||||
if authorized:
|
# ensure we can see this one
|
||||||
if request.method == 'POST':
|
if corpstats and not corpstats in available:
|
||||||
form = CorputilsSearchForm(request.POST)
|
raise PermissionDenied('You do not have permission to view the selected corporation statistics module.')
|
||||||
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))
|
|
||||||
|
|
||||||
member_list = {}
|
context = {
|
||||||
if settings.IS_CORP:
|
'available': available,
|
||||||
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)
|
|
||||||
|
|
||||||
SearchResult = namedtuple('SearchResult',
|
# paginate
|
||||||
['name', 'id', 'main', 'api_registered', 'character', 'apiinfo'])
|
members = []
|
||||||
|
if corpstats:
|
||||||
|
page = request.GET.get('page', 1)
|
||||||
|
members = get_page(corpstats.get_member_objects(request.user), page)
|
||||||
|
|
||||||
searchresults = []
|
if corpstats:
|
||||||
for memberid, member_data in member_list.items():
|
context.update({
|
||||||
if searchstring.lower() in member_data["name"].lower():
|
'corpstats': corpstats.get_view_model(request.user),
|
||||||
try:
|
'members': members,
|
||||||
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)
|
return render(request, 'corputils/corpstats.html', context=context)
|
||||||
main = EveCharacter.objects.get(character_id=mainid)
|
|
||||||
if can_see_api(request.user, char):
|
@login_required
|
||||||
api_registered = True
|
@permission_required('corputils.view_corpstats')
|
||||||
apiinfo = EveApiKeyPair.objects.get(api_id=char.api_id)
|
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:
|
else:
|
||||||
api_registered = False
|
raise PermissionDenied('You do not have permission to update member data for the selected corporation statistics module.')
|
||||||
apiinfo = None
|
return redirect('corputils:view_corp', corp_id=corp.corporation_id)
|
||||||
except EveCharacter.DoesNotExist:
|
|
||||||
api_registered = False
|
|
||||||
char = None
|
|
||||||
main = ""
|
|
||||||
apiinfo = None
|
|
||||||
|
|
||||||
searchresults.append(SearchResult(name=member_data["name"], id=memberid, main=main,
|
@login_required
|
||||||
api_registered=api_registered,
|
@permission_required('corputils.view_corpstats')
|
||||||
character=char, apiinfo=apiinfo))
|
def corpstats_search(request):
|
||||||
|
results = []
|
||||||
logger.info("Found %s members for user %s matching search string %s" % (
|
search_string = request.GET.get('search_string', None)
|
||||||
len(searchresults), request.user, searchstring))
|
if search_string:
|
||||||
|
has_similar = CorpStats.objects.filter(_members__icontains=search_string).visible_to(request.user)
|
||||||
context = {'corp': corp, 'results': searchresults, 'search_form': CorputilsSearchForm(),
|
for corpstats in has_similar:
|
||||||
"year": datetime.datetime.now().year, "month": datetime.datetime.now().month}
|
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:
|
||||||
return render(request, 'registered/corputilssearchview.html',
|
results.append((corpstats, CorpStats.MemberObject(s[0], s[1], show_apis=corpstats.show_apis(request.user))))
|
||||||
context=context)
|
page = request.GET.get('page', 1)
|
||||||
else:
|
results = sorted(results, key=lambda x: x[1].character_name)
|
||||||
logger.debug("Form invalid - returning for user %s to retry." % request.user)
|
results_page = get_page(results, page)
|
||||||
context = {'corp': corp, 'members': None, 'search_form': CorputilsSearchForm()}
|
context = {
|
||||||
return render(request, 'registered/corputilssearchview.html', context=context)
|
'available': CorpStats.objects.visible_to(request.user),
|
||||||
|
'results': results_page,
|
||||||
else:
|
'search_string': search_string,
|
||||||
logger.debug("Returning empty search form for user %s" % request.user)
|
}
|
||||||
return redirect("auth_corputils")
|
return render(request, 'corputils/search.html', context=context)
|
||||||
else:
|
return redirect('corputils:view')
|
||||||
logger.warn('User %s not authorized to view corp stats for corp ID %s' % (request.user, corpid))
|
|
||||||
return redirect("auth_dashboard")
|
|
||||||
|
@ -14,6 +14,7 @@ sleekxmpp
|
|||||||
django>=1.10,<2.0
|
django>=1.10,<2.0
|
||||||
django-bootstrap-form
|
django-bootstrap-form
|
||||||
django-navhelper
|
django-navhelper
|
||||||
|
django-bootstrap-pagination
|
||||||
|
|
||||||
# awating release for fix to celery/django-celery#447
|
# awating release for fix to celery/django-celery#447
|
||||||
# django-celery
|
# django-celery
|
||||||
|
@ -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
|
|
@ -157,9 +157,9 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if perms.auth.corp_apis or perms.auth.alliance_apis %}
|
{% if perms.corputils.view_corpstats %}
|
||||||
<li>
|
<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" %}
|
<i class="fa fa-share-alt fa-fw grayiconecolor"></i>{% trans " Corporation Stats" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -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 %}
|
|
@ -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 %}
|
|
Loading…
x
Reference in New Issue
Block a user