Adarnof 1b4f5e4e88 Adarnof's Little Things (#547)
* Port to Django 1.10
Initial migrations for current states of all models. Requires faking to retain data.
Removed all references to render_to_response, replacing with render shortcut.
Same for HttpResponseRedirect to render shortcut.
Corrected notification signal import to wait for app registry to finish loading.

* Correct typos from render conversion

* Modify models to suppress Django field warnings

* Script for automatic database conversion
 - fakes initial migrations to preserve data
Include LOGIN_URL setting

* Correct context processor import typo

* Removed pathfinder support.
Current pathfinder versions require SSO, not APIs added to database.
Conditionally load additional database definitions only if services are enabled.
Prevents errors when running auth without creating all possible databases.

* Condense context processors

* Include Django 1.10 installation in migrate script
Remove syncdb/evolve, replace with migrate for update script

* Replaced member/blue perms with user state system
Removed sigtracker
Initial migrations for default perms and groups
Removed perm bootstrapping on first run

* Clean up services list

* Remove fleet fittings page

* Provide action feedback via django messaging
Display unread notification count
Correct left navbar alignment

* Stop storing service passwords.
Provide them one time upon activation or reset.
Closes #177

* Add group sync buttons to admin site
Allow searcing of AuthServicesInfo models
Display user main character

* Correct button CSS to remove underlines on hover

* Added bulk actions to notifications
Altered notification default ordering

* Centralize API key validation.
Remove unused error count on API key model.
Restructure API key refresh task to queue all keys per user and await completion.
Closes #350

* Example configuration files for supervisor.
Copy to /etc/supervisor/conf.d and restart to take effect.
Closes #521
Closes #266

* Pre-save receiver for member/blue state switching
Removed is_blue field
Added link to admin site

* Remove all hardcoded URLs from views and templates
Correct missing render arguments
Closes #540

* Correct celeryd process directory

* Migration to automatically set user states.
Runs instead of waiting for next API refresh cycle. Should make the transition much easier.

* Verify service accounts accessible to member state

* Restructure project to remove unnecessary apps.
(celerytask, util, portal, registraion apps)
Added workarounds for python 3 compatibility.

* Correct python2 compatibility

* Check services against state being changed to

* Python3 compatibility fixes

* Relocate x2bool py3 fix

* SSO integration for logging in to existing accounts.

* Add missing url names for fleetup reverse

* Sanitize groupnames before syncing.

* Correct trailing slash preventing url resolution

* Alter group name sanitization to allow periods and hyphens

* Correct state check on pre_save model for corp/alliance group assignment

* Remove sigtracker table from old dbs to allow user deletion

* Include missing celery configuration

* Teamspeak error handling

* Prevent celery worker deadlock on async group result wait

* Correct active navbar links for translated urls.
Correct corp status url resolution for some links.
Remove DiscordAuthToken model.
2016-10-16 18:01:14 -04:00

347 lines
18 KiB
Python

from __future__ import unicode_literals
from django.conf import settings
from celery.task import periodic_task
from django.contrib.auth.models import User
from notifications import notify
from celery import task
from celery.task.schedules import crontab
from authentication.models import AuthServicesInfo
from eveonline.managers import EveManager
from eveonline.models import EveApiKeyPair
from services.managers.eve_api_manager import EveApiManager
from eveonline.models import EveCharacter
from eveonline.models import EveCorporationInfo
from eveonline.models import EveAllianceInfo
from authentication.tasks import set_state
import logging
import evelink
logger = logging.getLogger(__name__)
@task
def refresh_api(api):
logger.debug('Running update on api key %s' % api.api_id)
still_valid = True
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)
current_chars = EveCharacter.objects.filter(api_id=api.api_id)
for c in current_chars:
if not int(c.character_id) in characters.result:
logger.info("Character %s no longer found on API ID %s" % (c, api.api_id))
c.delete()
except evelink.api.APIError as e:
logger.warning('Received unexpected APIError (%s) while updating API %s' % (e.code, api.api_id))
except EveApiManager.ApiInvalidError:
logger.debug("API key %s is no longer valid; it and its characters will be deleted." % api.api_id)
notify(api.user, "API Failed Validation", message="Your API key ID %s is no longer valid." % api.api_id,
level="danger")
still_valid = False
except EveApiManager.ApiAccountValidationError:
logger.info(
"Determined api key %s for user %s no longer meets account access requirements." % (api.api_id, api.user))
notify(api.user, "API Failed Validation",
message="Your API key ID %s is no longer account-wide as required." % api.api_id, level="danger")
still_valid = False
except EveApiManager.ApiMaskValidationError as e:
logger.info("Determined api key %s for user %s no longer meets minimum access mask as required." % (
api.api_id, api.user))
notify(api.user, "API Failed Validation",
message="Your API key ID %s no longer meets access mask requirements. Required: %s Got: %s" % (
api.api_id, e.required_mask, e.api_mask), level="danger")
still_valid = False
finally:
if not still_valid:
EveManager.delete_characters_by_api_id(api.api_id, api.user.id)
EveManager.delete_api_key_pair(api.api_id, api.user.id)
notify(api.user, "API Key Deleted",
message="Your API key ID %s is invalid. It and its associated characters have been deleted." % api.api_id,
level="danger")
@task
def refresh_user_apis(user):
logger.debug('Refreshing all APIs belonging to user %s' % user)
apis = EveApiKeyPair.objects.filter(user=user)
for x in apis:
refresh_api(x)
# Check our main character
auth = AuthServicesInfo.objects.get_or_create(user=user)[0]
if auth.main_char_id:
if EveCharacter.objects.filter(character_id=auth.main_char_id).exists() is False:
logger.info(
"User %s main character id %s missing model. Clearning main character." % (user, auth.main_char_id))
auth.main_char_id = ''
auth.save()
notify(user, "Main Character Reset",
message="Your specified main character no longer has a model.\nThis could be the result of "
"an invalid API.\nYour main character ID has been reset.",
level="warn")
set_state(user)
@periodic_task(run_every=crontab(minute=0, hour="*/3"))
def run_api_refresh():
for u in User.objects.all():
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_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
@periodic_task(run_every=crontab(minute=0, hour="*/2"))
def run_corp_update():
if EveApiManager.check_if_api_server_online() is False:
logger.warn("Aborted updating corp and alliance models: API server unreachable")
return
standing_level = 'alliance'
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:
standings = standings[standing_level]
for standing in standings:
if int(standings[standing]['standing']) >= settings.BLUE_STANDING:
logger.debug("Standing %s meets threshold" % standing)
if EveApiManager.check_if_id_is_alliance(standing):
logger.debug("Standing %s is an alliance" % standing)
if EveAllianceInfo.objects.filter(alliance_id=standing).exists():
alliance = EveAllianceInfo.objects.get(alliance_id=standing)
if alliance.is_blue is not True:
logger.info("Updating alliance %s as blue" % alliance)
alliance.is_blue = True
alliance.save()
else:
populate_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():
corp = EveCorporationInfo.objects.get(corporation_id=standing)
if corp.is_blue is not True:
logger.info("Updating corp %s as blue" % corp)
corp.is_blue = True
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)
# update alliance standings
for alliance in EveAllianceInfo.objects.filter(is_blue=True):
if int(alliance.alliance_id) in standings:
if float(standings[int(alliance.alliance_id)]['standing']) < float(settings.BLUE_STANDING):
logger.info("Alliance %s no longer meets minimum blue standing threshold" % alliance)
alliance.is_blue = False
alliance.save()
else:
logger.info("Alliance %s no longer in standings" % alliance)
alliance.is_blue = False
alliance.save()
# update corp standings
for corp in EveCorporationInfo.objects.filter(is_blue=True):
if int(corp.corporation_id) in standings:
if float(standings[int(corp.corporation_id)]['standing']) < float(settings.BLUE_STANDING):
logger.info("Corp %s no longer meets minimum blue standing threshold" % corp)
corp.is_blue = False
corp.save()
else:
if corp.alliance:
if not corp.alliance.is_blue:
logger.info("Corp %s and its alliance %s are no longer blue" % (corp, corp.alliance))
corp.is_blue = False
corp.save()
else:
logger.info("Corp %s is no longer blue" % corp)
corp.is_blue = False
corp.save()
# 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:
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)