mirror of
https://github.com/nikdoof/test-auth.git
synced 2025-12-14 06:42:16 +00:00
Initial import
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
0
__init__.py
Normal file
0
__init__.py
Normal file
0
eve_api/__init__.py
Normal file
0
eve_api/__init__.py
Normal file
32
eve_api/admin.py
Normal file
32
eve_api/admin.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Admin interface models. Automatically detected by admin.autodiscover().
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from eve_api.models import *
|
||||
|
||||
class EVEAccountAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'user')
|
||||
search_fields = ['id']
|
||||
admin.site.register(EVEAccount, EVEAccountAdmin)
|
||||
|
||||
class EVEPlayerCharacterAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'name', 'corporation')
|
||||
search_fields = ['id', 'name']
|
||||
admin.site.register(EVEPlayerCharacter, EVEPlayerCharacterAdmin)
|
||||
|
||||
class EVEPlayerCorporationInline(admin.TabularInline):
|
||||
model = EVEPlayerCorporation
|
||||
fields = ('name', 'ticker')
|
||||
extra = 0
|
||||
|
||||
class EVEPlayerAllianceAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'name', 'ticker', 'member_count', 'date_founded')
|
||||
search_fields = ['name', 'ticker']
|
||||
date_hierarchy = 'date_founded'
|
||||
inlines = [EVEPlayerCorporationInline]
|
||||
admin.site.register(EVEPlayerAlliance, EVEPlayerAllianceAdmin)
|
||||
|
||||
class EVEPlayerCorporationAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'name', 'ticker', 'member_count', 'alliance')
|
||||
search_fields = ['name', 'ticker']
|
||||
admin.site.register(EVEPlayerCorporation, EVEPlayerCorporationAdmin)
|
||||
16
eve_api/api_exceptions.py
Normal file
16
eve_api/api_exceptions.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Contains exeptions used in the eve_api app.
|
||||
"""
|
||||
class APIAuthException(Exception):
|
||||
"""
|
||||
Raised when an invalid userID and/or authKey were provided.
|
||||
"""
|
||||
def __str__(self):
|
||||
return "An authentication was encountered while querying the EVE API."
|
||||
|
||||
class APINoUserIDException(Exception):
|
||||
"""
|
||||
Raised when a userID is required, but missing.
|
||||
"""
|
||||
def __str__(self):
|
||||
return "This query requires a valid userID, but yours is either missing or invalid."
|
||||
0
eve_api/api_puller/__init__.py
Normal file
0
eve_api/api_puller/__init__.py
Normal file
72
eve_api/api_puller/accounts.py
Executable file
72
eve_api/api_puller/accounts.py
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
This module abstracts the pulling of account data from the EVE API.
|
||||
"""
|
||||
from xml.dom import minidom
|
||||
from datetime import datetime
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Only mess with the environmental stuff if this is being ran directly.
|
||||
from importer_path import fix_environment
|
||||
fix_environment()
|
||||
|
||||
from django.conf import settings
|
||||
from eve_proxy.models import CachedDocument
|
||||
from eve_api.api_exceptions import APIAuthException, APINoUserIDException
|
||||
from eve_api.models import EVEAccount, EVEPlayerCharacter, EVEPlayerCorporation
|
||||
|
||||
def import_eve_account(api_key, user_id):
|
||||
"""
|
||||
Imports an account from the API into the EVEAccount model.
|
||||
"""
|
||||
print user_id, ":", api_key
|
||||
auth_params = {'userID': user_id, 'apiKey': api_key}
|
||||
account_doc = CachedDocument.objects.api_query('/account/Characters.xml.aspx',
|
||||
params=auth_params,
|
||||
no_cache=False)
|
||||
#print account_doc.body
|
||||
|
||||
dom = minidom.parseString(account_doc.body)
|
||||
characters_node_children = dom.getElementsByTagName('rowset')[0].childNodes
|
||||
|
||||
# Create or retrieve the account last to make sure everything
|
||||
# before here is good to go.
|
||||
try:
|
||||
account = EVEAccount.objects.get(id=user_id)
|
||||
except EVEAccount.DoesNotExist:
|
||||
account = EVEAccount(id=user_id)
|
||||
|
||||
account.api_key = api_key
|
||||
account.api_user_id = user_id
|
||||
account.api_last_updated = datetime.now()
|
||||
account.save()
|
||||
|
||||
for node in characters_node_children:
|
||||
try:
|
||||
# Get this first, as it's safe.
|
||||
corporation_id = node.getAttribute('corporationID')
|
||||
corp, created = EVEPlayerCorporation.objects.get_or_create(id=corporation_id)
|
||||
# Do this last, since the things we retrieved above are used
|
||||
# on the EVEPlayerCharacter object's fields.
|
||||
character_id = node.getAttribute('characterID')
|
||||
pchar, created = EVEPlayerCharacter.objects.get_or_create(id=character_id)
|
||||
name = node.getAttribute('name')
|
||||
# Save these for last to keep the save count low.
|
||||
pchar.name = name
|
||||
pchar.corporation = corp
|
||||
pchar.save()
|
||||
account.characters.add(pchar)
|
||||
except AttributeError:
|
||||
# This must be a Text node, ignore it.
|
||||
continue
|
||||
|
||||
return account
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Test import.
|
||||
"""
|
||||
api_key = settings.EVE_API_USER_KEY
|
||||
#api_key += "1"
|
||||
user_id = settings.EVE_API_USER_ID
|
||||
import_eve_account(api_key, user_id)
|
||||
130
eve_api/api_puller/alliances.py
Executable file
130
eve_api/api_puller/alliances.py
Executable file
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
This module pulls the master alliance XML list from the API and dumps it in the
|
||||
api_puller/xml_cache directory as needed. All alliance data must be updated
|
||||
in bulk, which is done reasonably quickly.
|
||||
"""
|
||||
from xml.dom import minidom
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Only mess with the environmental stuff if this is being ran directly.
|
||||
from importer_path import fix_environment
|
||||
fix_environment()
|
||||
|
||||
from django.conf import settings
|
||||
from eve_api.models import EVEPlayerAlliance, EVEPlayerCorporation
|
||||
from eve_proxy.models import CachedDocument
|
||||
|
||||
# This stores a list of all corps whose alliance attribute has been updated.
|
||||
UPDATED_CORPS = []
|
||||
|
||||
def __update_corp_from_alliance_node(alliance_node, alliance):
|
||||
"""
|
||||
Updates a corp's alliance membership from an alliance <row> element.
|
||||
"""
|
||||
member_corp_nodelist = alliance_node.getElementsByTagName('rowset')[0].childNodes
|
||||
|
||||
for node in member_corp_nodelist:
|
||||
corp_row_node = None
|
||||
try:
|
||||
# If this fails, this is a Text node and should be ignored.
|
||||
corporation_id = int(node.getAttribute('corporationID'))
|
||||
except AttributeError:
|
||||
# This is probably a Text node, ignore it.
|
||||
continue
|
||||
|
||||
corp, created = EVEPlayerCorporation.objects.get_or_create(id=corporation_id)
|
||||
corp.id = corporation_id
|
||||
corp.alliance = alliance
|
||||
corp.alliance_join_date = datetime.strptime(alliance_node.getAttribute('startDate'),
|
||||
'%Y-%m-%d %H:%M:%S')
|
||||
corp.save()
|
||||
# Store the corp in the updated corps list for later checks.
|
||||
UPDATED_CORPS.append(corp.id)
|
||||
|
||||
def __remove_invalid_corp_alliance_memberships():
|
||||
"""
|
||||
Compares UPDATED_CORPS list to the full list of player corporations. If
|
||||
the corporation was not updated from being found in one of the alliance
|
||||
data sets, it has no alliance affiliation and needs to be set to no
|
||||
alliance if it is not already a None value.
|
||||
"""
|
||||
all_corps = EVEPlayerCorporation.objects.all()
|
||||
# This is not terribly efficient, but it will do for a background process.
|
||||
for corp in all_corps:
|
||||
"""
|
||||
If the corp is not in the UPDATED_CORP list that was built from
|
||||
alliance memberCorporations rowsets, then it does not belong to an
|
||||
alliance and should be un-allianced if it currently is.
|
||||
"""
|
||||
if corp.id not in UPDATED_CORPS and corp.alliance != None:
|
||||
corp.alliance = None
|
||||
corp.save()
|
||||
|
||||
def __start_full_import():
|
||||
"""
|
||||
This method runs a full import of all known alliances. This may take a few
|
||||
minutes and should be ran regularly if you are maintaining a full corp
|
||||
list of all EVE corps as well.
|
||||
"""
|
||||
print "Querying /eve/AllianceList.xml.aspx/"
|
||||
alliance_doc = CachedDocument.objects.api_query('/eve/AllianceList.xml.aspx')
|
||||
print "Parsing..."
|
||||
dom = minidom.parseString(alliance_doc.body)
|
||||
result_node_children = dom.getElementsByTagName('result')[0].childNodes
|
||||
|
||||
# This will hold a reference to the <rowset name="alliances> Element.
|
||||
alliances_rowset_node = None
|
||||
# For some odd reason, two text nodes and an Element are children of
|
||||
# the result Element. Find the alliances rowset from its children.
|
||||
for node in result_node_children:
|
||||
try:
|
||||
# The node we want has a 'name' attribute.
|
||||
if node.getAttribute('name') == 'alliances':
|
||||
# Store the reference for later use.
|
||||
alliances_rowset_node = node
|
||||
# Look no further.
|
||||
break
|
||||
except AttributeError:
|
||||
# This must be a Text node, ignore it.
|
||||
continue
|
||||
|
||||
if alliances_rowset_node == None:
|
||||
print "No alliance rowset node could be found. Your AllianceList.xml file may be corrupt."
|
||||
sys.exit(1)
|
||||
|
||||
# We now have a list of <row> tags representing each alliance.
|
||||
print "Updating alliance and member corporation data..."
|
||||
for alliance_node in alliances_rowset_node.childNodes:
|
||||
try:
|
||||
# If this fails, this is a Text node and should be ignored.
|
||||
alliance_id = int(alliance_node.getAttribute('allianceID'))
|
||||
except AttributeError:
|
||||
# This is probably a Text node, ignore it.
|
||||
continue
|
||||
|
||||
"""
|
||||
Search for an existing EVEPlayerAlliance object with the given
|
||||
alliance ID. Create one if it doesn't exist, retrieve the existing
|
||||
object if it's already there.
|
||||
"""
|
||||
alliance, created = EVEPlayerAlliance.objects.get_or_create(id=alliance_id)
|
||||
alliance.id = alliance_id
|
||||
alliance.name = alliance_node.getAttribute('name')
|
||||
alliance.ticker = alliance_node.getAttribute('shortName')
|
||||
alliance.member_count = alliance_node.getAttribute('memberCount')
|
||||
alliance.date_founded = datetime.strptime(alliance_node.getAttribute('startDate'),
|
||||
'%Y-%m-%d %H:%M:%S')
|
||||
alliance.save()
|
||||
# Update member corp alliance attributes.
|
||||
__update_corp_from_alliance_node(alliance_node, alliance)
|
||||
|
||||
print "Alliances and member corps updated."
|
||||
print "Removing corps alliance memberships that are no longer valid..."
|
||||
__remove_invalid_corp_alliance_memberships()
|
||||
|
||||
if __name__ == "__main__":
|
||||
__start_full_import()
|
||||
55
eve_api/api_puller/corps.py
Executable file
55
eve_api/api_puller/corps.py
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Module for updating corp information. If this is ran directly, the module will
|
||||
iterate through all known alliances, looking at the corps in each alliance's
|
||||
member list. This can be very time-consuming and should not be done often.
|
||||
|
||||
Within your applications, you may call query_and_update_corp() to update
|
||||
an individual corp object as need be.
|
||||
|
||||
NOTE: To get corp data, it must be a member of an alliance.
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Only mess with the environmental stuff if this is being ran directly.
|
||||
from importer_path import fix_environment
|
||||
fix_environment()
|
||||
|
||||
from eve_api.models import EVEPlayerAlliance, EVEPlayerCorporation
|
||||
|
||||
def start_full_import():
|
||||
"""
|
||||
Imports all of the corps that are in all of the known alliances.
|
||||
|
||||
WARNING: THIS WILL TAKE A _LONG_ TIME AND MUST BE RAN AFTER
|
||||
eve_db.api_puller.alliances.__start_full_import() OR YOU WON'T GET ALL
|
||||
OF THE CORPS (or any at all).
|
||||
"""
|
||||
alliances = EVEPlayerAlliance.objects.all()
|
||||
|
||||
# These two variables are used to track progress.
|
||||
alliance_count = alliances.count()
|
||||
# Use this as a progress indicator.
|
||||
current_alliance_num = 1
|
||||
|
||||
for alliance in alliances:
|
||||
# Keep the user informed as to the progress.
|
||||
print "Alliance %d of %d..." % (current_alliance_num, alliance_count)
|
||||
# A list of the alliance's member corps.
|
||||
member_corps = alliance.eveplayercorporation_set.all()
|
||||
# We're getting the list of corps to update from alliance memberships.
|
||||
for corp in member_corps:
|
||||
print "Querying", corp.id
|
||||
corp.query_and_update_corp()
|
||||
|
||||
# Increment progress counter.
|
||||
current_alliance_num += 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
If ran directly, this will grab all of the corps from the known alliances.
|
||||
|
||||
WARNING: THIS WILL TAKE A VERY LONG TIME TO RUN! IT IS SUGGESTED YOU ONLY
|
||||
GRAB CORPS AS YOU NEED THEM.
|
||||
"""
|
||||
start_full_import()
|
||||
16
eve_api/api_puller/importer_path.py
Normal file
16
eve_api/api_puller/importer_path.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import os
|
||||
import sys
|
||||
# The path to the folder containing settings.py.
|
||||
BASE_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
APPS_PATH = os.path.join(BASE_PATH, 'apps')
|
||||
|
||||
def fix_environment():
|
||||
"""
|
||||
Callable function to set up all of the Django environmental variables and
|
||||
pathing for directly executable python modules.
|
||||
"""
|
||||
from importer_path import BASE_PATH
|
||||
# Prepare the environment
|
||||
sys.path.insert(0, APPS_PATH)
|
||||
sys.path.insert(0, BASE_PATH)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
||||
15
eve_api/app_defines.py
Normal file
15
eve_api/app_defines.py
Normal file
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
Standard definitions that don't change.
|
||||
"""
|
||||
# API status definitions for EVEAccount.
|
||||
API_STATUS_PENDING = 0
|
||||
API_STATUS_OK = 1
|
||||
API_STATUS_AUTH_ERROR = 2
|
||||
API_STATUS_OTHER_ERROR = 3
|
||||
# This tuple is used to assemble the choices list for the field.
|
||||
API_STATUS_CHOICES = (
|
||||
(API_STATUS_PENDING, 'Unknown'),
|
||||
(API_STATUS_OK, 'OK'),
|
||||
(API_STATUS_AUTH_ERROR, 'Auth Error'),
|
||||
(API_STATUS_OTHER_ERROR, 'Other Error'),
|
||||
)
|
||||
96
eve_api/managers.py
Normal file
96
eve_api/managers.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from xml.dom import minidom
|
||||
from django.db import models
|
||||
from eve_proxy.models import CachedDocument
|
||||
|
||||
class InvalidCorpID(Exception):
|
||||
"""
|
||||
Thrown when an invalid corp id is given in an api query.
|
||||
"""
|
||||
def __init__(self, id):
|
||||
self.value = "ID: %s does not match any corporation." % id
|
||||
|
||||
def __str___(self):
|
||||
return repr(self.value)
|
||||
|
||||
def _api_get_id_from_name(name):
|
||||
"""
|
||||
Queries the EVE API looking for the ID of the specified corporation,
|
||||
alliance, or character based on its name. This is not case sensitive.
|
||||
|
||||
name: (str) Corporation name to search for.
|
||||
"""
|
||||
query_doc = CachedDocument.objects.api_query('/eve/CharacterID.xml.aspx',
|
||||
params={'names': name})
|
||||
query_dat = query_doc.body.decode("utf-8", "replace")
|
||||
dom = minidom.parseString(query_dat)
|
||||
|
||||
id_node = dom.getElementsByTagName('row')[0]
|
||||
object_id = id_node.getAttribute('characterID')
|
||||
|
||||
if object_id == '0':
|
||||
raise self.model.DoesNotExist('EVE API returned no matches for the provided corp name.')
|
||||
else:
|
||||
return int(object_id)
|
||||
|
||||
class EVEPlayerCharacterManager(models.Manager):
|
||||
def api_get_id_from_name(self, name):
|
||||
"""
|
||||
This uses a common call for corps, characters, and alliances.
|
||||
"""
|
||||
return _api_get_id_from_name(name)
|
||||
|
||||
class EVEPlayerAllianceManager(models.Manager):
|
||||
def api_get_id_from_name(self, name):
|
||||
"""
|
||||
This uses a common call for corps, characters, and alliances.
|
||||
"""
|
||||
return _api_get_id_from_name(name)
|
||||
|
||||
class EVEPlayerCorporationManager(models.Manager):
|
||||
def get_or_query_by_id(self, corp_id):
|
||||
"""
|
||||
Queries for a corporation. If the corp can't be founded, check the
|
||||
EVE API service for information on it. If a match still can't be
|
||||
found, return EVEPlayerCorporation.DoesNotExist.
|
||||
|
||||
corp_id: (int) Corp's ID.
|
||||
"""
|
||||
try:
|
||||
return self.get(id=corp_id)
|
||||
except self.model.DoesNotExist:
|
||||
try:
|
||||
self.api_corp_sheet_xml(corp_id)
|
||||
new_corp = self.create(id=corp_id)
|
||||
new_corp.query_and_update_corp()
|
||||
return new_corp
|
||||
except InvalidCorpID:
|
||||
raise
|
||||
|
||||
def api_get_id_from_name(self, name):
|
||||
"""
|
||||
This uses a common call for corps, characters, and alliances.
|
||||
"""
|
||||
return _api_get_id_from_name(name)
|
||||
|
||||
def api_corp_sheet_xml(self, id):
|
||||
"""
|
||||
Returns a corp's data sheet from the EVE API in the form of an XML
|
||||
minidom doc.
|
||||
"""
|
||||
corp_doc = CachedDocument.objects.api_query('/corp/CorporationSheet.xml.aspx',
|
||||
params={'corporationID': id})
|
||||
corp_dat = corp_doc.body.decode("utf-8", "replace")
|
||||
|
||||
# Convert incoming data to UTF-8.
|
||||
dom = minidom.parseString(corp_dat)
|
||||
|
||||
error_node = dom.getElementsByTagName('error')
|
||||
|
||||
# If there's an error, see if it's because the corp doesn't exist.
|
||||
if error_node:
|
||||
error_code = error_node[0].getAttribute('code')
|
||||
if error_code == '523':
|
||||
raise InvalidCorpID(id)
|
||||
|
||||
return dom
|
||||
|
||||
6
eve_api/models/__init__.py
Normal file
6
eve_api/models/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
By importing all of these sub-modules, the models package is transparently
|
||||
accessible by the rest of the project. This makes it act just as if it were
|
||||
one monolithic models.py.
|
||||
"""
|
||||
from api_player import *
|
||||
195
eve_api/models/api_player.py
Normal file
195
eve_api/models/api_player.py
Normal file
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
This module holds data from the EVE XML API.
|
||||
"""
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from eve_proxy.models import CachedDocument
|
||||
from eve_api.managers import EVEPlayerCorporationManager, EVEPlayerAllianceManager, EVEPlayerCharacterManager
|
||||
from eve_api.app_defines import API_STATUS_CHOICES, API_STATUS_PENDING
|
||||
|
||||
class EVEAPIModel(models.Model):
|
||||
"""
|
||||
A simple abstract base class to set some consistent fields on the models
|
||||
that are updated from the EVE API.
|
||||
"""
|
||||
api_last_updated = models.DateTimeField(blank=True, null=True,
|
||||
verbose_name="Time last updated from API",
|
||||
help_text="When this object was last updated from the EVE API.")
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
class EVEAccount(EVEAPIModel):
|
||||
"""
|
||||
Use this class to store EVE user account information. Note that its use is
|
||||
entirely optional and up to the developer's discretion.
|
||||
"""
|
||||
user = models.ForeignKey(User, blank=True, null=True,
|
||||
help_text="User that owns this account")
|
||||
description = models.CharField(max_length=50, blank=True,
|
||||
help_text="User-provided description.")
|
||||
api_key = models.CharField(max_length=64, verbose_name="API Key")
|
||||
api_user_id = models.IntegerField(verbose_name="API User ID")
|
||||
characters = models.ManyToManyField("EVEPlayerCharacter", blank=True,
|
||||
null=True)
|
||||
api_status = models.IntegerField(choices=API_STATUS_CHOICES,
|
||||
default=API_STATUS_PENDING,
|
||||
verbose_name="API Status",
|
||||
help_text="End result of the last attempt at updating this object from the API.")
|
||||
|
||||
def in_corp(self, corpid):
|
||||
for char in self.characters.all():
|
||||
if char.corporation_id == corpid:
|
||||
return True
|
||||
return False
|
||||
|
||||
class Meta:
|
||||
app_label = 'eve_api'
|
||||
verbose_name = 'EVE Account'
|
||||
verbose_name_plural = 'EVE Accounts'
|
||||
ordering = ['api_user_id']
|
||||
|
||||
class EVEPlayerCharacter(EVEAPIModel):
|
||||
"""
|
||||
Represents an individual player character within the game. Not to be
|
||||
confused with an account.
|
||||
"""
|
||||
name = models.CharField(max_length=255, blank=True, null=False)
|
||||
corporation = models.ForeignKey('EVEPlayerCorporation', blank=True, null=True)
|
||||
# TODO: Choices field
|
||||
race = models.IntegerField(blank=True, null=True)
|
||||
# TODO: Choices field
|
||||
gender = models.IntegerField(blank=True, null=True)
|
||||
balance = models.FloatField("Account Balance", blank=True, null=True)
|
||||
attrib_intelligence = models.IntegerField("Intelligence", blank=True,
|
||||
null=True)
|
||||
attrib_memory = models.IntegerField("Memory", blank=True, null=True)
|
||||
attrib_charisma = models.IntegerField("Charisma", blank=True, null=True)
|
||||
attrib_perception = models.IntegerField("Perception", blank=True, null=True)
|
||||
attrib_willpower = models.IntegerField("Willpower", blank=True, null=True)
|
||||
|
||||
objects = EVEPlayerCharacterManager()
|
||||
|
||||
def __unicode__(self):
|
||||
if self.name:
|
||||
return "%s (%d)" % (self.name, self.id)
|
||||
else:
|
||||
return "(%d)" % self.id
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
class Meta:
|
||||
app_label = 'eve_api'
|
||||
verbose_name = 'Player Character'
|
||||
verbose_name_plural = 'Player Characters'
|
||||
|
||||
class EVEPlayerAlliance(EVEAPIModel):
|
||||
"""
|
||||
Represents a player-controlled alliance. Updated from the alliance
|
||||
EVE XML API puller at intervals.
|
||||
"""
|
||||
name = models.CharField(max_length=255, blank=True, null=False)
|
||||
ticker = models.CharField(max_length=15, blank=True, null=False)
|
||||
#executor_character = models.ForeignKey(EVECharacter, blank=True, null=False)
|
||||
member_count = models.IntegerField(blank=True, null=True)
|
||||
date_founded = models.DateField(blank=True, null=True)
|
||||
|
||||
objects = EVEPlayerAllianceManager()
|
||||
|
||||
class Meta:
|
||||
app_label = 'eve_api'
|
||||
ordering = ['date_founded']
|
||||
verbose_name = 'Player Alliance'
|
||||
verbose_name_plural = 'Player Alliances'
|
||||
|
||||
def __unicode__(self):
|
||||
if self.name:
|
||||
return "%s (%d)" % (self.name, self.id)
|
||||
else:
|
||||
return "(#%d)" % self.id
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
class EVEPlayerCorporation(EVEAPIModel):
|
||||
"""
|
||||
Represents a player-controlled corporation. Updated from a mixture of
|
||||
the alliance and corporation API pullers.
|
||||
"""
|
||||
name = models.CharField(max_length=255, blank=True, null=True)
|
||||
ticker = models.CharField(max_length=15, blank=True, null=True)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
url = models.URLField(verify_exists=False, blank=True, null=True)
|
||||
ceo_character = models.ForeignKey(EVEPlayerCharacter, blank=True, null=True)
|
||||
#home_station = models.ForeignKey(EVEStation, blank=True, null=False)
|
||||
alliance = models.ForeignKey(EVEPlayerAlliance, blank=True, null=True)
|
||||
alliance_join_date = models.DateField(blank=True, null=True)
|
||||
tax_rate = models.FloatField(blank=True, null=True)
|
||||
member_count = models.IntegerField(blank=True, null=True)
|
||||
shares = models.IntegerField(blank=True, null=True)
|
||||
|
||||
# Logo generation stuff
|
||||
logo_graphic_id = models.IntegerField(blank=True, null=True)
|
||||
logo_shape1 = models.IntegerField(blank=True, null=True)
|
||||
logo_shape2 = models.IntegerField(blank=True, null=True)
|
||||
logo_shape3 = models.IntegerField(blank=True, null=True)
|
||||
logo_color1 = models.IntegerField(blank=True, null=True)
|
||||
logo_color2 = models.IntegerField(blank=True, null=True)
|
||||
logo_color3 = models.IntegerField(blank=True, null=True)
|
||||
|
||||
objects = EVEPlayerCorporationManager()
|
||||
|
||||
class Meta:
|
||||
app_label = 'eve_api'
|
||||
verbose_name = 'Player Corporation'
|
||||
verbose_name_plural = 'Player Corporations'
|
||||
|
||||
def __str__(self):
|
||||
if self.name:
|
||||
return self.name
|
||||
else:
|
||||
return "Corp #%d" % self.id
|
||||
|
||||
def query_and_update_corp(self):
|
||||
"""
|
||||
Takes an EVEPlayerCorporation object and updates it from the
|
||||
EVE API service.
|
||||
"""
|
||||
# Pull XML from the EVE API via eve_proxy.
|
||||
dom = EVEPlayerCorporation.objects.api_corp_sheet_xml(self.id)
|
||||
|
||||
# Tuples of pairings of tag names and the attribute on the Corporation
|
||||
# object to set the data to.
|
||||
tag_mappings = (
|
||||
('corporationName', 'name'),
|
||||
('ticker', 'ticker'),
|
||||
('url', 'url'),
|
||||
('description', 'description'),
|
||||
('memberCount', 'member_count'),
|
||||
('graphicID', 'logo_graphic_id'),
|
||||
('shape1', 'logo_shape1'),
|
||||
('shape2', 'logo_shape2'),
|
||||
('shape3', 'logo_shape3'),
|
||||
('color1', 'logo_color1'),
|
||||
('color2', 'logo_color2'),
|
||||
('color3', 'logo_color3'),
|
||||
)
|
||||
|
||||
# Iterate through the tag mappings, setting the values of the tag names
|
||||
# (first member of the tuple) to the attribute named in the second member
|
||||
# of the tuple on the EVEPlayerCorporation object.
|
||||
for tag_map in tag_mappings:
|
||||
try:
|
||||
setattr(self, tag_map[1],
|
||||
dom.getElementsByTagName(tag_map[0])[0].firstChild.nodeValue)
|
||||
except AttributeError:
|
||||
# This tag has no value, skip it.
|
||||
continue
|
||||
except IndexError:
|
||||
# Something weird has happened
|
||||
print " * Index Error:", tag_map[0]
|
||||
continue
|
||||
|
||||
print "Updating", self.id, self.name
|
||||
self.save()
|
||||
9
eve_proxy/__init__.py
Executable file
9
eve_proxy/__init__.py
Executable file
@@ -0,0 +1,9 @@
|
||||
VERSION = (0, 4)
|
||||
|
||||
# Dynamically calculate the version based on VERSION tuple
|
||||
if len(VERSION)>2 and VERSION[2] is not None:
|
||||
str_version = "%d.%d_%s" % VERSION[:3]
|
||||
else:
|
||||
str_version = "%d.%d" % VERSION[:2]
|
||||
|
||||
__version__ = str_version
|
||||
9
eve_proxy/admin.py
Executable file
9
eve_proxy/admin.py
Executable file
@@ -0,0 +1,9 @@
|
||||
from django.contrib import admin
|
||||
from eve_proxy.models import CachedDocument
|
||||
|
||||
class CachedDocumentAdmin(admin.ModelAdmin):
|
||||
model = CachedDocument
|
||||
list_display = ('url_path', 'time_retrieved', 'cached_until')
|
||||
verbose_name = 'Cached Document'
|
||||
verbose_name_plural = 'Cached Documents'
|
||||
admin.site.register(CachedDocument, CachedDocumentAdmin)
|
||||
126
eve_proxy/models.py
Executable file
126
eve_proxy/models.py
Executable file
@@ -0,0 +1,126 @@
|
||||
import httplib
|
||||
import urllib
|
||||
import xml
|
||||
from datetime import datetime, timedelta
|
||||
from xml.dom import minidom
|
||||
from django.db import models
|
||||
from eve_api.api_exceptions import APIAuthException, APINoUserIDException
|
||||
|
||||
# You generally never want to change this unless you have a very good reason.
|
||||
API_URL = 'api.eve-online.com'
|
||||
|
||||
class CachedDocumentManager(models.Manager):
|
||||
"""
|
||||
This manager handles querying or retrieving CachedDocuments.
|
||||
"""
|
||||
def cache_from_eve_api(self, cached_doc, url_path, params, no_cache=False):
|
||||
"""
|
||||
Connect to the EVE API server, send the request, and cache it to
|
||||
a CachedDocument. This is typically not something you want to call
|
||||
directly. Use api_query().
|
||||
"""
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded"}
|
||||
# This is the connection to the EVE API server.
|
||||
conn = httplib.HTTPConnection(API_URL)
|
||||
# Combine everything into an HTTP request.
|
||||
conn.request("POST", url_path, params, headers)
|
||||
# Retrieve the response from the server.
|
||||
response = conn.getresponse()
|
||||
# Save the response (an XML document) to the CachedDocument.
|
||||
cached_doc.body = response.read()
|
||||
|
||||
try:
|
||||
# Parse the response via minidom
|
||||
dom = minidom.parseString(cached_doc.body)
|
||||
except xml.parsers.expat.ExpatError:
|
||||
print "XML Parser Error:"
|
||||
print cached_doc.body
|
||||
return
|
||||
|
||||
# Set the CachedDocument's time_retrieved and cached_until times based
|
||||
# on the values in the XML response. This will be used in future
|
||||
# requests to see if the CachedDocument can be retrieved directly or
|
||||
# if it needs to be re-cached.
|
||||
cached_doc.time_retrieved = datetime.utcnow()
|
||||
cached_doc.cached_until = dom.getElementsByTagName('cachedUntil')[0].childNodes[0].nodeValue
|
||||
|
||||
# Finish up and return the resulting document just in case.
|
||||
if no_cache == False:
|
||||
cached_doc.save()
|
||||
|
||||
return dom
|
||||
|
||||
def api_query(self, url_path, params=None, no_cache=False):
|
||||
"""
|
||||
Transparently handles querying EVE API or retrieving the document from
|
||||
the cache.
|
||||
|
||||
Arguments:
|
||||
url_path: (string) Path to the EVE API page to query. For example:
|
||||
/eve/ErrorList.xml.aspx
|
||||
params: (dictionary/string) A dictionary of extra parameters to include.
|
||||
May also be a string representation of
|
||||
the query: userID=1&characterID=xxxxxxxx
|
||||
"""
|
||||
if type({}) == type(params):
|
||||
# If 'params' is a dictionary, convert it to a URL string.
|
||||
params = urllib.urlencode(params)
|
||||
elif params == None or params.strip() == '':
|
||||
# For whatever reason, EVE API freaks out if there are no parameters.
|
||||
# Add a bogus parameter if none are specified. I'm sure there's a
|
||||
# better fix for this.
|
||||
params = 'odd_parm=1'
|
||||
|
||||
# Combine the URL path and the parameters to create the full query.
|
||||
query_name = '%s?%s' % (url_path, params)
|
||||
|
||||
if no_cache:
|
||||
# If no_cache is enabled, don't even attempt a lookup.
|
||||
cached_doc = CachedDocument()
|
||||
created = False
|
||||
else:
|
||||
# Retrieve or create a new CachedDocument based on the full URL
|
||||
# and parameters.
|
||||
cached_doc, created = self.get_or_create(url_path=query_name)
|
||||
|
||||
# EVE uses UTC.
|
||||
current_eve_time = datetime.utcnow()
|
||||
|
||||
# Figure out if we need hit EVE API and re-cache, or just pull from
|
||||
# the local cache (based on cached_until).
|
||||
if no_cache or created or \
|
||||
cached_doc.cached_until == None or \
|
||||
current_eve_time > cached_doc.cached_until:
|
||||
# Cache from EVE API
|
||||
dom = self.cache_from_eve_api(cached_doc, url_path, params,
|
||||
no_cache=no_cache)
|
||||
else:
|
||||
# Parse the document here since it was retrieved from the
|
||||
# database cache instead of queried for.
|
||||
dom = minidom.parseString(cached_doc.body)
|
||||
|
||||
# Check for the presence errors. Only check the bare minimum,
|
||||
# generic stuff that applies to most or all queries. User-level code
|
||||
# should check for the more specific errors.
|
||||
error_node = dom.getElementsByTagName('error')
|
||||
if error_node:
|
||||
error_code = error_node[0].getAttribute('code')
|
||||
# User specified an invalid userid and/or auth key.
|
||||
if error_code == '203':
|
||||
raise APIAuthException()
|
||||
elif error_code == '106':
|
||||
raise APINoUserIDException()
|
||||
|
||||
return cached_doc
|
||||
|
||||
class CachedDocument(models.Model):
|
||||
"""
|
||||
This is a cached XML document from the EVE API.
|
||||
"""
|
||||
url_path = models.CharField(max_length=255)
|
||||
body = models.TextField()
|
||||
time_retrieved = models.DateTimeField(blank=True, null=True)
|
||||
cached_until = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
# The custom manager handles the querying.
|
||||
objects = CachedDocumentManager()
|
||||
8
eve_proxy/urls.py
Executable file
8
eve_proxy/urls.py
Executable file
@@ -0,0 +1,8 @@
|
||||
from django.conf.urls.defaults import *
|
||||
|
||||
urlpatterns = patterns('eve_proxy.views',
|
||||
# This view can be used just like EVE API's http://api.eve-online.com.
|
||||
# Any parameters or URL paths are sent through the cache system and
|
||||
# forwarded to the EVE API site as needed.
|
||||
url(r'^', 'retrieve_xml', name='eve_proxy-retrieve_xml'),
|
||||
)
|
||||
22
eve_proxy/views.py
Executable file
22
eve_proxy/views.py
Executable file
@@ -0,0 +1,22 @@
|
||||
from django.http import HttpResponse
|
||||
from eve_proxy.models import CachedDocument
|
||||
|
||||
def retrieve_xml(request):
|
||||
"""
|
||||
A view that forwards EVE API requests through the cache system, either
|
||||
retrieving a cached document or querying and caching as needed.
|
||||
"""
|
||||
# This is the URL path (minus the parameters).
|
||||
url_path = request.META['PATH_INFO']
|
||||
# The parameters attached to the end of the URL path.
|
||||
params = request.META['QUERY_STRING']
|
||||
|
||||
if url_path == '/' or url_path == '':
|
||||
# If they don't provide any kind of query, shoot a quick error message.
|
||||
return HttpResponse('No API query specified.')
|
||||
|
||||
# The query system will retrieve a cached_doc that was either previously
|
||||
# or newly cached depending on cache intervals.
|
||||
cached_doc = CachedDocument.objects.api_query(url_path, params)
|
||||
# Return the document's body as XML.
|
||||
return HttpResponse(cached_doc.body, mimetype='text/xml')
|
||||
11
manage.py
Executable file
11
manage.py
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/python
|
||||
from django.core.management import execute_manager
|
||||
try:
|
||||
import settings # Assumed to be in the same directory.
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_manager(settings)
|
||||
85
settings.py
Normal file
85
settings.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# Django settings for login project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@domain.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
||||
DATABASE_NAME = '/home/dreddit/www/login.dredd.it/login.db3' # Or path to database file if using sqlite3.
|
||||
DATABASE_USER = '' # Not used with sqlite3.
|
||||
DATABASE_PASSWORD = '' # Not used with sqlite3.
|
||||
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
|
||||
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# If running in a Windows environment this must be set to the same as your
|
||||
# system time zone.
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
|
||||
# Absolute path to the directory that holds media.
|
||||
# Example: "/home/media/media.lawrence.com/"
|
||||
MEDIA_ROOT = ''
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash if there is a path component (optional in other cases).
|
||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
||||
MEDIA_URL = ''
|
||||
|
||||
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://foo.com/media/", "/media/".
|
||||
ADMIN_MEDIA_PREFIX = '/media/'
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = '8i2+dd-b2tg9g%mq$&i$-8beh4i5^2mm=e-nh^$p47^w=z1igr'
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.load_template_source',
|
||||
'django.template.loaders.app_directories.load_template_source',
|
||||
# 'django.template.loaders.eggs.load_template_source',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'login.urls'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'registration',
|
||||
'eve_proxy',
|
||||
'eve_api',
|
||||
'sso',
|
||||
)
|
||||
|
||||
AUTH_PROFILE_MODULE = 'sso.UserProfile'
|
||||
0
sso/__init__.py
Normal file
0
sso/__init__.py
Normal file
18
sso/models.py
Normal file
18
sso/models.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from eve_api.models.api_player import EVEAccount
|
||||
|
||||
class UserProfile(User):
|
||||
eveaccount = models.ForeignKey(EVEAccount)
|
||||
|
||||
class Site(models.Model):
|
||||
url = models.CharField(max_length=200)
|
||||
active = models.BooleanField()
|
||||
api = models.CharField(max_length=200)
|
||||
|
||||
class SiteAccount(models.Model):
|
||||
user = models.ForeignKey(UserProfile)
|
||||
site = models.ForeignKey(Site)
|
||||
username = models.CharField(max_length=200)
|
||||
active = models.BooleanField()
|
||||
|
||||
23
sso/tests.py
Normal file
23
sso/tests.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.failUnlessEqual(1 + 1, 2)
|
||||
|
||||
__test__ = {"doctest": """
|
||||
Another way to test that 1 + 1 is equal to 2.
|
||||
|
||||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
|
||||
1
sso/views.py
Normal file
1
sso/views.py
Normal file
@@ -0,0 +1 @@
|
||||
# Create your views here.
|
||||
11
urls.py
Normal file
11
urls.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.conf.urls.defaults import *
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
from django.contrib import admin
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^login/', include('django.contrib.auth.views.login')),
|
||||
|
||||
(r'^admin/', include(admin.site.urls)),
|
||||
)
|
||||
Reference in New Issue
Block a user