class ProjectSerializer(serializers.ModelSerializer): resource_uri = serializers.SerializerMethodField() created = TimezoneAwareDateTimeField(read_only=True) modified = TimezoneAwareDateTimeField(read_only=True) class Meta: model = Project fields = ('name', 'slug', 'description', 'guidelines', 'modified', 'created', 'workflow_enabled', 'resource_uri') # Based on the model code, slug can be blank, but this seems bad to # allow for API requests read_only_fields = ('modified', 'created') extra_kwargs = { 'slug': { 'required': True }, } def get_resource_uri(self, project): return reverse('api:projects-detail', kwargs={ 'team_slug': self.context['team'].slug, 'slug': project.slug, }, request=self.context['request']) def create(self, validated_data): return Project.objects.create(team=self.context['team'], **validated_data)
class TaskSerializer(serializers.ModelSerializer): resource_uri = serializers.SerializerMethodField() video_id = TeamVideoField(source='team_video') assignee = TeamMemberField(required=False) type = MappedChoiceField(Task.TYPE_CHOICES) created = TimezoneAwareDateTimeField(read_only=True) modified = TimezoneAwareDateTimeField(read_only=True) completed = TimezoneAwareDateTimeField(read_only=True) approved = MappedChoiceField( Task.APPROVED_CHOICES, required=False, default=Task._meta.get_field('approved').get_default(), ) class Meta: model = Task fields = ( 'id', 'video_id', 'language', 'type', 'assignee', 'priority', 'created', 'modified', 'completed', 'approved', 'resource_uri', ) def get_resource_uri(self, task): return reverse('api:tasks-detail', kwargs={ 'team_slug': self.context['team'].slug, 'id': task.id, }, request=self.context['request']) def create(self, validated_data): validated_data['team'] = self.context['team'] return super(TaskSerializer, self).create(validated_data)
class ApplicationSerializer(serializers.ModelSerializer): user = UserField(read_only=True) status = MappedChoiceField( Application.STATUSES, default=Application._meta.get_field('status').get_default()) resource_uri = serializers.SerializerMethodField() created = TimezoneAwareDateTimeField(read_only=True) modified = TimezoneAwareDateTimeField(read_only=True) default_error_messages = { 'invalid-status-choice': "Unknown status: {status}", 'not-pending': "Application not pending", } def get_resource_uri(self, application): return reverse('api:team-application-detail', kwargs={ 'team_slug': self.context['team'].slug, 'id': application.id, }, request=self.context['request']) class Meta: model = Application fields = ( 'id', 'status', 'user', 'note', 'created', 'modified', 'resource_uri', ) read_only_fields = ( 'id', 'note', 'created', 'modified', ) def validate_status(self, status): if status not in (Application.STATUS_APPROVED, Application.STATUS_DENIED): self.fail('invalid-status-choice', status=status) return status def update(self, instance, validated_data): if instance.status != Application.STATUS_PENDING: self.fail('not-pending') if validated_data['status'] == Application.STATUS_APPROVED: instance.approve(self.context['user'], 'API') elif validated_data['status'] == Application.STATUS_DENIED: instance.deny(self.context['user'], 'API') return instance
class ActivitySerializer(serializers.ModelSerializer): type = serializers.IntegerField(source='action_type') user = serializers.CharField(source='user.username') comment = serializers.CharField(source='comment.content') created = TimezoneAwareDateTimeField(read_only=True) video = serializers.CharField(source='video.video_id') video_uri = serializers.HyperlinkedRelatedField( source='video', view_name='api:video-detail', lookup_field='video_id', read_only=True) language = serializers.CharField(source='new_language.language_code') language_url = serializers.SerializerMethodField() resource_uri = serializers.HyperlinkedIdentityField( view_name='api:activity-detail', lookup_field='id', ) def get_language_url(self, action): if not action.new_language: return None return reverse('api:subtitle-language-detail', kwargs={ 'video_id': action.new_language.video.video_id, 'language_code': action.new_language.language_code, }, request=self.context['request']) class Meta: model = Action fields = ('id', 'type', 'created', 'video', 'video_uri', 'language', 'language_url', 'user', 'comment', 'new_video_title', 'resource_uri')
class TeamNotificationSerializer(serializers.ModelSerializer): in_progress = serializers.BooleanField(source='is_in_progress') timestamp = TimezoneAwareDateTimeField(read_only=True) data = serializers.SerializerMethodField() resource_uri = serializers.SerializerMethodField() class Meta: model = TeamNotification fields = ('number', 'url', 'data', 'timestamp', 'in_progress', 'response_status', 'error_message', 'resource_uri') def get_data(self, notification): try: return json.loads(notification.data) except StandardError: # Error parsing the JSON. Just return data as a string return notification.data def get_resource_uri(self, notification): return reverse('api:team-notifications-detail', kwargs={ 'team_slug': self.context['team'].slug, 'number': notification.number, }, request=self.context['request'])
class NotesSerializer(serializers.Serializer): user = serializers.CharField(source='user.username', read_only=True) created = TimezoneAwareDateTimeField(read_only=True) body = serializers.CharField() def create(self, validated_data): return self.context['editor_notes'].post( self.context['user'], validated_data['body'])
class NotesSerializer(serializers.Serializer): user = UserField(read_only=True) created = TimezoneAwareDateTimeField(read_only=True) body = serializers.CharField() def create(self, validated_data): if self.context['editor_notes'] is None: raise PermissionDenied() return self.context['editor_notes'].post(self.context['user'], validated_data['body'])
class LegacyActivitySerializer(serializers.ModelSerializer): type = serializers.IntegerField(source='type_code') type_name = serializers.SlugField(source='type') user = serializers.CharField(source='user.username') comment = serializers.SerializerMethodField() new_video_title = serializers.SerializerMethodField() created = TimezoneAwareDateTimeField(read_only=True) video = serializers.CharField(source='video.video_id') video_uri = serializers.HyperlinkedRelatedField( source='video', view_name='api:video-detail', lookup_field='video_id', read_only=True) language = serializers.SerializerMethodField() language_url = serializers.SerializerMethodField() resource_uri = serializers.HyperlinkedIdentityField( view_name='api:activity-detail', lookup_field='id', ) def get_language(self, record): return record.language_code or None def get_comment(self, record): if record.type == 'comment-added': return record.get_related_obj().content else: return None def get_new_video_title(self, record): if record.type == 'video-title-changed': return record.get_related_obj().new_title else: return None def get_language_url(self, record): if not (record.language_code and record.video): return None return reverse('api:subtitle-language-detail', kwargs={ 'video_id': record.video.video_id, 'language_code': record.language_code, }, request=self.context['request']) class Meta: model = ActivityRecord fields = ('id', 'type', 'type_name', 'created', 'video', 'video_uri', 'language', 'language_url', 'user', 'comment', 'new_video_title', 'resource_uri')
class VideoURLSerializer(serializers.Serializer): created = TimezoneAwareDateTimeField(read_only=True) url = serializers.URLField(max_length=URL_MAX_LENGTH) primary = serializers.BooleanField(required=False) original = serializers.BooleanField(required=False) id = serializers.IntegerField(read_only=True) resource_uri = serializers.SerializerMethodField() videoid = serializers.CharField(read_only=True) type = serializers.SerializerMethodField(read_only=True) def get_resource_uri(self, video_url): return reverse('api:video-url-detail', kwargs={ 'video_id': self.context['video'].video_id, 'pk': video_url.id, }, request=self.context['request']) def get_type(self, video_url): vt = video_type_registrar[video_url.type] return vt.name def create(self, validated_data): try: new_url = self.context['video'].add_url(validated_data['url'], self.context['user']) except Video.DuplicateUrlError as e: raise serializers.ValidationError( "DuplicateUrlError for url: {}".format(e.video_url)) if validated_data.get('primary'): new_url.make_primary(self.context['user']) if ('original' in validated_data and validated_data['original'] != new_url.original): new_url.original = validated_data['original'] new_url.save() return new_url def update(self, video_url, validated_data): if ('original' in validated_data and validated_data['original'] != video_url.original): video_url.original = validated_data['original'] video_url.save() if validated_data.get('primary'): video_url.make_primary(self.context['user']) return video_url
class TeamNotificationSerializer(serializers.ModelSerializer): in_progress = serializers.BooleanField(source='is_in_progress') timestamp = TimezoneAwareDateTimeField(read_only=True) data = serializers.SerializerMethodField() class Meta: model = TeamNotification fields = ('number', 'url', 'data', 'timestamp', 'in_progress', 'response_status', 'error_message') def get_data(self, notification): try: return json.loads(notification.data) except StandardError: # Error parsing the JSON. Just return data as a string return notification.data
class VideoURLSerializer(serializers.Serializer): created = TimezoneAwareDateTimeField(read_only=True) url = serializers.CharField() primary = serializers.BooleanField(required=False) original = serializers.BooleanField(required=False) id = serializers.IntegerField(read_only=True) resource_uri = serializers.SerializerMethodField() videoid = serializers.CharField(read_only=True) type = serializers.SerializerMethodField(read_only=True) def get_resource_uri(self, video_url): return reverse('api:video-url-detail', kwargs={ 'video_id': self.context['video'].video_id, 'pk': video_url.id, }, request=self.context['request']) def get_type(self, video_url): vt = video_type_registrar[video_url.type] return vt.name def create(self, validated_data): vt = video_type_registrar.video_type_for_url(validated_data['url']) new_url = self.context['video'].videourl_set.create( url=validated_data['url'], original=validated_data.get('original', False), type=vt.abbreviation, added_by=self.context['user'], ) if validated_data.get('primary'): new_url.make_primary(self.context['user']) return new_url def update(self, video_url, validated_data): if ('original' in validated_data and validated_data['original'] != video_url.original): video_url.original = validated_data['original'] video_url.save() if validated_data.get('primary'): video_url.make_primary(self.context['user']) return video_url
class VideoSerializer(serializers.Serializer): # Note we could try to use ModelSerializer, but we are so far from the # default implementation that it makes more sense to not inherit. id = serializers.CharField(source='video_id', read_only=True) video_url = serializers.URLField(write_only=True, required=True) video_type = serializers.SerializerMethodField() primary_audio_language_code = LanguageCodeField(required=False, allow_blank=True) original_language = serializers.CharField(source='language', read_only=True) title = serializers.CharField(required=False, allow_blank=True) description = serializers.CharField(required=False, allow_blank=True) duration = serializers.IntegerField(required=False) thumbnail = serializers.URLField(required=False, allow_blank=True) created = TimezoneAwareDateTimeField(read_only=True) team = TeamSerializer(required=False, allow_null=True) project = ProjectSerializer(required=False, allow_null=True) all_urls = serializers.SerializerMethodField() metadata = VideoMetadataSerializer(required=False) languages = VideoLanguageShortSerializer(source='all_subtitle_languages', many=True, read_only=True) activity_uri = serializers.HyperlinkedIdentityField( view_name='api:video-activity', lookup_field='video_id', ) urls_uri = serializers.HyperlinkedIdentityField( view_name='api:video-url-list', lookup_field='video_id', ) subtitle_languages_uri = serializers.HyperlinkedIdentityField( view_name='api:subtitle-language-list', lookup_field='video_id', ) resource_uri = serializers.HyperlinkedIdentityField( view_name='api:video-detail', lookup_field='video_id') default_error_messages = { 'project-without-team': "Can't specify project without team", 'unknown-project': 'Unknown project: {project}', 'video-exists': 'Video already exists for {url}', 'invalid-url': 'Invalid URL: {url}', } class Meta: list_serializer_class = VideoListSerializer def __init__(self, *args, **kwargs): super(VideoSerializer, self).__init__(*args, **kwargs) if self.instance: # video_url should only be sent for creation self.fields['video_url'].read_only = True @property def team_video(self): if self.instance: return self.instance.get_team_video() else: return None def get_all_urls(self, video): video_urls = list(video.get_video_urls()) video_urls.sort(key=lambda vurl: vurl.primary, reverse=True) return [vurl.url for vurl in video_urls] def get_video_type(self, video): types = set() for url in video.get_video_urls(): types.add(url.type) if len(types) == 1: return types.pop() return "" def will_add_video_to_team(self): if not self.team_video: return 'team' in self.validated_data if 'team' in self.validated_data: if self.validated_data['team'] != self.team_video.team: return True if 'project' in self.validated_data: if self.validated_data['project'] != self.team_video.project: return True return False def will_remove_video_from_team(self): if 'team' not in self.validated_data or not self.team_video: return False return self.team_video.team != self.validated_data['team'] def to_internal_value(self, data): self.fixup_data(data) return super(VideoSerializer, self).to_internal_value(data) def validate(self, data): if data.get('project'): if not data.get('team'): self.fail('project-without-team') try: data['project'] = Project.objects.get(team=data['team'], slug=data['project']) except Project.DoesNotExist: self.fail('unknown-project', project=data['project']) return data def fixup_data(self, data): """Alter incoming data to support deprecated behavior.""" for name, value in data.items(): if value == '': # Remove any field has the empty string as its value # This is deprecated behavior form the old API. del data[name] elif name in ('team', 'project') and value == 'null': # Replace "null" with None for team/project data[name] = None def to_representation(self, video): data = super(VideoSerializer, self).to_representation(video) # convert blank language codes to None if video.primary_audio_language_code == '': data['primary_audio_language_code'] = None data['original_language'] = None return data def create(self, validated_data): def setup_video(video, video_url): for key in ('title', 'description', 'duration', 'thumbnail', 'primary_audio_language_code'): if validated_data.get(key): setattr(video, key, validated_data[key]) if validated_data.get('metadata'): video.update_metadata(validated_data['metadata'], commit=False) self._update_team(video, validated_data) try: return Video.add(validated_data['video_url'], self.context['user'], setup_video)[0] except VideoTypeError: self.fail('invalid-url', url=validated_data['video_url']) except Video.UrlAlreadyAdded: self.fail('video-exists', url=validated_data['video_url']) def update(self, video, validated_data): simple_fields = ( 'title', 'description', 'duration', 'thumbnail', 'primary_audio_language_code', ) for field_name in simple_fields: if field_name in validated_data: if field_name == "duration": if not getattr(video, field_name): setattr(video, field_name, validated_data[field_name]) else: setattr(video, field_name, validated_data[field_name]) if validated_data.get('metadata'): video.update_metadata(validated_data['metadata'], commit=True) self._update_team(video, validated_data) video.save() return video def _update_team(self, video, validated_data): if 'team' not in validated_data: return team = validated_data['team'] project = validated_data.get('project') team_video = video.get_team_video() if team is None: if team_video: team_video.delete() video.is_public = True else: if project is None: project = team.default_project if team_video: team_video.move_to(team, project, self.context['user']) else: TeamVideo.objects.create(video=video, team=team, project=project, added_by=self.context['user']) video.is_public = team.is_visible video.clear_team_video_cache()
class ActivitySerializer(serializers.ModelSerializer): type = serializers.SlugField() user = UserField(read_only=True) date = TimezoneAwareDateTimeField(source='created') video = serializers.CharField(source='video.video_id') language = serializers.SerializerMethodField() video_uri = serializers.HyperlinkedRelatedField( source='video', view_name='api:video-detail', lookup_field='video_id', read_only=True) language_uri = serializers.SerializerMethodField() def get_language(self, record): return record.language_code or None def get_language_uri(self, record): if not (record.language_code and record.video): return None return reverse('api:subtitle-language-detail', kwargs={ 'video_id': record.video.video_id, 'language_code': record.language_code, }, request=self.context['request']) def to_representation(self, record): data = super(ActivitySerializer, self).to_representation(record) extra_data_method_name = 'get_{}_extra'.format( record.type.replace('-', '_')) extra_field_method = getattr(self, extra_data_method_name, None) if extra_field_method: data.update(extra_field_method(record)) return data def get_video_url_added_extra(self, record): url_edit = record.get_related_obj() return { 'url': url_edit.new_url, } def get_video_url_edited_extra(self, record): url_edit = record.get_related_obj() return { 'old_url': url_edit.old_url, 'new_url': url_edit.new_url, } def get_video_url_deleted_extra(self, record): url_edit = record.get_related_obj() return { 'url': url_edit.old_url, } def get_video_deleted_extra(self, record): video_deletion = record.get_related_obj() return { 'title': video_deletion.title, } class Meta: model = ActivityRecord fields = ( 'type', 'date', 'user', 'video', 'language', 'video_uri', 'language_uri', )
class SubtitleLanguageSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) created = TimezoneAwareDateTimeField(read_only=True) language_code = LanguageCodeField() is_primary_audio_language = serializers.BooleanField(required=False) is_rtl = serializers.BooleanField(read_only=True) is_translation = serializers.SerializerMethodField() original_language_code = serializers.SerializerMethodField() name = serializers.CharField(source='get_language_code_display', read_only=True) title = serializers.CharField(source='get_title', read_only=True) description = serializers.CharField(source='get_description', read_only=True) metadata = VideoMetadataSerializer(required=False, read_only=True) subtitle_count = serializers.IntegerField(read_only=True, source='get_subtitle_count') subtitles_complete = serializers.BooleanField(required=False) versions = MiniSubtitleVersionsField(read_only=True) resource_uri = serializers.SerializerMethodField() default_error_messages = { 'language-exists': _('Language already created: {language_code}'), } class Meta: list_serializer_class = SubtitleLanguageListSerializer def __init__(self, *args, **kwargs): super(SubtitleLanguageSerializer, self).__init__(*args, **kwargs) if self.instance: self.fields['language_code'].read_only = True def get_is_translation(self, language): return compat.subtitlelanguage_is_translation(language) def get_original_language_code(self, language): return compat.subtitlelanguage_original_language_code(language) def get_resource_uri(self, language): kwargs = { 'video_id': language.video.video_id, 'language_code': language.language_code, } return reverse('api:subtitle-language-detail', kwargs=kwargs, request=self.context['request']) def to_representation(self, language): if 'versions' not in self.context: # For the list view, the SubtitleLanguageListSerializer generates # versions, for the detail view we need to generate versions # ourselves _fetch_versions([language], self.context) data = super(SubtitleLanguageSerializer, self).to_representation( language) data['num_versions'] = len(data['versions']) data['is_original'] = data['is_primary_audio_language'] self.add_reviewer_and_approver(data, language) return data def add_reviewer_and_approver(self, data, language): """Add the reviewer/approver fields.""" for version in self.context['versions'][language.id]: reviewer = version.get_reviewed_by() approver = version.get_approved_by() if reviewer: data['reviewer'] = reviewer.username if approver: data['approver'] = approver.username def validate_language_code(self, language_code): if (SubtitleLanguage.objects .filter(video=self.context['video'], language_code=language_code) .exists()): raise serializers.ValidationError("Language already exists") return language_code def create(self, validated_data): language = SubtitleLanguage.objects.create( video=self.context['video'], language_code=validated_data['language_code']) return self.update(language, validated_data) def update(self, language, validated_data): subtitles_complete = validated_data.get( 'subtitles_complete', self.initial_data.get('is_complete', None)) primary_audio_language = validated_data.get( 'is_primary_audio_language', self.initial_data.get('is_original', None)) video = self.context['video'] if subtitles_complete is not None: language.subtitles_complete = subtitles_complete try: language.save() except IntegrityError: self.fail('language-exists', language_code=language.language_code) if primary_audio_language is not None: video.primary_audio_language_code = language.language_code video.save() videos.tasks.video_changed_tasks.delay(video.pk) return language