From 21016b7c71c5043c2ecd51a26bca180c3b0c9d28 Mon Sep 17 00:00:00 2001 From: tzangms Date: Mon, 7 Apr 2014 02:30:39 +0800 Subject: [PATCH] initial commit --- .gitignore | 4 ++ dj_elastictranscoder/__init__.py | 1 + dj_elastictranscoder/admins.py | 7 ++++ dj_elastictranscoder/endpoint.py | 12 ++++++ dj_elastictranscoder/models.py | 21 +++++++++++ dj_elastictranscoder/tests.py | 65 ++++++++++++++++++++++++++++++++ dj_elastictranscoder/urls.py | 5 +++ dj_elastictranscoder/utils.py | 29 ++++++++++++++ dj_elastictranscoder/views.py | 60 +++++++++++++++++++++++++++++ runtests.py | 44 +++++++++++++++++++++ setup.py | 29 ++++++++++++++ 11 files changed, 277 insertions(+) create mode 100644 .gitignore create mode 100644 dj_elastictranscoder/__init__.py create mode 100644 dj_elastictranscoder/admins.py create mode 100644 dj_elastictranscoder/endpoint.py create mode 100644 dj_elastictranscoder/models.py create mode 100644 dj_elastictranscoder/tests.py create mode 100644 dj_elastictranscoder/urls.py create mode 100644 dj_elastictranscoder/utils.py create mode 100644 dj_elastictranscoder/views.py create mode 100644 runtests.py create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0321307 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +*.swp +*.pyc +*.egg-info diff --git a/dj_elastictranscoder/__init__.py b/dj_elastictranscoder/__init__.py new file mode 100644 index 0000000..11d27f8 --- /dev/null +++ b/dj_elastictranscoder/__init__.py @@ -0,0 +1 @@ +__version__ = '0.1' diff --git a/dj_elastictranscoder/admins.py b/dj_elastictranscoder/admins.py new file mode 100644 index 0000000..d1610dd --- /dev/null +++ b/dj_elastictranscoder/admins.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from .models import EncodeJob + +class EncodeJobAdmin(admin.ModelAdmin): + list_display = ('id', 'state', 'message') + list_filter = ('state',) +admin.site.register(EncodeJob, EncodeJob) diff --git a/dj_elastictranscoder/endpoint.py b/dj_elastictranscoder/endpoint.py new file mode 100644 index 0000000..ae2c3bf --- /dev/null +++ b/dj_elastictranscoder/endpoint.py @@ -0,0 +1,12 @@ +import json + +def handler(notification_type): + filename = 'fixtures/on%s.json' % notification_type + with open(filename) as f: + data = json.loads(f.read()) + print data['Message'] + + +handler('progress') +handler('error') +handler('complete') diff --git a/dj_elastictranscoder/models.py b/dj_elastictranscoder/models.py new file mode 100644 index 0000000..0760ad6 --- /dev/null +++ b/dj_elastictranscoder/models.py @@ -0,0 +1,21 @@ +from django.db import models +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.generic import GenericForeignKey + + +class EncodeJob(models.Model): + STATE_CHOICES = ( + (0, 'Waiting'), + (1, 'Progressing'), + (2, 'Error'), + (3, 'Warning'), + (4, 'Complete'), + ) + id = models.CharField(max_length=100, primary_key=True) + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + state = models.PositiveIntegerField(choices=STATE_CHOICES, default=0, db_index=True) + content_object = GenericForeignKey() + message = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + last_modified = models.DateTimeField(auto_now=True) diff --git a/dj_elastictranscoder/tests.py b/dj_elastictranscoder/tests.py new file mode 100644 index 0000000..120a879 --- /dev/null +++ b/dj_elastictranscoder/tests.py @@ -0,0 +1,65 @@ +import os.path + +from django.test import TestCase +from django.db import models +from django.contrib.contenttypes.models import ContentType + +from .models import EncodeJob + + +PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__)) +FIXTURE_DIRS = os.path.join(PROJECT_ROOT, 'fixtures') + + +class Item(models.Model): + name = models.CharField(max_length=100) + + +class SNSNotificationTest(TestCase): + urls = 'dj_elastictranscoder.urls' + + def setUp(self): + item = Item.objects.create(name='Hello') + content_type = ContentType.objects.get_for_model(Item) + self.job_id = '1396802241671-jkmme8' + + self.job = EncodeJob.objects.create(id=self.job_id, content_type=content_type, object_id=item.id) + + def test_initial(self): + job = EncodeJob.objects.get(id=self.job_id) + self.assertEqual(job.state, 0) + + + def test_onprogress(self): + with open(os.path.join(FIXTURE_DIRS, 'onprogress.json')) as f: + content = f.read() + + resp = self.client.post('/sns_endpoint/', content, content_type="application/json") + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.content, 'Done') + + job = EncodeJob.objects.get(id=self.job_id) + self.assertEqual(job.state, 1) + + def test_onerror(self): + with open(os.path.join(FIXTURE_DIRS, 'onerror.json')) as f: + content = f.read() + + resp = self.client.post('/sns_endpoint/', content, content_type="application/json") + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.content, 'Done') + + job = EncodeJob.objects.get(id=self.job_id) + self.assertEqual(job.state, 3) + + + def test_oncomplete(self): + with open(os.path.join(FIXTURE_DIRS, 'oncomplete.json')) as f: + content = f.read() + + resp = self.client.post('/sns_endpoint/', content, content_type="application/json") + self.assertEqual(resp.status_code, 200) + self.assertEqual(resp.content, 'Done') + + job = EncodeJob.objects.get(id=self.job_id) + self.assertEqual(job.state, 4) diff --git a/dj_elastictranscoder/urls.py b/dj_elastictranscoder/urls.py new file mode 100644 index 0000000..e34d33c --- /dev/null +++ b/dj_elastictranscoder/urls.py @@ -0,0 +1,5 @@ +from django.conf.urls import url, patterns + +urlpatterns = patterns('dj_elastictranscoder.views', + url(r'^sns_endpoint/$', 'sns_endpoint'), +) diff --git a/dj_elastictranscoder/utils.py b/dj_elastictranscoder/utils.py new file mode 100644 index 0000000..ce7b57d --- /dev/null +++ b/dj_elastictranscoder/utils.py @@ -0,0 +1,29 @@ +from boto import elastictranscoder + +from django.conf import settings +from django.contrib.contenttypes.models import ContentType + +from .models import EncodeJob + + +def encode(obj, pipeline_id, input_name, outputs, preset_id, region=None): + """ + encode + """ + aws_access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None) + aws_secret_access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) + + if not region: + aws_region = getattr(settings, 'AWS_REGION', None) + + if not aws_access_key_id or not aws_secret_access_key or not aws_region: + assert False, 'Please provide `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`' + + transcoder = elastictranscoder.connect_to_region(aws_region, + aws_access_key_id=aws_access_key_id, + aws_secret_access_key=aws_secret_access_key) + + resp = transcoder.create_job(pipeline_id, input_name, outputs=outputs) + + content_type = ContentType.objects.get_for_model(obj) + EncodeJob.objects.create(id=resp['Job']['Id'], content_type=content_type, object_id=obj.id) diff --git a/dj_elastictranscoder/views.py b/dj_elastictranscoder/views.py new file mode 100644 index 0000000..e026ced --- /dev/null +++ b/dj_elastictranscoder/views.py @@ -0,0 +1,60 @@ +import json + +from django.http import HttpResponse, HttpResponseBadRequest +from django.core.mail import mail_admins +from .models import EncodeJob + + +def sns_endpoint(request): + """ + Receive SNS notification + """ + + try: + data = json.loads(request.read()) + except ValueError: + return HttpResponseBadRequest('Invalid JSON') + + + # handle SNS subscription + if data['Type'] == 'SubscriptionConfirmation': + subscribe_url = data['SubscribeURL'] + subscribe_body = """ + Please visit this URL below to confirm your subscription with SNS + + %s """ % subscribe_url + + mail_admins('Please confirm SNS subscription', subscribe_body) + return HttpResponse('OK') + + + # + try: + message = json.loads(data['Message']) + except ValueError: + assert False, data['Message'] + + job, created = EncodeJob.objects.get_or_create(id=message['jobId']) + + if message['state'] == 'PROGRESSING': + job.state = 1 + elif message['state'] == 'COMPLETED': + job.state = 4 + elif message['state'] == 'ERROR': + job.state = 3 + + job.save() + + # TODO: send signal to handler + + return HttpResponse('Done') + + +def _oncomplete(request, message): + pass + +def _onerror(request, message): + pass + +def _onprogress(request, message): + pass diff --git a/runtests.py b/runtests.py new file mode 100644 index 0000000..07b4727 --- /dev/null +++ b/runtests.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +import sys +from os.path import dirname, abspath + +from django.conf import settings + + +settings.configure( + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:' + } + }, + INSTALLED_APPS=[ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'dj_elastictranscoder', + ], + SITE_ID=1, + DEBUG=False, + ROOT_URLCONF='', +) + + + +def runtests(**test_args): + from django.test.utils import get_runner + + parent = dirname(abspath(__file__)) + sys.path.insert(0, parent) + + TestRunner = get_runner(settings) + test_runner = TestRunner(verbosity=1, interactive=True) + failures = test_runner.run_tests(['dj_elastictranscoder'], test_args) + sys.exit(failures) + + +if __name__ == '__main__': + runtests(*sys.argv[1:]) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..bd1c04c --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +from setuptools import setup, find_packages +from dj_elastictranscoder import __version__ + + +setup( + name='django-elastic-transcoder', + version=__version__, + description="django-elastic-transcoder", + author='tzangms', + author_email='tzangms@streetvoice.com', + url='http://github.com/StreetVoice/django-elastic-transcoder', + license='MIT', + test_suite='runtests.runtests', + packages=['dj_elastictranscoder',], + include_package_data=True, + zip_safe=False, + install_requires = [ + "django >= 1.4", + "boto >= 2.5", + "South >= 0.8", + ], + classifiers=[ + "Programming Language :: Python", + "Topic :: Software Development :: Libraries :: Python Modules", + "Framework :: Django", + "Environment :: Web Environment", + ], + keywords='django,aws,elastic,transcoder', +)