From e1dc20ff2ef52a57607fba04926fbdd320d448c8 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sun, 20 May 2012 11:31:56 +0100 Subject: [PATCH] Work on updating eve_proxy for Django 1.4 and TZ support. --- app/eve_proxy/__init__.py | 2 +- app/eve_proxy/models.py | 21 ++++++++++------ app/eve_proxy/tests.py | 7 +++--- app/eve_proxy/urls.py | 4 ++- app/eve_proxy/views.py | 52 +++++++++++++++++---------------------- 5 files changed, 45 insertions(+), 41 deletions(-) diff --git a/app/eve_proxy/__init__.py b/app/eve_proxy/__init__.py index dab0451..0fe5b46 100644 --- a/app/eve_proxy/__init__.py +++ b/app/eve_proxy/__init__.py @@ -1,4 +1,4 @@ -VERSION = (0, 4) +VERSION = (0, 5) # Dynamically calculate the version based on VERSION tuple if len(VERSION)>2 and VERSION[2] is not None: diff --git a/app/eve_proxy/models.py b/app/eve_proxy/models.py index 394e734..f548a42 100644 --- a/app/eve_proxy/models.py +++ b/app/eve_proxy/models.py @@ -8,6 +8,7 @@ from xml.dom import minidom from django.db import models, IntegrityError from django.conf import settings from django.core.cache import cache +from django.utils.timezone import utc, now from eve_proxy.exceptions import * @@ -77,7 +78,7 @@ class CachedDocumentManager(models.Manager): doc = CachedDocument(pk=doc_key, url_path=url) created = True - if created or not doc.cached_until or datetime.utcnow() > doc.cached_until or no_cache: + if created or not doc.cached_until or now() > doc.cached_until or no_cache: stat_update_count('eve_proxy_api_requests') req = urllib2.Request(url) @@ -104,23 +105,29 @@ class CachedDocumentManager(models.Manager): raise DocumentRetrievalError(e.reason) else: doc.body = unicode(conn.read(), 'utf-8') - doc.time_retrieved = datetime.utcnow() + doc.time_retrieved = now() error = 0 try: # Parse the response via minidom dom = minidom.parseString(doc.body.encode('utf-8')) except: - doc.cached_until = datetime.utcnow() + doc.cached_until = now() else: - date = datetime.strptime(dom.getElementsByTagName('cachedUntil')[0].childNodes[0].nodeValue, '%Y-%m-%d %H:%M:%S') - # Add 30 seconds to the cache timers, avoid hitting too early and account for some minor clock skew. - doc.cached_until = date + timedelta(seconds=getattr(settings, 'EVE_PROXY_GLOBAL_CACHE_ADJUSTMENT', 30)) + + # Calculate the cache timer on the basis of the currentTime and cachedUntil fields provided by the API + # This allows for clock skew not to fuck with the timers. + currenttime = datetime.strptime(dom.getElementsByTagName('currentTime')[0].childNodes[0].nodeValue, '%Y-%m-%d %H:%M:%S') + cacheuntil = datetime.strptime(dom.getElementsByTagName('cachedUntil')[0].childNodes[0].nodeValue, '%Y-%m-%d %H:%M:%S') + date = now() + (cacheuntil - currenttime) + + # Add the global adjustment, to avoid CCP's hardline cache timers + doc.cached_until += timedelta(seconds=getattr(settings, 'EVE_PROXY_GLOBAL_CACHE_ADJUSTMENT', 30)) # Allow for tuning of individual pages with bad cache returns adjustconfig = getattr(settings, 'EVE_PROXY_CACHE_ADJUSTMENTS', {}) if url_path.lower() in adjustconfig: - doc.cached_until = date + timedelta(seconds=adjustconfig[url_path.lower()]) + doc.cached_until += timedelta(seconds=adjustconfig[url_path.lower()]) enode = dom.getElementsByTagName('error') if enode: diff --git a/app/eve_proxy/tests.py b/app/eve_proxy/tests.py index fb47f47..6965548 100644 --- a/app/eve_proxy/tests.py +++ b/app/eve_proxy/tests.py @@ -4,6 +4,7 @@ from datetime import datetime import time from django.utils import unittest +from django.utils.timezone import now from eve_proxy.models import CachedDocument from eve_proxy.exceptions import * @@ -40,8 +41,8 @@ class CachedDocumentTestCase(unittest.TestCase): """ Tests if objects are being cached correctly """ url = '/server/ServerStatus.xml.aspx' - obj = CachedDocument.objects.api_query(url, no_cache=True) - obj2 = CachedDocument.objects.api_query(url) + obj = CachedDocument.objects.api_query(url, no_cache=True) + obj2 = CachedDocument.objects.api_query(url) self.assertEqual(obj.pk, obj2.pk, "Objects are not caching correctly") @@ -51,7 +52,7 @@ class CachedDocumentTestCase(unittest.TestCase): url = '/server/ServerStatus.xml.aspx' obj = CachedDocument.objects.api_query(url, no_cache=True) ret_time = obj.time_retrieved - obj.cached_until = datetime.utcnow() + obj.cached_until = now() obj.save() time.sleep(1) diff --git a/app/eve_proxy/urls.py b/app/eve_proxy/urls.py index 6c2e37f..02d091e 100644 --- a/app/eve_proxy/urls.py +++ b/app/eve_proxy/urls.py @@ -1,8 +1,10 @@ from django.conf.urls.defaults import * +from eve_proxy.views import EVEAPIProxyView + 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'), + url(r'^', EVEAPIProxyView.as_view(), name='eveproxy-apiproxy'), ) diff --git a/app/eve_proxy/views.py b/app/eve_proxy/views.py index 83f687b..3108cdc 100644 --- a/app/eve_proxy/views.py +++ b/app/eve_proxy/views.py @@ -2,38 +2,32 @@ from django.core.urlresolvers import reverse from django.http import HttpResponse, HttpResponseNotFound, HttpResponseServerError 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'].replace(reverse('eve_proxy.views.retrieve_xml'),"/") - # The parameters attached to the end of the URL path. - if request.method == 'POST': - p = request.POST - else: - p = request.GET +class EVEAPIProxyView(View): + """Allows for standard EVE API calls to be proxied through your application""" - # Convert the QuerySet object into a dict - params = {} - for key,value in p.items(): - params[key] = value - - if url_path == '/' or url_path == '': - # If they don't provide any kind of query, shoot a quick error message. - return HttpResponseNotFound('No API query specified.') + def get(self, request, *args, **kwargs): + return self.get_document(request, request.GET) - if 'userID' in params and not 'service' in params: - return HttpResponse('No Service ID provided.') + def post(self, request, *args, **kwargs): + return self.get_document(request, request.POST) + + def get_document(self, request, params): + url_path = request.META['PATH_INFO'].replace(reverse('eveproxy-apiproxy'),"/") - try: - cached_doc = CachedDocument.objects.api_query(url_path, params, exceptions=False) - except: - return HttpResponseServerError('Error occured') + if url_path == '/' or url_path == '': + # If they don't provide any kind of query, shoot a quick error message. + return HttpResponseNotFound('No API query specified.') + + if 'userID' in params and not 'service' in params: + return HttpResponse('No Service ID provided.') - if cached_doc: - return HttpResponse(cached_doc.body, mimetype='text/xml') + try: + cached_doc = CachedDocument.objects.api_query(url_path, params, exceptions=False) + except: + return HttpResponseServerError('Error occured') - return HttpResponseNotFound('Error retrieving the document') + if cached_doc: + return HttpResponse(cached_doc.body, mimetype='text/xml') + + return HttpResponseNotFound('Error retrieving the document')