Commit 65c2a891 by PaulWattenberger

Added worker code to inform Sailthru about purchases

parent a937e046
...@@ -19,7 +19,7 @@ requirements: ...@@ -19,7 +19,7 @@ requirements:
pip install -qr requirements/local.txt --exists-action w pip install -qr requirements/local.txt --exists-action w
worker: worker:
celery -A ecommerce_worker worker --app=$(PACKAGE).celery_app:app --loglevel=info --queue=fulfillment celery -A ecommerce_worker worker --app=$(PACKAGE).celery_app:app --loglevel=info --queue=fulfillment,email_marketing
test: test:
WORKER_CONFIGURATION_MODULE=ecommerce_worker.configuration.test nosetests \ WORKER_CONFIGURATION_MODULE=ecommerce_worker.configuration.test nosetests \
......
"""
This file contains a primitive cache
"""
import threading
import time
lock = threading.Lock() # pylint: disable=invalid-name
class CacheObject(object):
"""Object saved in cache"""
def __init__(self, value, duration):
self.value = value
self.expire = time.time() + duration
class Cache(dict):
"""
Primitive key/value cache. Entries are kept in a dict with an expiration.
When a get of an expired entry is done, the cache is cleaned of all expired entries.
Locking is used for thread safety
"""
def get(self, key):
"""Get an object from the cache
Arguments:
key (str): Cache key
Returns:
Cached object
"""
lock.acquire()
try:
if key not in self:
return None
current_time = time.time()
if self[key].expire > current_time:
return self[key].value
# expired key, clean out all expired keys
deletes = []
for k, val in self.items():
if val.expire <= current_time:
deletes.append(k)
for k in deletes:
del self[k]
return None
finally:
lock.release()
def set(self, key, value, duration):
"""Save an object in the cache
Arguments:
key (str): Cache key
value (object): object to cache
duration (int): time in seconds to keep object in cache
"""
lock.acquire()
try:
self[key] = CacheObject(value, duration)
finally:
lock.release()
...@@ -22,6 +22,7 @@ CELERY_RESULT_BACKEND = None ...@@ -22,6 +22,7 @@ CELERY_RESULT_BACKEND = None
# See http://celery.readthedocs.org/en/latest/configuration.html#celery-imports. # See http://celery.readthedocs.org/en/latest/configuration.html#celery-imports.
CELERY_IMPORTS = ( CELERY_IMPORTS = (
'ecommerce_worker.fulfillment.v1.tasks', 'ecommerce_worker.fulfillment.v1.tasks',
'ecommerce_worker.sailthru.v1.tasks',
) )
# Prevent Celery from removing handlers on the root logger. Allows setting custom logging handlers. # Prevent Celery from removing handlers on the root logger. Allows setting custom logging handlers.
...@@ -51,3 +52,40 @@ ECOMMERCE_SERVICE_USERNAME = 'ecommerce_worker' ...@@ -51,3 +52,40 @@ ECOMMERCE_SERVICE_USERNAME = 'ecommerce_worker'
# Site Overrides provide support for site/partner-specific configuration settings where applicable # Site Overrides provide support for site/partner-specific configuration settings where applicable
# For example, the ECOMMERCE_API_ROOT value is different from one ecommerce site to the next # For example, the ECOMMERCE_API_ROOT value is different from one ecommerce site to the next
SITE_OVERRIDES = None SITE_OVERRIDES = None
# Settings for Sailthru email marketing integration
SAILTHRU = {
# Set to false to ignore Sailthru events
'SAILTHRU_ENABLE': True,
# Template used when user upgrades to verified
'SAILTHRU_UPGRADE_TEMPLATE': None,
# Template used with user purchases a course
'SAILTHRU_PURCHASE_TEMPLATE': None,
# Template used with user enrolls in a free course
'SAILTHRU_ENROLL_TEMPLATE': None,
# Abandoned cart template
'SAILTHRU_ABANDONED_CART_TEMPLATE': None,
# minutes to delay before abandoned cart message
'SAILTHRU_ABANDONED_CART_DELAY': 60,
# Sailthru key and secret required for integration
'SAILTHRU_KEY': None,
'SAILTHRU_SECRET': None,
# Retry settings for Sailthru celery tasks
'SAILTHRU_RETRY_SECONDS': 3600,
'SAILTHRU_RETRY_ATTEMPTS': 24,
# ttl for cached course content from Sailthru (in seconds)
'SAILTHRU_CACHE_TTL_SECONDS': 3600,
# dummy price for audit/honor (i.e., if cost = 0)
# Note: setting this value to 0 skips Sailthru calls for free transactions
'SAILTHRU_MINIMUM_COST': 100,
}
...@@ -31,3 +31,19 @@ ECOMMERCE_SERVICE_USERNAME = 'service' ...@@ -31,3 +31,19 @@ ECOMMERCE_SERVICE_USERNAME = 'service'
# SITE-SPECIFIC CONFIGURATION OVERRIDES # SITE-SPECIFIC CONFIGURATION OVERRIDES
SITE_OVERRIDES = {} SITE_OVERRIDES = {}
# Sailthru support unit test settings
SAILTHRU.update({
'SAILTHRU_UPGRADE_TEMPLATE': 'upgrade_template',
'SAILTHRU_PURCHASE_TEMPLATE': 'purchase_template',
'SAILTHRU_ENROLL_TEMPLATE': 'enroll_template',
'SAILTHRU_ABANDONED_CART_TEMPLATE': 'abandoned_template',
'SAILTHRU_KEY': 'key',
'SAILTHRU_SECRET': 'secret',
})
# Sailthru support unit test settings with override
TEST_SITE_OVERRIDES = {
'test_site': { 'SAILTHRU': dict(SAILTHRU,
SAILTHRU_UPGRADE_TEMPLATE='site_upgrade_template')}
}
"""Tests of cache."""
import logging
from unittest import TestCase
from ecommerce_worker.cache import Cache
log = logging.getLogger(__name__)
class CacheTests(TestCase):
"""
Tests for the cache routine.
"""
def setUp(self):
super(CacheTests, self).setUp()
def test_content_cache(self):
"""
Test content cache code
"""
# Create a cache and add 5 items to it
cache = Cache()
cache.set('key1', 'value1', 100)
cache.set('key2', 'value2', 100)
# set duration to zero or negative so they expire
cache.set('key3', 'value3', 0)
cache.set('key4', 'value4', -100)
cache.set('key5', 'value5', -100)
# should be 5 to start
self.assertEquals(len(cache), 5)
# getting one of the expired should clean out all expired
self.assertEquals(cache.get('key5'), None)
# make sure 2 left
self.assertEquals(len(cache), 2)
self.assertEquals(cache.get('key2'), 'value2')
self.assertEquals(cache.get('key1'), 'value1')
self.assertEquals(cache.get('key3'), None)
...@@ -2,3 +2,4 @@ ...@@ -2,3 +2,4 @@
# this package can serve as both a library and a standalone application. # this package can serve as both a library and a standalone application.
celery==3.1.18 celery==3.1.18
edx-rest-api-client==1.5.0 edx-rest-api-client==1.5.0
sailthru-client==2.2.3
...@@ -6,7 +6,7 @@ with open('README.rst') as readme: ...@@ -6,7 +6,7 @@ with open('README.rst') as readme:
setup( setup(
name='edx-ecommerce-worker', name='edx-ecommerce-worker',
version='0.4.1', version='0.5.0',
description='Celery tasks supporting the operations of edX\'s ecommerce service', description='Celery tasks supporting the operations of edX\'s ecommerce service',
long_description=long_description, long_description=long_description,
classifiers=[ classifiers=[
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment