From a630b5b3970b5c58d5e3d0b2f735fbd1bfb692f0 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Sun, 11 Dec 2016 20:11:05 -0500 Subject: [PATCH 01/19] Migrate to adarnauth-esi --- alliance_auth/settings.py.example | 8 ++++---- alliance_auth/urls.py | 4 ++-- authentication/views.py | 7 ++++--- eveonline/views.py | 5 ++--- requirements.txt | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index 271eace0..ce81d9f7 100644 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -61,7 +61,7 @@ INSTALLED_APPS = [ 'corputils', 'fleetactivitytracking', 'notifications', - 'eve_sso', + 'esi', 'geelweb.django.navhelper', ] @@ -237,9 +237,9 @@ FORUM_URL = os.environ.get('AA_FORUM_URL', '') # 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 diff --git a/alliance_auth/urls.py b/alliance_auth/urls.py index 29613fc5..1a1e770c 100755 --- a/alliance_auth/urls.py +++ b/alliance_auth/urls.py @@ -16,7 +16,7 @@ import fleetup.views import srp.views import notifications.views import hrapplications.views -import eve_sso.urls +import esi.urls # Functional/Untranslated URL's urlpatterns = [ @@ -27,7 +27,7 @@ urlpatterns = [ url(r'^admin/', include(admin.site.urls)), # SSO - url (r'^sso/', include(eve_sso.urls, namespace='eve_sso')), + url (r'^sso/', include(esi.urls, namespace='esi')), url (r'^sso/login$', authentication.views.sso_login, name='auth_sso_login'), # Index diff --git a/authentication/views.py b/authentication/views.py index 745a74bd..9124cfd2 100644 --- a/authentication/views.py +++ b/authentication/views.py @@ -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.') diff --git a/eveonline/views.py b/eveonline/views.py index 51727f3b..27a958fe 100755 --- a/eveonline/views.py +++ b/eveonline/views.py @@ -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 @@ -70,7 +70,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 +81,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: diff --git a/requirements.txt b/requirements.txt index aa50fe15..a1d00830 100755 --- a/requirements.txt +++ b/requirements.txt @@ -22,4 +22,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 From b4b739ee615db2dcfd24a29346c8d55c8e7de39a Mon Sep 17 00:00:00 2001 From: Adarnof Date: Sun, 11 Dec 2016 22:10:05 -0500 Subject: [PATCH 02/19] FAT uses ESI tokens to get character location/ship Closes #564 --- fleetactivitytracking/views.py | 85 ++++++++-------- .../public/characternotexisting.html | 90 ----------------- stock/templates/public/clickfatlinkview.html | 96 ------------------- .../registered/characternotexisting.html | 25 +++++ .../registered/clickfatlinkview.html | 36 +++++++ 5 files changed, 106 insertions(+), 226 deletions(-) delete mode 100644 stock/templates/public/characternotexisting.html delete mode 100644 stock/templates/public/clickfatlinkview.html create mode 100644 stock/templates/registered/characternotexisting.html create mode 100644 stock/templates/registered/clickfatlinkview.html diff --git a/fleetactivitytracking/views.py b/fleetactivitytracking/views.py index 467efb84..9921fcbe 100644 --- a/fleetactivitytracking/views.py +++ b/fleetactivitytracking/views.py @@ -13,6 +13,8 @@ from eveonline.managers import EveManager from fleetactivitytracking.forms import FatlinkForm from fleetactivitytracking.models import Fatlink, Fat +from esi.decorators import token_required + from slugify import slugify from collections import OrderedDict @@ -177,51 +179,54 @@ def fatlink_monthly_personal_statistics_view(request, year, month, char_id=None) @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() + context = {'registered': True} + except ValidationError as e: + messages = [] + for errorname, message in e.message_dict.items(): + messages.append(message[0].decode()) + context = {'errormessages': 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, 'registered/characternotexisting.html', context=context) + else: + context = {'expired': True} + except (ObjectDoesNotExist, KeyError): + context = {} + return render(request, 'registered/clickfatlinkview.html', context=context) @login_required diff --git a/stock/templates/public/characternotexisting.html b/stock/templates/public/characternotexisting.html deleted file mode 100644 index aa4159f1..00000000 --- a/stock/templates/public/characternotexisting.html +++ /dev/null @@ -1,90 +0,0 @@ -{% load staticfiles %} - - - - - - - - - - - - - Fleet participation - - - - - - - {% block extra_css %}{% endblock extra_css %} - - - - -
- - - - -
- - -
-

Character not found!

-
-
-
-
-
{{ character_name }}
-
-
- -
-
- - This character is not part of any registered API-key. You must go to API key management and add an API with the character on before being able to click fleet attendance links. -
-
-
-
-
-
-
- - - - - - - - - diff --git a/stock/templates/public/clickfatlinkview.html b/stock/templates/public/clickfatlinkview.html deleted file mode 100644 index 4bdfcde2..00000000 --- a/stock/templates/public/clickfatlinkview.html +++ /dev/null @@ -1,96 +0,0 @@ -{% load staticfiles %} - - - - - - - - - - - - - Fleet participation - - - - - - - {% block extra_css %}{% endblock extra_css %} - - - - -
- - - - -
- - -
- {% if registered %}

Fleet registered!

{% elif expired%}

This link has expired.

{% elif errormessages%}

Something horrible happened. Shoot your FC!

{% else %}

Invalid link.

{% endif %} -
- {% for message in errormessages %} - - {% endfor %} - {% if trusted %} -
-
-
-
Fleet stats
-
-
- -
-
-
-
-
-
-
- - {% else %} - - {% endif %} -
-
- - - - - - - - - diff --git a/stock/templates/registered/characternotexisting.html b/stock/templates/registered/characternotexisting.html new file mode 100644 index 00000000..b976c4df --- /dev/null +++ b/stock/templates/registered/characternotexisting.html @@ -0,0 +1,25 @@ +{% extends 'public/base.html' %} +{% block title %}Fleet Participation{% endblock %} +{% block page_title %}Fleet Participation{% endblock %} +
+

Character not found!

+
+
+
+
+
{{ character_name }}
+
+
+ +
+
+ + This character is not part of any registered API-key. You must go to API key management and add an API with the character on before being able to click fleet attendance links. +
+
+
+
+
+
+
+{% endblock %} diff --git a/stock/templates/registered/clickfatlinkview.html b/stock/templates/registered/clickfatlinkview.html new file mode 100644 index 00000000..a8007700 --- /dev/null +++ b/stock/templates/registered/clickfatlinkview.html @@ -0,0 +1,36 @@ +{% extends 'public/base.html' %} +{% block title %}Fleet participation{% endblock %} +{% block page_title %}Fleet Participation{% endblock %} +{% block content %} +
+ {% if registered %} +

Fleet registered!

+ {% elif expired%} +

This link has expired.

+ {% elif errormessages%} +

Something horrible happened. Shoot your FC!

+ {% else %} +

Invalid link.

