Massive reworking of eve_proxy to use sha1 ids for url_paths and various other small fixes

This commit is contained in:
2010-06-04 00:56:42 +01:00
parent a9c7c5a552
commit 4e64fa70d4
2 changed files with 84 additions and 96 deletions

View File

@@ -1,6 +1,7 @@
import httplib import httplib
import urllib import urllib
import xml import xml
import hashlib
from datetime import datetime, timedelta from datetime import datetime, timedelta
from xml.dom import minidom from xml.dom import minidom
from django.db import models from django.db import models
@@ -13,40 +14,74 @@ class CachedDocumentManager(models.Manager):
""" """
This manager handles querying or retrieving CachedDocuments. This manager handles querying or retrieving CachedDocuments.
""" """
def cache_from_eve_api(self, cached_doc, url_path, params, no_cache=False):
def get_document_id(self, url_path, params):
if params:
if 'service' in params:
del params['service']
paramstr = urllib.urlencode(params)
else:
paramstr = ''
return hashlib.sha1('%s?%s' % (url_path, paramstr)).hexdigest()
def cache_from_eve_api(self, url_path, params, no_cache=False):
""" """
Connect to the EVE API server, send the request, and cache it to Connect to the EVE API server, send the request, and cache it to
a CachedDocument. This is typically not something you want to call a CachedDocument. This is typically not something you want to call
directly. Use api_query(). directly. Use api_query().
""" """
method = 'GET'
paramstr = ''
if params:
if 'service' in params:
service = params['service']
del params['service']
else:
service = 'auth'
paramstr = urllib.urlencode(params)
if len(paramstr.strip()) > 0:
method = 'POST'
headers = {"Content-type": "application/x-www-form-urlencoded"} headers = {"Content-type": "application/x-www-form-urlencoded"}
# This is the connection to the EVE API server.
conn = httplib.HTTPConnection(API_URL) conn = httplib.HTTPConnection(API_URL)
# Combine everything into an HTTP request. conn.request(method, url_path, paramstr, headers)
conn.request("POST", url_path, params, headers)
# Retrieve the response from the server.
response = conn.getresponse() response = conn.getresponse()
# Save the response (an XML document) to the CachedDocument.
cached_doc.body = unicode(response.read(), 'utf-8') print url_path, paramstr, response.status
try:
# Parse the response via minidom
dom = minidom.parseString(cached_doc.body.encode('utf-8'))
except xml.parsers.expat.ExpatError:
return
# Set the CachedDocument's time_retrieved and cached_until times based if response.status == 200:
# on the values in the XML response. This will be used in future doc_id = self.get_document_id(url_path, params)
# requests to see if the CachedDocument can be retrieved directly or cached_doc, created = self.get_or_create(url_path=doc_id)
# if it needs to be re-cached. cached_doc.body = unicode(response.read(), 'utf-8')
cached_doc.time_retrieved = datetime.utcnow() 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 try:
# Parse the response via minidom
dom = minidom.parseString(cached_doc.body.encode('utf-8'))
except xml.parsers.expat.ExpatError:
cached_doc.cached_until = datetime.utcnow()
else:
cached_doc.cached_until = dom.getElementsByTagName('cachedUntil')[0].childNodes[0].nodeValue
# If this is user related, write a log instance
if params and 'userID' in params:
log = ApiAccessLog()
log.userid = params['userID']
log.service = service
log.time_access = cached_doc.time_retrieved
log.document = url_path
log.save()
# Finish up and return the resulting document just in case.
if no_cache == False:
cached_doc.save()
cached_doc = self.get(id=cached_doc.pk)
return cached_doc
def api_query(self, url_path, params=None, no_cache=False, exceptions=True): def api_query(self, url_path, params=None, no_cache=False, exceptions=True):
""" """
@@ -61,75 +96,35 @@ class CachedDocumentManager(models.Manager):
the query: userID=1&characterID=xxxxxxxx the query: userID=1&characterID=xxxxxxxx
""" """
# If we have a service ID, store and strip it from the query string. doc_id = self.get_document_id(url_path, params)
if 'service' in params:
service = params['service'] doc = None
del params['service'] if not no_cache:
else: try:
service = 'auth' doc = super(CachedDocumentManager, self).get_query_set().get(url_path=doc_id)
except self.model.DoesNotExist:
if 'userID' in params: pass
userid = params['userID']
paramstr = urllib.urlencode(params)
if params == None or paramstr.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.
paramstr = 'odd_parm=1'
# Combine the URL path and the parameters to create the full query.
query_name = '%s?%s' % (url_path, paramstr)
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. # EVE uses UTC.
current_eve_time = datetime.utcnow() current_eve_time = datetime.utcnow()
# Figure out if we need hit EVE API and re-cache, or just pull from if not doc or current_eve_time > doc.cached_until:
# the local cache (based on cached_until). doc = self.cache_from_eve_api(url_path, params, no_cache=no_cache)
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, paramstr,
no_cache=no_cache)
# If its a user related request, log the transaction. if doc:
if 'userID' in params: dom = minidom.parseString(doc.body.encode('utf-8'))
log = ApiAccessLog()
log.userid = userid if dom:
log.service = service error_node = dom.getElementsByTagName('error')
log.time_access = datetime.utcnow() if error_node and exceptions:
log.document = url_path error_code = error_node[0].getAttribute('code')
log.save() # User specified an invalid userid and/or auth key.
else: if error_code == '203':
# Parse the document here since it was retrieved from the raise APIAuthException()
# database cache instead of queried for. elif error_code == '106':
dom = minidom.parseString(cached_doc.body.encode('utf-8')) raise APINoUserIDException()
# 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.
if dom:
error_node = dom.getElementsByTagName('error')
if error_node and exceptions:
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 return doc
class CachedDocument(models.Model): class CachedDocument(models.Model):
""" """

View File

@@ -1,5 +1,5 @@
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseNotFound from django.http import HttpResponse, HttpResponseNotFound, HttpResponseServerError
from eve_proxy.models import CachedDocument from eve_proxy.models import CachedDocument
def retrieve_xml(request): def retrieve_xml(request):
@@ -28,14 +28,7 @@ def retrieve_xml(request):
if 'userID' in params and not 'service' in params: if 'userID' in params and not 'service' in params:
return HttpResponse('No Service ID provided.') return HttpResponse('No Service ID provided.')
# The query system will retrieve a cached_doc that was either previously cached_doc = CachedDocument.objects.api_query(url_path, params, exceptions=False)
# or newly cached depending on cache intervals.
try:
cached_doc = CachedDocument.objects.api_query(url_path, params, exceptions=False)
except:
return HttpResponseServerError()
# Return the document's body as XML.
if cached_doc: if cached_doc:
return HttpResponse(cached_doc.body, mimetype='text/xml') return HttpResponse(cached_doc.body, mimetype='text/xml')