Skip to content

Commit

Permalink
Merge pull request #937 from ubyssey/936-video
Browse files Browse the repository at this point in the history
936 video
  • Loading branch information
Xinlin He authored May 29, 2019
2 parents 6addc3f + 6fe4be7 commit 3e1031a
Show file tree
Hide file tree
Showing 30 changed files with 1,570 additions and 245 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
dist/
node_modules/

dispatch/static/manager/yarn-error.log
yarn.lock

.coverage
htmlcov/

# swap files
*.swp

*.vscode/

dispatch/tests/media

dispatch/apps/**/migrations/

65 changes: 52 additions & 13 deletions dispatch/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,6 @@ class Meta:
'updated_at'
)

class VideoSerializer(DispatchModelSerializer):
"""Serializes the Video model."""
class Meta:
model = Video
fields = (
'id',
'title',
'url',
)

class IssueSerializer(DispatchModelSerializer):
"""Serializes the Issue model."""

Expand Down Expand Up @@ -211,6 +201,55 @@ class Meta:
'name',
)

class VideoSerializer(DispatchModelSerializer):
"""Serializes the Video model."""

title = serializers.CharField(required=False, allow_null=True, allow_blank=True)
url = serializers.CharField(required=True, allow_null=True, allow_blank=False)

authors = AuthorSerializer(many=True, read_only=True)
author_ids = serializers.ListField(
write_only=True,
child=serializers.JSONField(),
validators=[AuthorValidator(True)])

tags = TagSerializer(many=True, read_only=True)
tag_ids = serializers.ListField(
write_only=True,
required=False,
child=serializers.IntegerField())

class Meta:
model = Video
fields = (
'id',
'title',
'url',
'authors',
'author_ids',
'tags',
'tag_ids',
'created_at',
'updated_at'
)

def create(self, validated_data):
return self.update(Video(), validated_data)

def update(self, instance, validated_data):
instance = super(VideoSerializer, self).update(instance, validated_data)

# Save authors
authors = validated_data.get('author_ids')
if authors:
instance.save_authors(authors)

tag_ids = validated_data.get('tag_ids', False)
if tag_ids != False:
instance.save_tags(tag_ids)

return instance

class ImageSerializer(serializers.HyperlinkedModelSerializer):
"""Serializes the Image model."""

Expand All @@ -227,7 +266,7 @@ class ImageSerializer(serializers.HyperlinkedModelSerializer):
author_ids = serializers.ListField(
write_only=True,
child=serializers.JSONField(),
validators=[AuthorValidator])
validators=[AuthorValidator(False)])

tags = TagSerializer(many=True, read_only=True)
tag_ids = serializers.ListField(
Expand Down Expand Up @@ -546,7 +585,7 @@ class SubsectionSerializer(DispatchModelSerializer):
author_ids = serializers.ListField(
write_only=True,
child=serializers.JSONField(),
validators=[AuthorValidator]
validators=[AuthorValidator(False)]
)
authors_string = serializers.CharField(source='get_author_string', read_only=True)
articles = SubsectionArticleSerializer(many=True, read_only=True, source='get_articles')
Expand Down Expand Up @@ -625,7 +664,7 @@ class ArticleSerializer(DispatchModelSerializer, DispatchPublishableSerializer):
author_ids = serializers.ListField(
write_only=True,
child=serializers.JSONField(),
validators=[AuthorValidator])
validators=[AuthorValidator(False)])
authors_string = serializers.CharField(source='get_author_string', read_only=True)

tags = TagSerializer(many=True, read_only=True)
Expand Down
30 changes: 18 additions & 12 deletions dispatch/api/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,24 @@ def SectionValidator(section_id, subsection_id, template, tags):
if errors:
raise ValidationError(errors)

def AuthorValidator(data):
"""Raise a ValidationError if data does not match the author format."""
if not isinstance(data, list):
# Convert single instance to a list
data = [data]

for author in data:
if 'person' not in author:
raise ValidationError('An author must contain a person.')
if 'type' in author and not isinstance(author['type'], str):
# If type is defined, it should be a string
raise ValidationError('The author type must be a string.')
class AuthorValidator(object):
def __init__(self, required):
self.required = required
def __call__(self, data):
"""Raise a ValidationError if data does not match the author format."""
if self.required and len(data) <= 0:
raise ValidationError('An author is required')

if not isinstance(data, list):
# Convert single instance to a list
data = [data]

for author in data:
if 'person' not in author:
raise ValidationError('An author must contain a person.')
if 'type' in author and not isinstance(author['type'], str):
# If type is defined, it should be a string
raise ValidationError('The author type must be a string.')

def TemplateValidator(template, template_data, tags, subsection_id):

Expand Down
68 changes: 41 additions & 27 deletions dispatch/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,6 @@ def get_queryset(self):
queryset = queryset.filter(name__icontains=q)
return queryset

class VideoViewSet(DispatchModelViewSet):
"""Viewset for Video model views."""
model = Video
serializer_class = VideoSerializer

def get_queryset(self):
queryset = Video.objects.all()
q = self.request.query_params.get('q', None)
if q is not None:
# If a search term (q) is present, filter queryset by term against `title`
queryset = queryset.filter(title__icontains=q)
return queryset