+ {% endif %} +
+ {% for message in errormessages %} + + {% endfor %} +
+
+
+
Fleet Stats
+
+
+ +
+
+
+
+
+
+
+
+
+{% endblock %} + From 32009fd3ff9cc6e38f06960a099a12ca3ad2d456 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Sun, 11 Dec 2016 23:26:20 -0500 Subject: [PATCH 03/19] Move FAT templates to app folder. Provide feedback via messages. --- .../characternotexisting.html | 0 .../fatlinkformatter.html | 0 .../fleetactivitytracking}/fatlinkmodify.html | 0 .../fatlinkpersonalmonthlystatisticsview.html | 0 .../fatlinkpersonalstatisticsview.html | 0 .../fatlinkstatisticsview.html | 0 .../fleetactivitytracking}/fatlinkview.html | 0 fleetactivitytracking/views.py | 32 ++++++++--------- stock/templates/public/base.html | 2 +- .../registered/clickfatlinkview.html | 36 ------------------- 10 files changed, 17 insertions(+), 53 deletions(-) rename {stock/templates/registered => fleetactivitytracking/templates/fleetactivitytracking}/characternotexisting.html (100%) rename {stock/templates/registered => fleetactivitytracking/templates/fleetactivitytracking}/fatlinkformatter.html (100%) rename {stock/templates/registered => fleetactivitytracking/templates/fleetactivitytracking}/fatlinkmodify.html (100%) rename {stock/templates/registered => fleetactivitytracking/templates/fleetactivitytracking}/fatlinkpersonalmonthlystatisticsview.html (100%) rename {stock/templates/registered => fleetactivitytracking/templates/fleetactivitytracking}/fatlinkpersonalstatisticsview.html (100%) rename {stock/templates/registered => fleetactivitytracking/templates/fleetactivitytracking}/fatlinkstatisticsview.html (100%) rename {stock/templates/registered => fleetactivitytracking/templates/fleetactivitytracking}/fatlinkview.html (100%) delete mode 100644 stock/templates/registered/clickfatlinkview.html diff --git a/stock/templates/registered/characternotexisting.html b/fleetactivitytracking/templates/fleetactivitytracking/characternotexisting.html similarity index 100% rename from stock/templates/registered/characternotexisting.html rename to fleetactivitytracking/templates/fleetactivitytracking/characternotexisting.html diff --git a/stock/templates/registered/fatlinkformatter.html b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkformatter.html similarity index 100% rename from stock/templates/registered/fatlinkformatter.html rename to fleetactivitytracking/templates/fleetactivitytracking/fatlinkformatter.html diff --git a/stock/templates/registered/fatlinkmodify.html b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html similarity index 100% rename from stock/templates/registered/fatlinkmodify.html rename to fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html diff --git a/stock/templates/registered/fatlinkpersonalmonthlystatisticsview.html b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html similarity index 100% rename from stock/templates/registered/fatlinkpersonalmonthlystatisticsview.html rename to fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalmonthlystatisticsview.html diff --git a/stock/templates/registered/fatlinkpersonalstatisticsview.html b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalstatisticsview.html similarity index 100% rename from stock/templates/registered/fatlinkpersonalstatisticsview.html rename to fleetactivitytracking/templates/fleetactivitytracking/fatlinkpersonalstatisticsview.html diff --git a/stock/templates/registered/fatlinkstatisticsview.html b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html similarity index 100% rename from stock/templates/registered/fatlinkstatisticsview.html rename to fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html diff --git a/stock/templates/registered/fatlinkview.html b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html similarity index 100% rename from stock/templates/registered/fatlinkview.html rename to fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html diff --git a/fleetactivitytracking/views.py b/fleetactivitytracking/views.py index 9921fcbe..6bc3aba9 100644 --- a/fleetactivitytracking/views.py +++ b/fleetactivitytracking/views.py @@ -6,7 +6,7 @@ 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 eveonline.models import EveCharacter from eveonline.models import EveCorporationInfo from eveonline.managers import EveManager @@ -71,7 +71,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 @@ -113,7 +113,7 @@ def fatlink_statistics_view(request, year=datetime.date.today().year, month=date context = {'fatStats': fatStatsList, '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 @@ -141,7 +141,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 @@ -175,7 +175,7 @@ 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 @@ -212,21 +212,21 @@ def click_fatlink_view(request, token, hash, fatname): try: fat.full_clean() fat.save() - context = {'registered': True} + messages.success(request, 'Fleet participation registered.') except ValidationError as e: - messages = [] + err_messages = [] for errorname, message in e.message_dict.items(): - messages.append(message[0].decode()) - context = {'errormessages': messages} + err_messages.append(message[0].decode()) + messages.error(request, ' '.join(err_messages)) else: context = {'character_id': token.character_id, 'character_name': token.character_name} - return render(request, 'registered/characternotexisting.html', context=context) + return render(request, 'fleetactivitytracking/characternotexisting.html', context=context) else: - context = {'expired': True} + messages.error(request, 'FAT link has expired.') except (ObjectDoesNotExist, KeyError): - context = {} - return render(request, 'registered/clickfatlinkview.html', context=context) + messages.error(request, 'Invalid FAT link.') + return redirect('auth_fatlink_view') @login_required @@ -255,7 +255,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} @@ -268,7 +268,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 @@ -296,4 +296,4 @@ def modify_fatlink_view(request, hash=""): context = {'fatlink': fatlink, 'registered_fats': registered_fats} - return render(request, 'registered/fatlinkmodify.html', context=context) + return render(request, 'fleetactivitytracking/fatlinkmodify.html', context=context) diff --git a/stock/templates/public/base.html b/stock/templates/public/base.html index 650d843a..8edaa580 100755 --- a/stock/templates/public/base.html +++ b/stock/templates/public/base.html @@ -12,7 +12,7 @@ - {% block title %}Empty title{% endblock title %} + {% block title %}Alliance Auth{% endblock title %} diff --git a/stock/templates/registered/clickfatlinkview.html b/stock/templates/registered/clickfatlinkview.html deleted file mode 100644 index a8007700..00000000 --- a/stock/templates/registered/clickfatlinkview.html +++ /dev/null @@ -1,36 +0,0 @@ -{% extends 'public/base.html' %} -{% block title %}Fleet participation{% endblock %} -{% block page_title %}Fleet Participation{% endblock %} -{% block content %} -
- {% if registered %} -

Fleet registered!

- {% elif expired%} -

This link has expired.

- {% elif errormessages%} -

Something horrible happened. Shoot your FC!

- {% else %} -

Invalid link.

- {% endif %} -
- {% for message in errormessages %} - - {% endfor %} -
-
-
-
Fleet Stats
-
-
- -
-
-
-
-
-
-
-
-
-{% endblock %} - From 27fc8a373f770ab4314187ed4fc715f9565ddfe3 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Tue, 13 Dec 2016 16:11:06 -0500 Subject: [PATCH 04/19] Pull corp memebrship data from ESI --- alliance_auth/settings.py.example | 1 + alliance_auth/urls.py | 23 +- corputils/admin.py | 4 + corputils/managers.py | 43 ++ corputils/migrations/0001_initial.py | 33 ++ .../migrations/0002_auto_20161213_0637.py | 19 + corputils/migrations/__init__.py | 0 corputils/models.py | 163 +++++++ corputils/templates/corputils/base.html | 39 ++ corputils/templates/corputils/corpstats.html | 92 ++++ corputils/templates/corputils/search.html | 43 ++ corputils/urls.py | 11 + corputils/views.py | 397 ++++-------------- requirements.txt | 1 + services/managers/evewho_manager.py | 25 -- stock/templates/public/base.html | 4 +- stock/templates/registered/corputils.html | 229 ---------- .../registered/corputilssearchview.html | 93 ---- 18 files changed, 549 insertions(+), 671 deletions(-) create mode 100644 corputils/managers.py create mode 100644 corputils/migrations/0001_initial.py create mode 100644 corputils/migrations/0002_auto_20161213_0637.py create mode 100644 corputils/migrations/__init__.py create mode 100644 corputils/templates/corputils/base.html create mode 100644 corputils/templates/corputils/corpstats.html create mode 100644 corputils/templates/corputils/search.html create mode 100644 corputils/urls.py delete mode 100644 services/managers/evewho_manager.py delete mode 100644 stock/templates/registered/corputils.html delete mode 100644 stock/templates/registered/corputilssearchview.html diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index ce81d9f7..23d753c3 100644 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -63,6 +63,7 @@ INSTALLED_APPS = [ 'notifications', 'esi', 'geelweb.django.navhelper', + 'bootstrap_pagination', ] MIDDLEWARE = [ diff --git a/alliance_auth/urls.py b/alliance_auth/urls.py index 1a1e770c..3416a130 100755 --- a/alliance_auth/urls.py +++ b/alliance_auth/urls.py @@ -10,12 +10,12 @@ 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 corputils.urls import esi.urls # Functional/Untranslated URL's @@ -27,8 +27,11 @@ urlpatterns = [ url(r'^admin/', include(admin.site.urls)), # SSO - url (r'^sso/', include(esi.urls, namespace='esi')), - 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[0-9]+)/$', corputils.views.corp_member_view, name='auth_corputils_corp_view'), - url(r'^corputils/(?P[0-9]+)/(?P[0-9]+)/(?P[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[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[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[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'), diff --git a/corputils/admin.py b/corputils/admin.py index baffc488..f3bb4062 100644 --- a/corputils/admin.py +++ b/corputils/admin.py @@ -1 +1,5 @@ from __future__ import unicode_literals +from corputils.models import CorpStats +from django.contrib import admin + +admin.site.register(CorpStats) diff --git a/corputils/managers.py b/corputils/managers.py new file mode 100644 index 00000000..701f8889 --- /dev/null +++ b/corputils/managers.py @@ -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) diff --git a/corputils/migrations/0001_initial.py b/corputils/migrations/0001_initial.py new file mode 100644 index 00000000..b56507d5 --- /dev/null +++ b/corputils/migrations/0001_initial.py @@ -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.')), + }, + ), + ] diff --git a/corputils/migrations/0002_auto_20161213_0637.py b/corputils/migrations/0002_auto_20161213_0637.py new file mode 100644 index 00000000..3fe547b2 --- /dev/null +++ b/corputils/migrations/0002_auto_20161213_0637.py @@ -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'}, + ), + ] diff --git a/corputils/migrations/__init__.py b/corputils/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/corputils/models.py b/corputils/models.py index baffc488..eb1896d9 100644 --- a/corputils/models.py +++ b/corputils/models.py @@ -1 +1,164 @@ 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) diff --git a/corputils/templates/corputils/base.html b/corputils/templates/corputils/base.html new file mode 100644 index 00000000..476a5974 --- /dev/null +++ b/corputils/templates/corputils/base.html @@ -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 %} +
+

{% trans "Corporation Member Data" %}

+
+ + {% block member_data %}{% endblock %} +
+
+{% endblock %} + diff --git a/corputils/templates/corputils/corpstats.html b/corputils/templates/corputils/corpstats.html new file mode 100644 index 00000000..dd9e110b --- /dev/null +++ b/corputils/templates/corputils/corpstats.html @@ -0,0 +1,92 @@ +{% extends 'corputils/base.html' %} +{% load i18n %} +{% load humanize %} +{% load bootstrap_pagination %} +{% block member_data %} + {% if corpstats %} +
+
+ + + + {% if corpstats.corp.alliance %} + + {% endif %} + + + + {% if corpstats.corp.alliance %} + + {% endif %} + +
{{ corpstats.corp.corporation_name }}{{ corpstats.corp.alliance.alliance_name }}
+
+
+
+
+ {% trans "API Index:" %} +
+
+ {{ corpstats.registered_members }}/{{ corpstats.total_members }} +
+
+
+
+
+
+
+
+
+

Members

+
+
+ Last update: {{ corpstats.last_updated|naturaltime }} + {% if corpstats.can_update %} + + + + {% endif %} +
+
+
+
+ {% bootstrap_paginate members range=10 %} +
+
+ + + + + {% if corpstats.show_apis %} + + {% endif %} + + + + + + {% for member in members %} + + + + {% if corpstats.show_apis %} + {% if member.api %} + + {% else %} + + {% endif %} + {% endif %} + + + + + + {% endfor %} +
CharacterAPIzKillboardMain CharacterMain CorporationMain Alliance
{{ member.character_name }}{{ member.api.api_id }}{% trans "Killboard" %}{{ member.main.character_name }}{{ member.main.corporation_name }}{{ member.main.alliance_name }}
+
+
+
+
+
+ {% endif %} +{% endblock %} diff --git a/corputils/templates/corputils/search.html b/corputils/templates/corputils/search.html new file mode 100644 index 00000000..0848a3e9 --- /dev/null +++ b/corputils/templates/corputils/search.html @@ -0,0 +1,43 @@ +{% extends "corputils/base.html" %} +{% load i18n %} +{% load bootstrap_pagination %} +{% block member_data %} +
+
+
Search Results
+
+
+
+ {% bootstrap_paginate results range=10 %} +
+ + + + + + + + + + + + {% for result in results %} + + + + + {% if result.1.api %} + + {% else %} + + {% endif %} + + + + + + {% endfor %} +
CharacterCorporationAPIzKillboardMain CharacterMain CorporationMain Alliance
{{ result.1.character_name }}{{ result.0.corp.corporation_name }}{{ result.1.api.api_id }}{% trans "Killboard" %}{{ result.1.main.character_name }}{{ result.1.main.corporation_name }}{{ result.1.main.alliance_name }}
+
+
+{% endblock %} diff --git a/corputils/urls.py b/corputils/urls.py new file mode 100644 index 00000000..3cd98e85 --- /dev/null +++ b/corputils/urls.py @@ -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(\d)*)/$', corputils.views.corpstats_view, name='view_corp'), + url(r'^(?P(\d)+)/update/$', corputils.views.corpstats_update, name='update'), + url(r'^search/$', corputils.views.corpstats_search, name='search'), + ] diff --git a/corputils/views.py b/corputils/views.py index 1dc6a874..c7e86def 100644 --- a/corputils/views.py +++ b/corputils/views.py @@ -1,325 +1,112 @@ 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 collections import namedtuple - -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 django.contrib.auth.decorators import login_required, permission_required +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 corputils.forms import CorputilsSearchForm -from evelink.api import APIError +from esi.decorators import token_required -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) +MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20)) +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 -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) - +@permission_required('corputils.view_corpstats') +@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) + CorpStats.objects.create(token=token, corp=corp) + 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.') + return redirect('corputils:view') @login_required -def corputils_search(request, corpid=settings.CORP_ID): - logger.debug("corputils_search called by user %s" % request.user) +@permission_required('corputils.view_corpstats') +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 this one + 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) + context = { + 'available': available, + } - SearchResult = namedtuple('SearchResult', - ['name', 'id', 'main', 'api_registered', 'character', 'apiinfo']) + # paginate + members = [] + if corpstats: + page = request.GET.get('page', 1) + members = get_page(corpstats.get_member_objects(request.user), page) - 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 + if corpstats: + context.update({ + 'corpstats': corpstats.get_view_model(request.user), + 'members': members, + }) - searchresults.append(SearchResult(name=member_data["name"], id=memberid, main=main, - api_registered=api_registered, - character=char, apiinfo=apiinfo)) + return render(request, 'corputils/corpstats.html', context=context) - logger.info("Found %s members for user %s matching search string %s" % ( - len(searchresults), request.user, searchstring)) - - 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 +@permission_required('corputils.view_corpstats') +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 +@permission_required('corputils.view_corpstats') +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') diff --git a/requirements.txt b/requirements.txt index a1d00830..eb129766 100755 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,7 @@ sleekxmpp django>=1.10,<2.0 django-bootstrap-form django-navhelper +django-bootstrap-pagination # awating release for fix to celery/django-celery#447 # django-celery diff --git a/services/managers/evewho_manager.py b/services/managers/evewho_manager.py deleted file mode 100644 index ee7407e2..00000000 --- a/services/managers/evewho_manager.py +++ /dev/null @@ -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 diff --git a/stock/templates/public/base.html b/stock/templates/public/base.html index 8edaa580..5b6bb177 100755 --- a/stock/templates/public/base.html +++ b/stock/templates/public/base.html @@ -157,9 +157,9 @@ {% endif %} - {% if perms.auth.corp_apis or perms.auth.alliance_apis %} + {% if perms.corputils.view_corpstats %}
  • - + {% trans " Corporation Stats" %}
  • diff --git a/stock/templates/registered/corputils.html b/stock/templates/registered/corputils.html deleted file mode 100644 index 8cfffe7d..00000000 --- a/stock/templates/registered/corputils.html +++ /dev/null @@ -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 %} - -
    -

    {% trans "Corporation Member Data" %}

    -
    - {% if corp %} -
    -
    -
    -
    {% trans "Corporation" %}
    -
    -
    - -
    -
    -

    {{ corp.corporation_name }}

    - -

    {% trans "Ticker:" %} {{ corp.corporation_ticker }}

    - -

    {% trans "Member count:" %} {{ corp.member_count }}

    - -

    {% trans "Player count:" %} {{characters_with_api|length}}

    - -

    {% trans "Unregistered characters:" %} {{characters_without_api|length|add:n_unacounted}}

    -
    -
    - {% trans "API Index:" %} -
    -
    - {{n_registered}}/{{ corp.member_count }} -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - {% if characters_with_api %} -
    -
    - - - - - - - - {% if perms.auth.fleetactivitytracking_statistics %} - - - {% else %} - - {% endif %} - - - {% for maincharname, player in characters_with_api %} - - - - - - - - {% if perms.auth.fleetactivitytracking %} - - {% endif %} - - - {% endfor %} -
    {% trans "Main character" %}{% trans "Main corporation" %}{% trans "Character list" %}{% trans "Fats" %}{% trans "Killboard" %}{% trans "Fleet statistics" %}{% trans "Killboard" %}{% trans "API JackKnife" %}
    - - -

    {{ maincharname }}

    -
    - {% if not corp.corporation_name == player.maincorp%} - - {{ player.maincorp }} - - {% else %} - - {{ player.maincorp }} - - {% endif %} - - {% for char in player.altlist %} -

    {{ char.character_name }}

    - {% endfor %} -
    - {{ player.n_fats }} - - {% for char in player.altlist %} -

    {% trans "Killboard" %}

    - {% endfor %} -
    - - - - - {% for apiinfo in player.apilist %} -

    - - {% trans "API JackKnife" %} - -

    - {% endfor %} -
    -
    -
    - {% else %} - - {% endif %} -
    -
    - {% if characters_without_api %} -
    -
    - {% if 0 < n_unacounted %} - - {% endif %} - - - - - - - {% for character_name, character_id in characters_without_api %} - - - - - - {% endfor %} -
    {% trans "Character" %}{% trans "Killboard" %}
    - - -

    {{ character_name }}

    -
    - {% trans "Killboard" %} -
    -
    -
    - {% else %} - - {% endif %} -
    -
    - {% else %} -
    -
    -
    - -
    -
    -
    -
    - {% endif %} -
    - -{% endblock content %} diff --git a/stock/templates/registered/corputilssearchview.html b/stock/templates/registered/corputilssearchview.html deleted file mode 100644 index dc45b6d0..00000000 --- a/stock/templates/registered/corputilssearchview.html +++ /dev/null @@ -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 %} -
    -

    {% trans "Member Search Results" %}

    -

    {{ corp.corporation_name }}

    -
    - -
    - -
    - - - - - - {% if perms.auth.fleetactivitytracking%} - - - {% else %} - - {% endif %} - - - {% for result in results %} - - - - - - - {% if perms.auth.fleetactivitytracking %} - {% if result.main %} - - {% else %} - - {% endif %} - {% endif %} - - - - {% endfor %} -
    {% trans "Character" %}{% trans "Main character" %}{% trans "Killboard" %}{% trans "Fleet statistics" %}{% trans "Killboard" %}{% trans "API JackKnife" %}
    - - {{ result.name }} - {% if result.api_registered%} - {{ result.main.character_name }} - {% else %} - {% trans "No API registered!" %} - {% endif %} - -

    {% trans "Killboard" %}

    -
    - - - - - {% if result.api_registered %} - - - - {% endif %} -
    -
    -
    -
    -
    -{% endblock content %} From 3d92e4c5c572220f5198233f3a2cc6c49b3cb991 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Tue, 13 Dec 2016 16:26:20 -0500 Subject: [PATCH 05/19] Perform first corpstats update on creation. --- corputils/migrations/0001_initial.py | 4 +++- .../migrations/0002_auto_20161213_0637.py | 19 ------------------- corputils/models.py | 3 ++- corputils/views.py | 8 +++++++- 4 files changed, 12 insertions(+), 22 deletions(-) delete mode 100644 corputils/migrations/0002_auto_20161213_0637.py diff --git a/corputils/migrations/0001_initial.py b/corputils/migrations/0001_initial.py index b56507d5..4c8850c5 100644 --- a/corputils/migrations/0001_initial.py +++ b/corputils/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.1 on 2016-12-13 06:23 +# Generated by Django 1.10.1 on 2016-12-13 21:24 from __future__ import unicode_literals from django.db import migrations, models @@ -27,6 +27,8 @@ class Migration(migrations.Migration): ], options={ 'default_permissions': ('add', 'delete', 'view'), + '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.')), }, ), diff --git a/corputils/migrations/0002_auto_20161213_0637.py b/corputils/migrations/0002_auto_20161213_0637.py deleted file mode 100644 index 3fe547b2..00000000 --- a/corputils/migrations/0002_auto_20161213_0637.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- 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'}, - ), - ] diff --git a/corputils/models.py b/corputils/models.py index eb1896d9..38d95672 100644 --- a/corputils/models.py +++ b/corputils/models.py @@ -32,7 +32,8 @@ class CorpStats(models.Model): 'delete', 'view', ) - verbose_name = "Corp stats" + verbose_name = "corp stats" + verbose_name_plural = "corp stats" objects = CorpStatsManager() diff --git a/corputils/views.py b/corputils/views.py index c7e86def..40dd51fa 100644 --- a/corputils/views.py +++ b/corputils/views.py @@ -35,11 +35,17 @@ def corpstats_add(request, token): else: 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) - CorpStats.objects.create(token=token, corp=corp) + 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 From 6fd3c32ba077a0cf283678b3c135985f727d8e7f Mon Sep 17 00:00:00 2001 From: Adarnof Date: Tue, 13 Dec 2016 17:30:06 -0500 Subject: [PATCH 06/19] Correct check if corputils is visible to user. Add additional admin site permissions. --- corputils/forms.py | 8 -------- corputils/managers.py | 9 ++++----- corputils/migrations/0001_initial.py | 4 ++-- corputils/models.py | 3 ++- corputils/views.py | 1 - 5 files changed, 8 insertions(+), 17 deletions(-) delete mode 100644 corputils/forms.py diff --git a/corputils/forms.py b/corputils/forms.py deleted file mode 100644 index 0a05bd8a..00000000 --- a/corputils/forms.py +++ /dev/null @@ -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...')})) diff --git a/corputils/managers.py b/corputils/managers.py index 701f8889..1c73ab60 100644 --- a/corputils/managers.py +++ b/corputils/managers.py @@ -12,14 +12,13 @@ class CorpStatsQuerySet(models.QuerySet): 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'): + if user.has_perm('corputils.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'): + if user.has_perm('corputils.alliance_apis'): + queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id)) + if user.has_perm('corputils.blue_apis'): queries.append(models.Q(corp__is_blue=True)) # filter based on queries diff --git a/corputils/migrations/0001_initial.py b/corputils/migrations/0001_initial.py index 4c8850c5..e58d8de4 100644 --- a/corputils/migrations/0001_initial.py +++ b/corputils/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.1 on 2016-12-13 21:24 +# Generated by Django 1.10.1 on 2016-12-13 22:24 from __future__ import unicode_literals from django.db import migrations, models @@ -26,7 +26,7 @@ class Migration(migrations.Migration): ('token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='esi.Token')), ], options={ - 'default_permissions': ('add', 'delete', 'view'), + 'default_permissions': ('add', 'change', 'remove', 'view'), '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.')), diff --git a/corputils/models.py b/corputils/models.py index 38d95672..726d5bbe 100644 --- a/corputils/models.py +++ b/corputils/models.py @@ -29,7 +29,8 @@ class CorpStats(models.Model): ) default_permissions = ( 'add', - 'delete', + 'change', + 'remove', 'view', ) verbose_name = "corp stats" diff --git a/corputils/views.py b/corputils/views.py index 40dd51fa..d768a58b 100644 --- a/corputils/views.py +++ b/corputils/views.py @@ -9,7 +9,6 @@ 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 corputils.forms import CorputilsSearchForm from esi.decorators import token_required MEMBERS_PER_PAGE = int(getattr(settings, 'CORPSTATS_MEMBERS_PER_PAGE', 20)) From 6ba084c7106d76667c24a5b701f24efca15fcb48 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Tue, 13 Dec 2016 17:31:36 -0500 Subject: [PATCH 07/19] Cleanup FAT edit page tables with pagination. Cleanup FAT statistics generation with smarter query. --- .../fleetactivitytracking/fatlinkmodify.html | 72 ++++++++++--------- fleetactivitytracking/views.py | 68 +++++++++++------- 2 files changed, 81 insertions(+), 59 deletions(-) diff --git a/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html index 3f08bb85..6db008c7 100644 --- a/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html +++ b/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html @@ -2,13 +2,13 @@ {% 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 %}
    -

    {% blocktrans %}Edit fatlink "{{ fatlink.name }}"{% endblocktrans %} +

    {% blocktrans %}Edit fatlink "{{ fatlink }}"{% endblocktrans %}

    -

    {% trans "Registered characters" %}

    - - - - - - - - - - {% for fat in registered_fats %} - - - - {% if fat.station != "No Station" %} - - {% else %} - - {% endif %} - - - - - {% endfor %} -
    {% trans "User" %}{% trans "Character" %}{% trans "System" %}{% trans "Ship" %}{% trans "Eve Time" %}
    {{ fat.user }}{{ fat.character.character_name }}Docked in {{ fat.system }}{{ fat.system }}{{ fat.shiptype }}{{ fat.fatlink.fatdatetime }} -
    - -
    -
    +
    +
    {% trans "Registered characters" %}
    +
    +
    + {% bootstrap_paginate registered_fats range=10 %} +
    + + + + + + + + + + {% for fat in registered_fats %} + + + + {% if fat.station != "No Station" %} + + {% else %} + + {% endif %} + + + + + {% endfor %} +
    {% trans "User" %}{% trans "Character" %}{% trans "System" %}{% trans "Ship" %}{% trans "Eve Time" %}
    {{ fat.user }}{{ fat.character.character_name }}Docked in {{ fat.system }}{{ fat.system }}{{ fat.shiptype }}{{ fat.fatlink.fatdatetime }} +
    + +
    +
    +
    +
    diff --git a/fleetactivitytracking/views.py b/fleetactivitytracking/views.py index 6bc3aba9..133b588f 100644 --- a/fleetactivitytracking/views.py +++ b/fleetactivitytracking/views.py @@ -7,6 +7,7 @@ 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 @@ -17,8 +18,6 @@ from esi.decorators import token_required from slugify import slugify -from collections import OrderedDict - import string import random import datetime @@ -27,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)) @@ -83,34 +94,35 @@ 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, 'fleetactivitytracking/fatlinkstatisticsview.html', context=context) @@ -276,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)) + + context = {'fatlink': fatlink, 'registered_fats': fat_page} return render(request, 'fleetactivitytracking/fatlinkmodify.html', context=context) From 02bd4570b062f74c730c7c280c045a973848523d Mon Sep 17 00:00:00 2001 From: Adarnof Date: Tue, 13 Dec 2016 17:45:42 -0500 Subject: [PATCH 08/19] Default to only available corpstats if no corp_id --- corputils/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/corputils/views.py b/corputils/views.py index d768a58b..b21254ad 100644 --- a/corputils/views.py +++ b/corputils/views.py @@ -61,10 +61,14 @@ def corpstats_view(request, corp_id=None): # get available models available = CorpStats.objects.visible_to(request.user) - # ensure we can see this one + # 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.') + # get default model if none requested + if not corp_id and available.count() == 1: + corpstats = available[0] + context = { 'available': available, } From 5a9418d792364e42e99ebb207edb5a0ed50e1dba Mon Sep 17 00:00:00 2001 From: Adarnof Date: Tue, 13 Dec 2016 19:29:02 -0500 Subject: [PATCH 09/19] Visual indication of member registration. --- corputils/models.py | 12 +++++++++--- corputils/templates/corputils/corpstats.html | 2 +- corputils/templates/corputils/search.html | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/corputils/models.py b/corputils/models.py index 726d5bbe..9fc6c746 100644 --- a/corputils/models.py +++ b/corputils/models.py @@ -90,9 +90,9 @@ class CorpStats(models.Model): 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'): + if self.corp.alliance and char.alliance_id == self.corp.alliance.alliance_id and user.has_perm('corputils.alliance_apis'): return True - elif user.has_perm('corputils.blue_apis') and self.corp.is_blue: + if user.has_perm('corputils.blue_apis') and self.corp.is_blue: return True except EveCharacter.DoesNotExist: pass @@ -117,13 +117,19 @@ class CorpStats(models.Model): 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 = EveApiKeyPair.objects.get(api_id=char.api_id) + 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 diff --git a/corputils/templates/corputils/corpstats.html b/corputils/templates/corputils/corpstats.html index dd9e110b..fbebcf31 100644 --- a/corputils/templates/corputils/corpstats.html +++ b/corputils/templates/corputils/corpstats.html @@ -66,7 +66,7 @@ Main Alliance {% for member in members %} - + {{ member.character_name }} {% if corpstats.show_apis %} diff --git a/corputils/templates/corputils/search.html b/corputils/templates/corputils/search.html index 0848a3e9..1687a39b 100644 --- a/corputils/templates/corputils/search.html +++ b/corputils/templates/corputils/search.html @@ -22,7 +22,7 @@ Main Alliance {% for result in results %} - + {{ result.1.character_name }} {{ result.0.corp.corporation_name }} From 5f88e7e1a530f9ad0a1d409e20ee48550739cc91 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Tue, 13 Dec 2016 20:22:09 -0500 Subject: [PATCH 10/19] Correct permission check for showing APIs --- corputils/models.py | 2 +- corputils/templates/corputils/corpstats.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/corputils/models.py b/corputils/models.py index 9fc6c746..3a29d058 100644 --- a/corputils/models.py +++ b/corputils/models.py @@ -87,7 +87,7 @@ class CorpStats(models.Model): auth = AuthServicesInfo.objects.get_or_create(user=user)[0] if auth.main_char_id: try: - char = EveCharacter.objects.get(id=auth.main_char_id) + 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'): diff --git a/corputils/templates/corputils/corpstats.html b/corputils/templates/corputils/corpstats.html index fbebcf31..45a2a63c 100644 --- a/corputils/templates/corputils/corpstats.html +++ b/corputils/templates/corputils/corpstats.html @@ -14,9 +14,9 @@ {% endif %} - {{ corpstats.corp.corporation_name }} +

    {{ corpstats.corp.corporation_name }}

    {% if corpstats.corp.alliance %} - {{ corpstats.corp.alliance.alliance_name }} +

    {{ corpstats.corp.alliance.alliance_name }}

    {% endif %} From 4ee10e0c31b886c58d21ca1460c3f37dff34086b Mon Sep 17 00:00:00 2001 From: Adarnof Date: Wed, 14 Dec 2016 20:40:12 -0500 Subject: [PATCH 11/19] Additional permissions for non-api viewing. Migration to convert permissions from old users. --- corputils/managers.py | 6 +- corputils/migrations/0001_initial.py | 4 +- .../migrations/0002_migrate_permissions.py | 125 ++++++++++++++++++ corputils/models.py | 4 +- corputils/views.py | 13 +- stock/templates/public/base.html | 2 +- 6 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 corputils/migrations/0002_migrate_permissions.py diff --git a/corputils/managers.py b/corputils/managers.py index 1c73ab60..8da08ac4 100644 --- a/corputils/managers.py +++ b/corputils/managers.py @@ -14,11 +14,11 @@ class CorpStatsQuerySet(models.QuerySet): char = EveCharacter.objects.get(character_id=auth.main_char_id) # build all accepted queries queries = [] - if user.has_perm('corputils.corp_apis'): + if user.has_perm('corputils.view_corp_corpstats'): queries.append(models.Q(corp__corporation_id=char.corporation_id)) - if user.has_perm('corputils.alliance_apis'): + if user.has_perm('corputils.view_alliance_corpstats'): queries.append(models.Q(corp__alliance__alliance_id=char.alliance_id)) - if user.has_perm('corputils.blue_apis'): + if user.has_perm('corputils.view_blue_corpstats'): queries.append(models.Q(corp__is_blue=True)) # filter based on queries diff --git a/corputils/migrations/0001_initial.py b/corputils/migrations/0001_initial.py index e58d8de4..4fa10a4a 100644 --- a/corputils/migrations/0001_initial.py +++ b/corputils/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.1 on 2016-12-13 22:24 +# Generated by Django 1.10.1 on 2016-12-14 21:36 from __future__ import unicode_literals from django.db import migrations, models @@ -26,7 +26,7 @@ class Migration(migrations.Migration): ('token', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='esi.Token')), ], options={ - 'default_permissions': ('add', 'change', 'remove', 'view'), + '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.')), diff --git a/corputils/migrations/0002_migrate_permissions.py b/corputils/migrations/0002_migrate_permissions.py new file mode 100644 index 00000000..8c8aa46f --- /dev/null +++ b/corputils/migrations/0002_migrate_permissions.py @@ -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 +from django.db.models import Q + +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'), + ] + + operations = [ + migrations.RunPython(forward, reverse), + ] diff --git a/corputils/models.py b/corputils/models.py index 3a29d058..c00089e3 100644 --- a/corputils/models.py +++ b/corputils/models.py @@ -31,7 +31,9 @@ class CorpStats(models.Model): 'add', 'change', 'remove', - 'view', + 'view_corp', + 'view_alliance', + 'view_blue', ) verbose_name = "corp stats" verbose_name_plural = "corp stats" diff --git a/corputils/views.py b/corputils/views.py index b21254ad..922e75d4 100644 --- a/corputils/views.py +++ b/corputils/views.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals from django.conf import settings -from django.contrib.auth.decorators import login_required, permission_required +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 @@ -23,8 +23,11 @@ def get_page(model_list, page_num): 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 -@permission_required('corputils.view_corpstats') +@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): @@ -48,7 +51,7 @@ def corpstats_add(request, token): return redirect('corputils:view') @login_required -@permission_required('corputils.view_corpstats') +@user_passes_test(access_corpstats_test) def corpstats_view(request, corp_id=None): corpstats = None show_apis = False @@ -88,7 +91,7 @@ def corpstats_view(request, corp_id=None): return render(request, 'corputils/corpstats.html', context=context) @login_required -@permission_required('corputils.view_corpstats') +@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) @@ -99,7 +102,7 @@ def corpstats_update(request, corp_id): return redirect('corputils:view_corp', corp_id=corp.corporation_id) @login_required -@permission_required('corputils.view_corpstats') +@user_passes_test(access_corpstats_test) def corpstats_search(request): results = [] search_string = request.GET.get('search_string', None) diff --git a/stock/templates/public/base.html b/stock/templates/public/base.html index 5b6bb177..2f0d3e5d 100755 --- a/stock/templates/public/base.html +++ b/stock/templates/public/base.html @@ -157,7 +157,7 @@ {% endif %} - {% if perms.corputils.view_corpstats %} + {% if perms.corputils.view_corp_corpstats or perms.corputils.view_alliance_corpstats or perms.corputils.view_blue_corpstats %}
  • {% trans " Corporation Stats" %} From b514f8cbcce774f8cee39634bbff75ecf805e28b Mon Sep 17 00:00:00 2001 From: Adarnof Date: Wed, 14 Dec 2016 20:57:56 -0500 Subject: [PATCH 12/19] Correct missing migration dependency. --- corputils/migrations/0002_migrate_permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/corputils/migrations/0002_migrate_permissions.py b/corputils/migrations/0002_migrate_permissions.py index 8c8aa46f..e4a3f852 100644 --- a/corputils/migrations/0002_migrate_permissions.py +++ b/corputils/migrations/0002_migrate_permissions.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals from django.db import migrations -from django.db.models import Q PERMISSIONS = { 'user': [ @@ -118,6 +117,7 @@ class Migration(migrations.Migration): dependencies = [ ('corputils', '0001_initial'), ('authentication', '0005_delete_perms'), + ('auth', '0008_alter_user_username_max_length'), ] operations = [ From 0fe5a1c5e3e22b7c2b725897c1d8336fd3c0684b Mon Sep 17 00:00:00 2001 From: Adarnof Date: Fri, 16 Dec 2016 17:43:44 -0500 Subject: [PATCH 13/19] Correct context processor for SSO address. --- authentication/context_processors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/authentication/context_processors.py b/authentication/context_processors.py index d403df83..e6f7d242 100644 --- a/authentication/context_processors.py +++ b/authentication/context_processors.py @@ -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, } From 66240ad296f9d97cf3f4462dba8059feef35f414 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Fri, 16 Dec 2016 22:13:29 -0500 Subject: [PATCH 14/19] Standardize EVE datasource responses. Remove alliance member count. --- alliance_auth/settings.py.example | 8 + ...005_remove_eveallianceinfo_member_count.py | 19 ++ eveonline/models.py | 1 - eveonline/providers.py | 215 ++++++++++++++++++ 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 eveonline/migrations/0005_remove_eveallianceinfo_member_count.py create mode 100644 eveonline/providers.py diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index 23d753c3..168314f6 100644 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -356,6 +356,14 @@ 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_DATA_PROVIDER - Name of default data source for getting eve entity data +# Available soruces are 'esi' and 'xml' +####################### +EVEONLINE_DATA_PROVIDER = os.environ.get('AA_EVEONLINE_DATA_PROVIDER', 'esi') + ##################### # Alliance Market ##################### diff --git a/eveonline/migrations/0005_remove_eveallianceinfo_member_count.py b/eveonline/migrations/0005_remove_eveallianceinfo_member_count.py new file mode 100644 index 00000000..f9a7bfab --- /dev/null +++ b/eveonline/migrations/0005_remove_eveallianceinfo_member_count.py @@ -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', + ), + ] diff --git a/eveonline/models.py b/eveonline/models.py index 74b26d48..118ae305 100644 --- a/eveonline/models.py +++ b/eveonline/models.py @@ -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 diff --git a/eveonline/providers.py b/eveonline/providers.py new file mode 100644 index 00000000..b33e7ba4 --- /dev/null +++ b/eveonline/providers.py @@ -0,0 +1,215 @@ +from django.utils.encoding import python_2_unicode_compatible +from esi.clients import esi_client_factory +from django.conf import settings +import evelink + +@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): + super(Alliance, self).__init__(id, name) + self.provider = provider + self.ticker = ticker + self.corp_ids = corp_ids + + 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) + + +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() + + +class EveSwaggerProvider(EveProvider): + def __init__(self, token=None): + self.client = esi_client_factory(token=token) + + def get_alliance(self, alliance_id): + data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result() + corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result() + model = Alliance( + self, + alliance_id, + data['alliance_name'], + data['ticker'], + corps, + ) + return model + + def get_corp(self, corp_id): + data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result() + model = Corporation( + self, + corp_id, + data['corporation_name'], + data['ticker'], + data['ceo_id'], + data['member_count'], + data['alliance_id'] if 'alliance_id' in data else None, + ) + return model + + def get_character(self, character_id): + data = self.client.Character.get_characters_character_id(character_id=character_id).result() + alliance_id = self.get_corp(data['corporation_id']).alliance_id + model = Character( + self, + character_id, + data['name'], + data['corporation_id'], + alliance_id, + ) + return model + + +class EveXmlProvider(EveProvider): + def __init__(self, api_key=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() + + def get_alliance(self, id): + api = evelink.eve.EVE(api=self.api) + alliances = api.alliances().result + results = alliances[int(id)] + model = Alliance( + self, + id, + results['name'], + results['ticker'], + results['member_corps'], + ) + return model + + def get_corp(self, id): + api = evelink.corp.Corp(api=self.api) + corpinfo = api.corporation_sheet(corp_id=int(id)).result + model = Corporation( + self, + id, + corpinfo['name'], + corpinfo['ceo']['id'], + corpinfo['members']['current'], + corpinfo['ticker'], + corpinfo['alliance']['id'] if corpinfo['alliance'] else None, + ) + return model + + def get_character(self, id): + api = evelink.eve.EVE(api=self.api) + charinfo = api.character_info_from_id(id).result + model = Character( + self, + id, + charinfo['name'], + charinfo['corp']['id'], + charinfo['alliance']['id'], + ) + return model + + + +class EveAdapter(EveProvider): + """ + Redirects queries to appropriate data source. + """ + def __init__(self, provider): + self.provider = provider + + def __repr__(self): + return "<{} ({})>".format(self.__class__.__name__, self.provider.__class__.__name__) + + def get_character(self, id): + return self.provider.get_character(id) + + def get_corporation(self, id): + return self.provider.get_corporation(id) + + def get_alliance(self, id): + return self.provider.get_alliance(id) + + +def adapter_factory(source=settings.EVEONLINE_DATA_PROVIDER, **kwargs): + if source == 'xml': + return EveAdapter(EveXmlProvider(**kwargs)) + elif source == 'esi': + return EveAdapter(EveSwaggerProvider(**kwargs)) + else: + raise ValueError('Unrecognized data source.') From ec9b43b0830a7c6fc8a645e93070d259b393ae88 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Sat, 17 Dec 2016 14:46:02 -0500 Subject: [PATCH 15/19] Allow different sources for EVE data types. --- alliance_auth/settings.py.example | 11 +++-- eveonline/providers.py | 72 ++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index 168314f6..0507efb8 100644 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -359,10 +359,15 @@ API_SSO_VALIDATION = 'True' == os.environ.get('AA_API_SSO_VALIDATION', 'False') ####################### # EVE Provider Settings ####################### -# EVEONLINE_DATA_PROVIDER - Name of default data source for getting eve entity data -# Available soruces are 'esi' and 'xml' +# 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_DATA_PROVIDER = os.environ.get('AA_EVEONLINE_DATA_PROVIDER', 'esi') +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 diff --git a/eveonline/providers.py b/eveonline/providers.py index b33e7ba4..c6c2e940 100644 --- a/eveonline/providers.py +++ b/eveonline/providers.py @@ -96,15 +96,20 @@ class EveProvider: raise NotImplementedError() +@python_2_unicode_compatible class EveSwaggerProvider(EveProvider): - def __init__(self, token=None): + 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, alliance_id): data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result() corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result() model = Alliance( - self, + self.adapter, alliance_id, data['alliance_name'], data['ticker'], @@ -115,7 +120,7 @@ class EveSwaggerProvider(EveProvider): def get_corp(self, corp_id): data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result() model = Corporation( - self, + self.adapter, corp_id, data['corporation_name'], data['ticker'], @@ -127,9 +132,9 @@ class EveSwaggerProvider(EveProvider): def get_character(self, character_id): data = self.client.Character.get_characters_character_id(character_id=character_id).result() - alliance_id = self.get_corp(data['corporation_id']).alliance_id + alliance_id = self.adapter.get_corp(data['corporation_id']).alliance_id model = Character( - self, + self.adapter, character_id, data['name'], data['corporation_id'], @@ -138,19 +143,24 @@ class EveSwaggerProvider(EveProvider): return model +@python_2_unicode_compatible class EveXmlProvider(EveProvider): - def __init__(self, api_key=None): + 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 results = alliances[int(id)] model = Alliance( - self, + self.adapter, id, results['name'], results['ticker'], @@ -162,7 +172,7 @@ class EveXmlProvider(EveProvider): api = evelink.corp.Corp(api=self.api) corpinfo = api.corporation_sheet(corp_id=int(id)).result model = Corporation( - self, + self.adapter, id, corpinfo['name'], corpinfo['ceo']['id'], @@ -176,40 +186,52 @@ class EveXmlProvider(EveProvider): api = evelink.eve.EVE(api=self.api) charinfo = api.character_info_from_id(id).result model = Character( - self, + self.adapter, id, charinfo['name'], charinfo['corp']['id'], charinfo['alliance']['id'], ) return model - class EveAdapter(EveProvider): """ Redirects queries to appropriate data source. """ - def __init__(self, provider): - self.provider = provider + 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 "<{} ({})>".format(self.__class__.__name__, self.provider.__class__.__name__) + 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.provider.get_character(id) + return self.char_provider.get_character(id) - def get_corporation(self, id): - return self.provider.get_corporation(id) + def get_corp(self, id): + return self.corp_provider.get_corp(id) def get_alliance(self, id): - return self.provider.get_alliance(id) + return self.alliance_provider.get_alliance(id) -def adapter_factory(source=settings.EVEONLINE_DATA_PROVIDER, **kwargs): - if source == 'xml': - return EveAdapter(EveXmlProvider(**kwargs)) - elif source == 'esi': - return EveAdapter(EveSwaggerProvider(**kwargs)) - else: - raise ValueError('Unrecognized data source.') +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]) From 8379fdd7d530a49b9fbd0d8de10ad61c056190e9 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Sat, 17 Dec 2016 20:51:59 -0500 Subject: [PATCH 16/19] Update corp/alliance models using adapters. --- eveonline/managers.py | 148 ++++++++++++++++---------- eveonline/providers.py | 116 +++++++++++++------- eveonline/tasks.py | 234 ++++++++++++----------------------------- 3 files changed, 240 insertions(+), 258 deletions(-) diff --git a/eveonline/managers.py b/eveonline/managers.py index 214433e4..e8ee3d4b 100644 --- a/eveonline/managers.py +++ b/eveonline/managers.py @@ -3,17 +3,35 @@ 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 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(id, user, api_id): + return EveManager.create_character_obj(adapter.get_character(id), user, api_id) + + @staticmethod + 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 create_character(character_id, character_name, corporation_id, corporation_name, corporation_ticker, alliance_id, @@ -50,6 +68,20 @@ class EveManager: chars.result[char]['alliance']['name'], user, api_id) + @staticmethod + 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 update_characters_from_list(chars): logger.debug("Updating characters from list: %s" % chars.result) @@ -83,64 +115,74 @@ 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_api_key_pairs(user): diff --git a/eveonline/providers.py b/eveonline/providers.py index c6c2e940..be71244a 100644 --- a/eveonline/providers.py +++ b/eveonline/providers.py @@ -1,8 +1,19 @@ 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): @@ -43,11 +54,12 @@ class Corporation(Entity): class Alliance(Entity): - def __init__(self, provider, id, name, ticker, corp_ids): + 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 @@ -57,6 +69,10 @@ class Alliance(Entity): 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): @@ -105,42 +121,52 @@ class EveSwaggerProvider(EveProvider): def __str__(self): return 'esi' - def get_alliance(self, alliance_id): - data = self.client.Alliance.get_alliances_alliance_id(alliance_id=alliance_id).result() - corps = self.client.Alliance.get_alliances_alliance_id_corporations(alliance_id=alliance_id).result() - model = Alliance( - self.adapter, - alliance_id, - data['alliance_name'], - data['ticker'], - corps, - ) - return model + 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, corp_id): - data = self.client.Corporation.get_corporations_corporation_id(corporation_id=corp_id).result() - model = Corporation( - self.adapter, - corp_id, - data['corporation_name'], - data['ticker'], - data['ceo_id'], - data['member_count'], - data['alliance_id'] if 'alliance_id' in data else None, - ) - return model + 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, character_id): - data = self.client.Character.get_characters_character_id(character_id=character_id).result() - alliance_id = self.adapter.get_corp(data['corporation_id']).alliance_id - model = Character( - self.adapter, - character_id, - data['name'], - data['corporation_id'], - alliance_id, - ) - return model + 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 @@ -158,19 +184,28 @@ class EveXmlProvider(EveProvider): def get_alliance(self, id): api = evelink.eve.EVE(api=self.api) alliances = api.alliances().result - results = alliances[int(id)] + 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) - corpinfo = api.corporation_sheet(corp_id=int(id)).result + 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, @@ -184,7 +219,12 @@ class EveXmlProvider(EveProvider): def get_character(self, id): api = evelink.eve.EVE(api=self.api) - charinfo = api.character_info_from_id(id).result + 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 model = Character( self.adapter, id, diff --git a/eveonline/tasks.py b/eveonline/tasks.py index 894e2dfc..8d98bbea 100644 --- a/eveonline/tasks.py +++ b/eveonline/tasks.py @@ -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 @@ -105,65 +106,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 +122,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 +168,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 +179,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 +210,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) From 604808b195cda5e43053c075608401ab149b63c6 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Mon, 19 Dec 2016 20:56:14 -0500 Subject: [PATCH 17/19] Retrieve character objects from api keys. --- eveonline/managers.py | 10 ++++++++-- eveonline/providers.py | 18 ++++++++++-------- eveonline/tasks.py | 21 ++++++++------------- eveonline/views.py | 5 ++--- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/eveonline/managers.py b/eveonline/managers.py index e8ee3d4b..5036ca1c 100644 --- a/eveonline/managers.py +++ b/eveonline/managers.py @@ -3,7 +3,7 @@ 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 +from eveonline.providers import eve_adapter_factory, EveXmlProvider from services.managers.eve_api_manager import EveApiManager import logging @@ -33,7 +33,7 @@ class EveManager: ) @staticmethod - def create_character(character_id, character_name, corporation_id, + def create_xml_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)) @@ -184,6 +184,12 @@ class EveManager: 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): if EveApiKeyPair.objects.filter(user=user).exists(): diff --git a/eveonline/providers.py b/eveonline/providers.py index be71244a..861ed39f 100644 --- a/eveonline/providers.py +++ b/eveonline/providers.py @@ -217,6 +217,15 @@ class EveXmlProvider(EveProvider): ) 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: @@ -225,14 +234,7 @@ class EveXmlProvider(EveProvider): if int(e.code) == 105: raise ObjectNotFound(id, 'character') raise e - model = Character( - self.adapter, - id, - charinfo['name'], - charinfo['corp']['id'], - charinfo['alliance']['id'], - ) - return model + return self._build_character(charinfo) class EveAdapter(EveProvider): diff --git a/eveonline/tasks.py b/eveonline/tasks.py index 8d98bbea..10ed57cf 100644 --- a/eveonline/tasks.py +++ b/eveonline/tasks.py @@ -27,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: diff --git a/eveonline/views.py b/eveonline/views.py index 27a958fe..d1aec33c 100755 --- a/eveonline/views.py +++ b/eveonline/views.py @@ -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_with_id(c.id, 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']) From 55f349c35bbd0ee5f2e56f8eb044e4f9b821f0da Mon Sep 17 00:00:00 2001 From: Adarnof Date: Mon, 19 Dec 2016 21:07:45 -0500 Subject: [PATCH 18/19] Remove legacy xml functions. --- eveonline/managers.py | 55 ------------------------------------------- eveonline/views.py | 2 +- 2 files changed, 1 insertion(+), 56 deletions(-) diff --git a/eveonline/managers.py b/eveonline/managers.py index 5036ca1c..5638cbce 100644 --- a/eveonline/managers.py +++ b/eveonline/managers.py @@ -32,42 +32,6 @@ class EveManager: api_id = api_id, ) - @staticmethod - def create_xml_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) - - @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) - @staticmethod def update_character(id): return EveManager.update_character_obj(adapter.get_character(id)) @@ -82,25 +46,6 @@ class EveManager: model.alliance_name = char.alliance.name model.save() - @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']) - @staticmethod def create_api_keypair(api_id, api_key, user_id): logger.debug("Creating api keypair id %s for user_id %s" % (api_id, user_id)) diff --git a/eveonline/views.py b/eveonline/views.py index d1aec33c..b377a2e1 100755 --- a/eveonline/views.py +++ b/eveonline/views.py @@ -47,7 +47,7 @@ def add_api_key(request): owner = request.user # Grab characters associated with the key pair characters = EveManager.get_characters_from_api(api_key) - [EveManager.create_character_with_id(c.id, owner, api_key.api_id) for c in characters if not EveCharacter.objects.filter(character_id=c.id).exists()] + [EveManager.create_character(c.id, 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']) From 2a87eed059ccc68609805122513f5d1b7bf81474 Mon Sep 17 00:00:00 2001 From: Adarnof Date: Mon, 19 Dec 2016 21:13:45 -0500 Subject: [PATCH 19/19] Reduce API calls to speed up API checks --- eveonline/views.py | 2 +- services/managers/eve_api_manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/eveonline/views.py b/eveonline/views.py index b377a2e1..24457e1b 100755 --- a/eveonline/views.py +++ b/eveonline/views.py @@ -47,7 +47,7 @@ def add_api_key(request): owner = request.user # Grab characters associated with the key pair characters = EveManager.get_characters_from_api(api_key) - [EveManager.create_character(c.id, owner, api_key.api_id) for c in characters if not EveCharacter.objects.filter(character_id=c.id).exists()] + [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']) diff --git a/services/managers/eve_api_manager.py b/services/managers/eve_api_manager.py index a61a3c6b..840442c9 100644 --- a/services/managers/eve_api_manager.py +++ b/services/managers/eve_api_manager.py @@ -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']