Permalink
Browse files

merged dev

  • Loading branch information...
ehazlett committed Aug 13, 2012
2 parents 5deddc8 + 6581c09 commit ae4ffcd98963695d7df6cc8918396ed0817569fb
Showing 325 changed files with 42,330 additions and 42,629 deletions.
View
2 .kick
@@ -1,7 +1,7 @@
process do |files|
files.take_and_map do |file|
case file
- when %r{^media/js/embedder/embedder.js$}
+ when %r{^media/js/embedder/embedder.js|media/js/embedder/popcorn.transcript.js$}
execute "jshint media/js/embedder/embedder.js"
execute "media/js/embedder/compile-embedder.sh"
when %r{^media/css/v1.scss$}
@@ -1,25 +1,28 @@
# Amara, universalsubtitles.org
-#
+#
# Copyright (C) 2012 Participatory Culture Foundation
-#
+#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
-#
+#
# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see
+# along with this program. If not, see
# http://www.gnu.org/licenses/agpl-3.0.html.
-from django.conf.urls.defaults import patterns, url
+from django.contrib import admin
+from models import ThirdPartyAccount, YoutubeSyncRule
-urlpatterns = patterns('icanhaz.views',
- url('^(?P<video_id>[\w]+)/visibility-form/show/', 'get_visibility_form', name='get-visibility-form'),
-)
+class ThirdPartyAccountAdmin(admin.ModelAdmin):
+ list_display = ('type', 'username',)
+
+admin.site.register(ThirdPartyAccount, ThirdPartyAccountAdmin)
+admin.site.register(YoutubeSyncRule)
@@ -0,0 +1,45 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'YoutubeSyncRule'
+ db.create_table('accountlinker_youtubesyncrule', (
+ ('user', self.gf('django.db.models.fields.TextField')()),
+ ('video', self.gf('django.db.models.fields.TextField')()),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('team', self.gf('django.db.models.fields.TextField')()),
+ ))
+ db.send_create_signal('accountlinker', ['YoutubeSyncRule'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'YoutubeSyncRule'
+ db.delete_table('accountlinker_youtubesyncrule')
+
+
+ models = {
+ 'accountlinker.thirdpartyaccount': {
+ 'Meta': {'unique_together': "(('type', 'username'),)", 'object_name': 'ThirdPartyAccount'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'oauth_access_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'oauth_refresh_token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
+ 'username': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'accountlinker.youtubesyncrule': {
+ 'Meta': {'object_name': 'YoutubeSyncRule'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'team': ('django.db.models.fields.TextField', [], {}),
+ 'user': ('django.db.models.fields.TextField', [], {}),
+ 'video': ('django.db.models.fields.TextField', [], {})
+ }
+ }
+
+ complete_apps = ['accountlinker']
@@ -17,17 +17,63 @@
# http://www.gnu.org/licenses/agpl-3.0.html.
from django.db import models
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured, ValidationError
from videos.models import VIDEO_TYPE
from .videos.types import (
video_type_registrar, UPDATE_VERSION_ACTION, DELETE_LANGUAGE_ACTION
)
+from teams.models import Team
+from auth.models import CustomUser as User
+
+from utils.metrics import Meter
# for now, they kind of match
ACCOUNT_TYPES = VIDEO_TYPE
+
+def youtube_sync(video, language):
+ """
+ Simplified version of what's found in
+ ``ThirdPartyAccount.mirror_on_third_party``. It doesn't bother checking if
+ we should be syncing this or not. Only does the new Youtube/Amara
+ integration syncing. Used on debug page for video.
+ """
+ version = language.latest_version()
+
+ if version:
+ if not version.is_public or not version.is_synced():
+ return
+
+ always_push_account = ThirdPartyAccount.objects.always_push_account()
+
+ for vurl in video.videourl_set.all():
+ vt = video_type_registrar.video_type_for_url(vurl.url)
+
+ try:
+ vt.update_subtitles(version, always_push_account)
+ Meter('youtube.push.success').inc()
+ except:
+ Meter('youtube.push.fail').inc()
+ finally:
+ Meter('youtube.push.request').inc()
+
+
class ThirdPartyAccountManager(models.Manager):
+ def always_push_account(self):
+ """
+ Get the ThirdPartyAccount that is able to push to any video on Youtube.
+ Raise ``ImproperlyConfigured`` if it can't be found.
+ """
+ username = getattr(settings, 'YOUTUBE_ALWAYS_PUSH_USERNAME')
+
+ try:
+ return self.get(username=username)
+ except ThirdPartyAccount.DoesNotExist:
+ raise ImproperlyConfigured("Can't find youtube account")
+
def mirror_on_third_party(self, video, language, action, version=None):
"""
Does the specified action (video.types.UPDATE_VERSION_ACTION or
@@ -52,22 +98,43 @@ def mirror_on_third_party(self, video, language, action, version=None):
# We can't mirror unsynced or non-public versions.
return
+ try:
+ rule = YoutubeSyncRule.objects.all()[0]
+ should_sync = rule.should_sync(video)
+ always_push_account = self.always_push_account()
+ except IndexError:
+ should_sync = False
+
for vurl in video.videourl_set.all():
+ already_updated = False
+ vt = video_type_registrar.video_type_for_url(vurl.url)
+
+ if should_sync:
+ try:
+ vt.update_subtitles(version, always_push_account)
+ already_updated = True
+ Meter('youtube.push.success').inc()
+ except:
+ Meter('youtube.push.fail').inc()
+ finally:
+ Meter('youtube.push.request').inc()
+
username = vurl.owner_username
+
if not username:
continue
try:
account = ThirdPartyAccount.objects.get(type=vurl.type, username=username)
except ThirdPartyAccount.DoesNotExist:
continue
- vt = video_type_registrar.video_type_for_url(vurl.url)
if hasattr(vt, action):
- if action == UPDATE_VERSION_ACTION:
+ if action == UPDATE_VERSION_ACTION and not already_updated:
vt.update_subtitles(version, account)
elif action == DELETE_LANGUAGE_ACTION:
vt.delete_subtitles(language, account)
+
class ThirdPartyAccount(models.Model):
"""
Links a third party account (e.g. YouTube's') to a certain video URL
@@ -90,4 +157,91 @@ class ThirdPartyAccount(models.Model):
class Meta:
unique_together = ("type", "username")
-
+
+ def __unicode__(self):
+ return '%s - %s' % (self.get_type_display(), self.username)
+
+
+class YoutubeSyncRule(models.Model):
+ """
+ An instance of this class determines which Youtube videos should be synced
+ back to Youtube via the new integration.
+
+ There should only ever be one instance of this class in the database.
+
+ You should run a query and then call it like this:
+
+ rule = YoutubeSyncRule.objects.all()[0]
+ rule.should_sync(video)
+
+ Where ``video`` is a ``videos.models.Video`` instance.
+
+ ``team`` should be a comma-separated list of team slugs that you want to
+ sync. ``user`` should be a comma-separated list of usernames of users
+ whose videos should be synced. ``video`` is a list of primary keys of
+ videos that should be synced.
+
+ You can also specify a wildcard "*" to any of the above to match any teams,
+ any users, or any videos.
+ """
+ team = models.TextField(default='', blank=True,
+ help_text='Comma separated list of slugs')
+ user = models.TextField(default='', blank=True,
+ help_text='Comma separated list of usernames')
+ video = models.TextField(default='', blank=True,
+ help_text='Comma separated list of pks')
+
+ def __unicode__(self):
+ return 'Youtube sync rule'
+
+ def team_in_list(self, team):
+ if not team:
+ return False
+ teams = self.team.split(',')
+ if '*' in teams:
+ return True
+ return team in teams
+
+ def user_in_list(self, user):
+ users = self.user.split(',')
+ if '*' in users:
+ return True
+ return user.username in users
+
+ def video_in_list(self, pk):
+ pks = self.video.split(',')
+ if '*' in pks:
+ return True
+ if len(pks) == 1 and pks[0] == '':
+ return False
+ return pk in map(int, pks)
+
+ def should_sync(self, video):
+ tv = video.get_team_video()
+ team = None
+ if tv:
+ team = tv.team.slug
+
+ return self.team_in_list(team) or \
+ self.user_in_list(video.user) or \
+ self.video_in_list(video.pk)
+
+ def _clean(self, name):
+ if name not in ['team', 'user']:
+ return
+ field = getattr(self, name)
+ values = set(field.split(','))
+ values = [v for v in values if v != '*']
+ if len(values) == 1 and values[0] == '':
+ return []
+ return values
+
+ def clean(self):
+ teams = self._clean('team')
+ users = self._clean('user')
+
+ if len(teams) != Team.objects.filter(slug__in=teams).count():
+ raise ValidationError("One or more teams not found")
+
+ if len(users) != User.objects.filter(username__in=users).count():
+ raise ValidationError("One or more users not found")
@@ -1,16 +1,66 @@
-"""
-This file demonstrates writing tests using the unittest module. These will pass
-when you run "manage.py test".
-
-Replace this with more appropriate tests for your application.
-"""
+# Amara, universalsubtitles.org
+#
+# Copyright (C) 2012 Participatory Culture Foundation
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see
+# http://www.gnu.org/licenses/agpl-3.0.html.
from django.test import TestCase
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+
+from accountlinker.models import ThirdPartyAccount, YoutubeSyncRule
+from videos.models import Video
+from auth.models import CustomUser as User
+
+
+class AccountTest(TestCase):
+ fixtures = ["staging_users.json", "staging_videos.json", "staging_teams.json"]
+
+ def test_retrieval(self):
+
+ acc = ThirdPartyAccount.objects.create(type='Y', username='abc',
+ oauth_access_token='a', oauth_refresh_token='b')
+
+ with self.assertRaises(ImproperlyConfigured):
+ ThirdPartyAccount.objects.always_push_account()
+
+ setattr(settings, 'YOUTUBE_ALWAYS_PUSH_USERNAME', 'abc')
+ self.assertEquals(ThirdPartyAccount.objects.always_push_account().pk,
+ acc.pk)
+
+ def test_rules(self):
+ video = Video.objects.filter(teamvideo__isnull=False)[0]
+ video.user = User.objects.get(username='admin')
+ team = video.get_team_video().team
+ team = team.slug
+
+ r = YoutubeSyncRule.objects.create()
+ self.assertFalse(r.should_sync(video))
+
+ YoutubeSyncRule.objects.all().delete()
+ r = YoutubeSyncRule.objects.create(team=team)
+ self.assertTrue(r.should_sync(video))
+
+ YoutubeSyncRule.objects.all().delete()
+ r = YoutubeSyncRule.objects.create(user='admin')
+ self.assertTrue(r.should_sync(video))
+ YoutubeSyncRule.objects.all().delete()
+ r = YoutubeSyncRule.objects.create(video='*')
+ self.assertTrue(r.should_sync(video))
-class SimpleTest(TestCase):
- def test_basic_addition(self):
- """
- Tests that 1 + 1 always equals 2.
- """
- self.assertEqual(1 + 1, 2)
+ YoutubeSyncRule.objects.all().delete()
+ r = YoutubeSyncRule.objects.create(team='*')
+ self.assertTrue(r.should_sync(video))
Oops, something went wrong.

0 comments on commit ae4ffcd

Please sign in to comment.