class ArticleViewSet(DispatchModelViewSet, DispatchPublishableMixin):
"""Viewset for Article model views."""
model = Article
Expand Down Expand Up @@ -272,20 +259,6 @@ def reset_password(self, request, pk=None):
else:
raise UnpermittedActionError()

class TagViewSet(DispatchModelViewSet):
"""Viewset for Tag model views."""
model = Tag
serializer_class = TagSerializer

def get_queryset(self):
queryset = Tag.objects.all()
q = self.request.query_params.get('q', None)
if q is not None:
# If a search term (q) is present, filter queryset by term against `name`
queryset = queryset.filter(name__icontains=q)

return queryset

class TopicViewSet(DispatchModelViewSet):
"""Viewset for Topic model views."""
model = Topic
Expand Down Expand Up @@ -325,6 +298,47 @@ def get_queryset(self):
queryset = queryset.filter(title__icontains=q)
return queryset

class TagViewSet(DispatchModelViewSet):
"""Viewset for Tag model views."""
model = Tag
serializer_class = TagSerializer

def get_queryset(self):
queryset = Tag.objects.all()
q = self.request.query_params.get('q', None)
if q is not None:
# If a search term (q) is present, filter queryset by term against `name`
queryset = queryset.filter(name__icontains=q)

return queryset

class VideoViewSet(DispatchModelViewSet):
"""Viewset for Video model views."""
model = Video
serializer_class = VideoSerializer
filter_backends = (filters.OrderingFilter,)
update_fields = ('title', 'authors', 'tags')

def get_queryset(self):
queryset = Video.objects.order_by('-updated_at')

author = self.request.query_params.get('author', None)
tags = self.request.query_params.getlist('tags', None)
q = self.request.query_params.get('q', None)

if author is not None:
queryset = queryset.filter(authors__person_id=author)

if tags is not None:
for tag in tags:
queryset = queryset.filter(tags__id=tag)

if q is not None:
# If a search term (q) is present, filter queryset by term against `title`
queryset = queryset.filter(title__icontains=q)

return queryset

class ImageViewSet(viewsets.ModelViewSet):
"""Viewset for Image model views."""
model = Image
Expand Down
51 changes: 51 additions & 0 deletions dispatch/migrations/0023_publishable_constraints_default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2019-05-05 22:30
from __future__ import unicode_literals

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('dispatch', '0022_publishable_constraints'),
]

operations = [
migrations.AlterField(
model_name='article',
name='head',
field=models.NullBooleanField(db_index=True, default=None),
),
migrations.AlterField(
model_name='article',
name='is_active',
field=models.NullBooleanField(default=True),
),
migrations.AlterField(
model_name='article',
name='is_published',
field=models.NullBooleanField(db_index=True, default=None),
),
migrations.AlterField(
model_name='page',
name='head',
field=models.NullBooleanField(db_index=True, default=None),
),
migrations.AlterField(
model_name='page',
name='is_active',
field=models.NullBooleanField(default=True),
),
migrations.AlterField(
model_name='page',
name='is_published',
field=models.NullBooleanField(db_index=True, default=None),
),
migrations.AlterField(
model_name='topic',
name='slug',
field=models.SlugField(max_length=255, unique=True),
),
]
37 changes: 37 additions & 0 deletions dispatch/migrations/0024_video_meta_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2019-05-05 22:30
from __future__ import unicode_literals

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
('dispatch', '0023_publishable_constraints_default'),
]

operations = [
migrations.AddField(
model_name='video',
name='authors',
field=models.ManyToManyField(related_name='video_authors', to='dispatch.Author'),
),
migrations.AddField(
model_name='video',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='video',
name='tags',
field=models.ManyToManyField(to='dispatch.Tag'),
),
migrations.AddField(
model_name='video',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
]
19 changes: 18 additions & 1 deletion dispatch/modules/content/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,27 @@ def get_absolute_url(self):
""" Returns page URL. """
return "%s%s/" % (settings.BASE_URL, self.slug)

class Video(Model):
class Video(Model, AuthorMixin):
title = CharField(max_length=255)
url = CharField(max_length=500)

authors = ManyToManyField(Author, related_name='video_authors')
tags = ManyToManyField('Tag')

created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)

AuthorModel = Author

def save_tags(self, tag_ids):
self.tags.clear()
for tag_id in tag_ids:
try:
tag = Tag.objects.get(id=int(tag_id))
self.tags.add(tag)
except Tag.DoesNotExist:
pass

class Image(Model, AuthorMixin):
img = ImageField(upload_to='images/%Y/%m')
title = CharField(max_length=255, blank=True, null=True)
Expand Down
1 change: 0 additions & 1 deletion dispatch/modules/content/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ def render_node(html, node, index):
html = render_node(html, node, index)
if (node['type'] == 'ad'):
index += 1
# return mark_safe(reduce(render_node, content, ''))
return mark_safe(html)

def content_to_json(content):
Expand Down
1 change: 1 addition & 0 deletions dispatch/static/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"react-redux-loading-bar": "^4.0.5",
"react-router": "^3.2.1",
"react-router-redux": "^4.0.8",
"react-youtube": "^7.9.0",
"redux": "^3.5.2",
"redux-logger": "^3.0.6",
"redux-promise-middleware": "^4.0.0",
Expand Down
Loading

0 comments on commit 3e1031a

Please sign in to comment.