From a6827e604de2c9bbbeebba386301f0d86e5a18ec Mon Sep 17 00:00:00 2001 From: sonny Date: Wed, 22 Jul 2020 23:49:18 +0200 Subject: [PATCH] Replace rest_framework_swagger with drf_yasg rest_framework is deprecated see https://github.com/marcgibbons/django-rest-swagger#django-rest-swagger-deprecated-2019-06-04 --- src/newsreader/js/pages/homepage/App.js | 4 +- .../js/pages/homepage/components/PostModal.js | 11 +- .../{feedlist => postlist}/PostItem.js | 13 +- .../FeedList.js => postlist/PostList.js} | 4 +- .../{feedlist => postlist}/filters.js | 0 src/newsreader/js/pages/homepage/constants.js | 3 + src/newsreader/news/collection/admin.py | 12 +- src/newsreader/news/collection/forms.py | 23 +- src/newsreader/news/collection/reddit.py | 93 +- src/newsreader/news/collection/serializers.py | 2 +- .../tests/endpoints/rule/detail/tests.py | 51 +- .../tests/endpoints/rule/list/tests.py | 70 +- .../collection/tests/feed/builder/tests.py | 6 +- .../collection/tests/feed/collector/tests.py | 14 +- .../tests/feed/duplicate_handler/tests.py | 40 +- .../collection/tests/reddit/builder/mocks.py | 2191 ++++++++++++++++- .../collection/tests/reddit/builder/tests.py | 201 +- .../tests/views/test_subreddit_views.py | 27 +- .../tests/endpoints/category/detail/tests.py | 24 +- .../tests/endpoints/category/list/tests.py | 76 +- .../core/tests/endpoints/post/detail/tests.py | 88 +- .../core/tests/endpoints/post/list/tests.py | 66 +- src/newsreader/news/core/tests/factories.py | 9 +- .../scss/components/post/_post.scss | 11 +- 24 files changed, 2728 insertions(+), 311 deletions(-) rename src/newsreader/js/pages/homepage/components/{feedlist => postlist}/PostItem.js (83%) rename src/newsreader/js/pages/homepage/components/{feedlist/FeedList.js => postlist/PostList.js} (95%) rename src/newsreader/js/pages/homepage/components/{feedlist => postlist}/filters.js (100%) diff --git a/src/newsreader/js/pages/homepage/App.js b/src/newsreader/js/pages/homepage/App.js index bdf0149..91cfa4e 100644 --- a/src/newsreader/js/pages/homepage/App.js +++ b/src/newsreader/js/pages/homepage/App.js @@ -6,7 +6,7 @@ import { isEqual } from 'lodash'; import { fetchCategories } from './actions/categories'; import Sidebar from './components/sidebar/Sidebar.js'; -import FeedList from './components/feedlist/FeedList.js'; +import PostList from './components/postlist/PostList.js'; import PostModal from './components/PostModal.js'; import Messages from '../../components/Messages.js'; @@ -19,7 +19,7 @@ class App extends React.Component { return ( <> - + {this.props.error && ( diff --git a/src/newsreader/js/pages/homepage/components/PostModal.js b/src/newsreader/js/pages/homepage/components/PostModal.js index acc700a..08033bc 100644 --- a/src/newsreader/js/pages/homepage/components/PostModal.js +++ b/src/newsreader/js/pages/homepage/components/PostModal.js @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import Cookies from 'js-cookie'; import { unSelectPost, markPostRead } from '../actions/posts.js'; +import { CATEGORY_TYPE, RULE_TYPE, FEED, SUBREDDIT } from '../constants.js'; import { formatDatetime } from '../../../utils.js'; class PostModal extends React.Component { @@ -43,6 +44,10 @@ class PostModal extends React.Component { const post = this.props.post; const publicationDate = formatDatetime(post.publicationDate); const titleClassName = post.read ? 'post__title post__title--read' : 'post__title'; + const ruleUrl = + this.props.rule.type === FEED + ? `/collection/rules/${this.props.rule.id}/` + : `/collection/rules/subreddits/${this.props.rule.id}/`; return (
@@ -70,11 +75,7 @@ class PostModal extends React.Component { )} - + {this.props.rule.name} diff --git a/src/newsreader/js/pages/homepage/components/feedlist/PostItem.js b/src/newsreader/js/pages/homepage/components/postlist/PostItem.js similarity index 83% rename from src/newsreader/js/pages/homepage/components/feedlist/PostItem.js rename to src/newsreader/js/pages/homepage/components/postlist/PostItem.js index a796916..9b64289 100644 --- a/src/newsreader/js/pages/homepage/components/feedlist/PostItem.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostItem.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { CATEGORY_TYPE, RULE_TYPE } from '../../constants.js'; +import { CATEGORY_TYPE, RULE_TYPE, FEED, SUBREDDIT } from '../../constants.js'; import { selectPost } from '../../actions/posts.js'; import { formatDatetime } from '../../../../utils.js'; @@ -14,6 +14,11 @@ class PostItem extends React.Component { ? 'posts__header posts__header--read' : 'posts__header'; + const ruleUrl = + rule.type === FEED + ? `/collection/rules/${rule.id}/` + : `/collection/rules/subreddits/${rule.id}/`; + return (
  • {this.props.selected.type == CATEGORY_TYPE && ( - + {rule.name} diff --git a/src/newsreader/js/pages/homepage/components/feedlist/FeedList.js b/src/newsreader/js/pages/homepage/components/postlist/PostList.js similarity index 95% rename from src/newsreader/js/pages/homepage/components/feedlist/FeedList.js rename to src/newsreader/js/pages/homepage/components/postlist/PostList.js index e679eed..cd57d6d 100644 --- a/src/newsreader/js/pages/homepage/components/feedlist/FeedList.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostList.js @@ -8,7 +8,7 @@ import { filterPosts } from './filters.js'; import LoadingIndicator from '../../../../components/LoadingIndicator.js'; import PostItem from './PostItem.js'; -class FeedList extends React.Component { +class PostList extends React.Component { checkScrollHeight = ::this.checkScrollHeight; componentDidMount() { @@ -83,4 +83,4 @@ const mapDispatchToProps = dispatch => ({ fetchPostsBySection: (rule, page = false) => dispatch(fetchPostsBySection(rule, page)), }); -export default connect(mapStateToProps, mapDispatchToProps)(FeedList); +export default connect(mapStateToProps, mapDispatchToProps)(PostList); diff --git a/src/newsreader/js/pages/homepage/components/feedlist/filters.js b/src/newsreader/js/pages/homepage/components/postlist/filters.js similarity index 100% rename from src/newsreader/js/pages/homepage/components/feedlist/filters.js rename to src/newsreader/js/pages/homepage/components/postlist/filters.js diff --git a/src/newsreader/js/pages/homepage/constants.js b/src/newsreader/js/pages/homepage/constants.js index 0e3f3d3..66b6365 100644 --- a/src/newsreader/js/pages/homepage/constants.js +++ b/src/newsreader/js/pages/homepage/constants.js @@ -1,2 +1,5 @@ export const RULE_TYPE = 'RULE'; export const CATEGORY_TYPE = 'CATEGORY'; + +export const SUBREDDIT = 'subreddit'; +export const FEED = 'feed'; diff --git a/src/newsreader/news/collection/admin.py b/src/newsreader/news/collection/admin.py index e82dea5..c5a7c5c 100644 --- a/src/newsreader/news/collection/admin.py +++ b/src/newsreader/news/collection/admin.py @@ -6,7 +6,14 @@ from newsreader.news.collection.models import CollectionRule class CollectionRuleAdmin(admin.ModelAdmin): fields = ("url", "name", "timezone", "category", "favicon", "user") - list_display = ("name", "category", "url", "last_suceeded", "succeeded") + list_display = ( + "name", + "type_display", + "category", + "url", + "last_suceeded", + "succeeded", + ) list_filter = ("user",) def save_model(self, request, obj, form, change): @@ -14,5 +21,8 @@ class CollectionRuleAdmin(admin.ModelAdmin): obj.user = request.user obj.save() + def type_display(self, rule): + return rule.get_type_display() + admin.site.register(CollectionRule, CollectionRuleAdmin) diff --git a/src/newsreader/news/collection/forms.py b/src/newsreader/news/collection/forms.py index a8aac52..604500d 100644 --- a/src/newsreader/news/collection/forms.py +++ b/src/newsreader/news/collection/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ @@ -6,15 +7,17 @@ import pytz from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.reddit import REDDIT_API_URL from newsreader.news.core.models import Category def get_reddit_help_text(): return mark_safe( - "Only subreddits are supported. For example: " - "https://www.reddit.com/r/aww." - " Note that subreddit urls should NOT include 'www' because Reddit is picky." + "Only subreddits are supported" + " see the 'listings' section in the reddit API docs." + " For example: https://oauth.reddit.com/r/aww" ) @@ -65,15 +68,19 @@ class SubRedditRuleForm(CollectionRuleForm): timezone = None + def clean_url(self): + url = self.cleaned_data["url"] + + if not url.startswith(REDDIT_API_URL): + raise ValidationError(_("This does not look like an Reddit API URL")) + + return url + def save(self, commit=True): instance = super().save(commit=False) instance.type = RuleTypeChoices.subreddit instance.timezone = str(pytz.utc) - instance.user = self.user - - if not instance.url.endswith(".json"): - instance.url = f"{instance.url}.json" if commit: instance.save() diff --git a/src/newsreader/news/collection/reddit.py b/src/newsreader/news/collection/reddit.py index 1e2837b..557271c 100644 --- a/src/newsreader/news/collection/reddit.py +++ b/src/newsreader/news/collection/reddit.py @@ -10,6 +10,7 @@ from uuid import uuid4 from django.conf import settings from django.core.cache import cache from django.utils import timezone +from django.utils.html import format_html import bleach import pytz @@ -42,6 +43,12 @@ REDDIT_API_URL = "https://oauth.reddit.com" RATE_LIMIT = 60 RATE_LIMIT_DURATION = timedelta(seconds=60) +REDDIT_IMAGE_EXTENSIONS = (".jpg", ".png", ".gif") +REDDIT_VIDEO_EXTENSIONS = (".mp4", ".gifv", ".webm") + +# see type prefixes on https://www.reddit.com/dev/api/ +REDDIT_POST = "t3" + def get_reddit_authorization_url(user): state = str(uuid4()) @@ -114,30 +121,72 @@ class RedditBuilder(Builder): results = {} for post in posts: - if not "data" in post: + if not "data" in post or post["kind"] != REDDIT_POST: continue - remote_identifier = post["data"]["id"] - title = truncate_text(Post, "title", post["data"]["title"]) - author = truncate_text(Post, "author", post["data"]["author"]) - url_fragment = f"{post['data']['permalink']}" + data = post["data"] + + remote_identifier = data["id"] + title = truncate_text(Post, "title", data["title"]) + author = truncate_text(Post, "author", data["author"]) + post_url_fragment = data["permalink"] + direct_url = data["url"] + is_text_post = data["is_self"] if remote_identifier in results: continue - uncleaned_body = post["data"]["selftext_html"] - unescaped_body = unescape(uncleaned_body) if uncleaned_body else "" - body = ( - bleach.clean( - unescaped_body, - tags=WHITELISTED_TAGS, - attributes=WHITELISTED_ATTRIBUTES, - strip=True, - strip_comments=True, + if is_text_post: + uncleaned_body = data["selftext_html"] + unescaped_body = unescape(uncleaned_body) if uncleaned_body else "" + body = ( + bleach.clean( + unescaped_body, + tags=WHITELISTED_TAGS, + attributes=WHITELISTED_ATTRIBUTES, + strip=True, + strip_comments=True, + ) + if unescaped_body + else "" + ) + elif direct_url.endswith(REDDIT_IMAGE_EXTENSIONS): + body = format_html( + "
    {title}
    ", + url=direct_url, + title=title, + ) + elif data["is_video"]: + video_info = data["secure_media"]["reddit_video"] + + body = format_html( + "
    ", + url=video_info["fallback_url"], + ) + elif direct_url.endswith(REDDIT_VIDEO_EXTENSIONS): + extension = next( + extension.replace(".", "") + for extension in REDDIT_VIDEO_EXTENSIONS + if direct_url.endswith(extension) + ) + + if extension == "gifv": + body = format_html( + "
    ", + url=direct_url.replace(extension, "mp4"), + ) + else: + body = format_html( + "
    ", + url=direct_url, + extension=extension, + ) + else: + body = format_html( + "", + url=direct_url, + title=title, ) - if unescaped_body - else "" - ) try: parsed_date = datetime.fromtimestamp(post["data"]["created_utc"]) @@ -146,12 +195,12 @@ class RedditBuilder(Builder): logging.warning(f"Failed parsing timestamp from {url_fragment}") created_date = timezone.now() - data = { + post_data = { "remote_identifier": remote_identifier, "title": title, "body": body, "author": author, - "url": f"{REDDIT_URL}{url_fragment}", + "url": f"{REDDIT_URL}{post_url_fragment}", "publication_date": created_date, "rule": rule, } @@ -159,13 +208,13 @@ class RedditBuilder(Builder): if remote_identifier in self.existing_posts: existing_post = self.existing_posts[remote_identifier] - for key, value in data.items(): + for key, value in post_data.items(): setattr(existing_post, key, value) results[existing_post.remote_identifier] = existing_post continue - results[remote_identifier] = Post(**data) + results[remote_identifier] = Post(**post_data) return results.values() @@ -271,7 +320,7 @@ class RedditClient(Client): yield response_data except StreamDeniedException as e: - logger.exception( + logger.warning( f"Access token expired for user {stream.user.pk}" ) diff --git a/src/newsreader/news/collection/serializers.py b/src/newsreader/news/collection/serializers.py index 640d16e..04bdba5 100644 --- a/src/newsreader/news/collection/serializers.py +++ b/src/newsreader/news/collection/serializers.py @@ -12,4 +12,4 @@ class RuleSerializer(serializers.ModelSerializer): class Meta: model = CollectionRule - fields = ("id", "name", "url", "favicon", "category", "user", "unread") + fields = ("id", "type", "name", "url", "favicon", "category", "user", "unread") diff --git a/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py index 02f7334..8dfe6ed 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py @@ -4,9 +4,9 @@ from django.test import TestCase from django.urls import reverse from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.tests.factories import CollectionRuleFactory +from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.models import Post -from newsreader.news.core.tests.factories import CategoryFactory, PostFactory +from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory class CollectionRuleDetailViewTestCase(TestCase): @@ -15,7 +15,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.client.force_login(self.user) def test_simple(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) response = self.client.get( reverse("api:news:collection:rules-detail", args=[rule.pk]) @@ -29,6 +29,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertTrue("url" in data) self.assertTrue("favicon" in data) self.assertTrue("category" in data) + self.assertTrue("type" in data) def test_not_known(self): response = self.client.get( @@ -40,7 +41,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(data["detail"], "Not found.") def test_post(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) response = self.client.post( reverse("api:news:collection:rules-detail", args=[rule.pk]) @@ -51,7 +52,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(data["detail"], 'Method "POST" not allowed.') def test_patch(self): - rule = CollectionRuleFactory(name="BBC", user=self.user) + rule = FeedFactory(name="BBC", user=self.user) response = self.client.patch( reverse("api:news:collection:rules-detail", args=[rule.pk]), @@ -67,7 +68,7 @@ class CollectionRuleDetailViewTestCase(TestCase): old_category = CategoryFactory(user=self.user) new_category = CategoryFactory(user=self.user) - rule = CollectionRuleFactory(name="BBC", category=old_category, user=self.user) + rule = FeedFactory(name="BBC", category=old_category, user=self.user) response = self.client.patch( reverse("api:news:collection:rules-detail", args=[rule.pk]), @@ -80,7 +81,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(data["category"], new_category.pk) def test_identifier_cannot_be_changed(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) response = self.client.patch( reverse("api:news:collection:rules-detail", args=[rule.pk]), @@ -93,7 +94,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(data["id"], rule.pk) def test_category_change(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) category = CategoryFactory(user=self.user) response = self.client.patch( @@ -108,7 +109,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(data["category"], category.pk) def test_put(self): - rule = CollectionRuleFactory(name="BBC", user=self.user) + rule = FeedFactory(name="BBC", user=self.user) response = self.client.put( reverse("api:news:collection:rules-detail", args=[rule.pk]), @@ -121,7 +122,7 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(data["name"], "BBC") def test_delete(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) response = self.client.delete( reverse("api:news:collection:rules-detail", args=[rule.pk]) @@ -132,7 +133,7 @@ class CollectionRuleDetailViewTestCase(TestCase): def test_rule_with_unauthenticated_user(self): self.client.logout() - rule = CollectionRuleFactory(name="BBC", user=self.user) + rule = FeedFactory(name="BBC", user=self.user) response = self.client.patch( reverse("api:news:collection:rules-detail", args=[rule.pk]), @@ -144,7 +145,7 @@ class CollectionRuleDetailViewTestCase(TestCase): def test_rule_with_unauthorized_user(self): other_user = UserFactory() - rule = CollectionRuleFactory(name="BBC", user=other_user) + rule = FeedFactory(name="BBC", user=other_user) response = self.client.patch( reverse("api:news:collection:rules-detail", args=[rule.pk]), @@ -155,10 +156,10 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(response.status_code, 403) def test_read_count(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) - PostFactory.create_batch(size=20, read=False, rule=rule) - PostFactory.create_batch(size=20, read=True, rule=rule) + FeedPostFactory.create_batch(size=20, read=False, rule=rule) + FeedPostFactory.create_batch(size=20, read=True, rule=rule) response = self.client.get( reverse("api:news:collection:rules-detail", args=[rule.pk]) @@ -175,9 +176,9 @@ class CollectionRuleReadTestCase(TestCase): self.client.force_login(self.user) def test_rule_read(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) - PostFactory.create_batch(size=20, read=False, rule=rule) + FeedPostFactory.create_batch(size=20, read=False, rule=rule) response = self.client.post( reverse("api:news:collection:rules-read", args=[rule.pk]) @@ -197,9 +198,9 @@ class CollectionRuleReadTestCase(TestCase): def test_unauthenticated_user(self): self.client.logout() - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) - PostFactory.create_batch(size=20, read=False, rule=rule) + FeedPostFactory.create_batch(size=20, read=False, rule=rule) response = self.client.post( reverse("api:news:collection:rules-read", args=[rule.pk]) @@ -209,9 +210,9 @@ class CollectionRuleReadTestCase(TestCase): def test_unauthorized_user(self): other_user = UserFactory() - rule = CollectionRuleFactory(user=other_user) + rule = FeedFactory(user=other_user) - PostFactory.create_batch(size=20, read=False, rule=rule) + FeedPostFactory.create_batch(size=20, read=False, rule=rule) response = self.client.post( reverse("api:news:collection:rules-read", args=[rule.pk]) @@ -221,7 +222,7 @@ class CollectionRuleReadTestCase(TestCase): self.assertEquals(Post.objects.filter(read=False).count(), 20) def test_get(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) response = self.client.get( reverse("api:news:collection:rules-read", args=[rule.pk]) @@ -230,7 +231,7 @@ class CollectionRuleReadTestCase(TestCase): self.assertEquals(response.status_code, 405) def test_patch(self): - rule = CollectionRuleFactory(name="BBC", user=self.user) + rule = FeedFactory(name="BBC", user=self.user) response = self.client.patch( reverse("api:news:collection:rules-read", args=[rule.pk]), @@ -241,7 +242,7 @@ class CollectionRuleReadTestCase(TestCase): self.assertEquals(response.status_code, 405) def test_put(self): - rule = CollectionRuleFactory(name="BBC", user=self.user) + rule = FeedFactory(name="BBC", user=self.user) response = self.client.put( reverse("api:news:collection:rules-read", args=[rule.pk]), @@ -252,7 +253,7 @@ class CollectionRuleReadTestCase(TestCase): self.assertEquals(response.status_code, 405) def test_delete(self): - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) response = self.client.delete( reverse("api:news:collection:rules-read", args=[rule.pk]) diff --git a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py index 19d2029..4d1ba8f 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py @@ -8,8 +8,8 @@ from django.urls import reverse import pytz from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.tests.factories import CollectionRuleFactory -from newsreader.news.core.tests.factories import CategoryFactory, PostFactory +from newsreader.news.collection.tests.factories import FeedFactory +from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory class RuleListViewTestCase(TestCase): @@ -18,7 +18,7 @@ class RuleListViewTestCase(TestCase): self.client.force_login(self.user) def test_simple(self): - CollectionRuleFactory.create_batch(size=3, user=self.user) + FeedFactory.create_batch(size=3, user=self.user) response = self.client.get(reverse("api:news:collection:rules-list")) data = response.json() @@ -30,19 +30,19 @@ class RuleListViewTestCase(TestCase): def test_ordering(self): rules = [ - CollectionRuleFactory( + FeedFactory( created=datetime.combine( date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc ), user=self.user, ), - CollectionRuleFactory( + FeedFactory( created=datetime.combine( date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc ), user=self.user, ), - CollectionRuleFactory( + FeedFactory( created=datetime.combine( date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc ), @@ -63,7 +63,7 @@ class RuleListViewTestCase(TestCase): self.assertEquals(data["results"][2]["id"], rules[0].pk) def test_pagination_count(self): - CollectionRuleFactory.create_batch(size=80, user=self.user) + FeedFactory.create_batch(size=80, user=self.user) response = self.client.get( reverse("api:news:collection:rules-list"), {"count": 30} @@ -124,7 +124,7 @@ class RuleListViewTestCase(TestCase): def test_rules_with_unauthenticated_user(self): self.client.logout() - CollectionRuleFactory.create_batch(size=3, user=self.user) + FeedFactory.create_batch(size=3, user=self.user) response = self.client.get(reverse("api:news:collection:rules-list")) @@ -132,7 +132,7 @@ class RuleListViewTestCase(TestCase): def test_rules_with_unauthorized_user(self): other_user = UserFactory() - CollectionRuleFactory.create_batch(size=3, user=other_user) + FeedFactory.create_batch(size=3, user=other_user) response = self.client.get(reverse("api:news:collection:rules-list")) data = response.json() @@ -149,8 +149,8 @@ class NestedRuleListViewTestCase(TestCase): self.client.force_login(self.user) def test_simple(self): - rule = CollectionRuleFactory.create(user=self.user) - PostFactory.create_batch(size=5, rule=rule) + rule = FeedFactory.create(user=self.user) + FeedPostFactory.create_batch(size=5, rule=rule) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) @@ -164,8 +164,8 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(data["count"], 5) def test_pagination(self): - rule = CollectionRuleFactory.create(user=self.user) - PostFactory.create_batch(size=80, rule=rule) + rule = FeedFactory.create(user=self.user) + FeedPostFactory.create_batch(size=80, rule=rule) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), @@ -178,7 +178,7 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(len(data["results"]), 30) def test_empty(self): - rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) @@ -197,7 +197,7 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(response.status_code, 404) def test_post(self): - rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) response = self.client.post( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), @@ -210,7 +210,7 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(data["detail"], 'Method "POST" not allowed.') def test_patch(self): - rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) response = self.client.patch( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), @@ -223,7 +223,7 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): - rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) response = self.client.put( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), @@ -236,7 +236,7 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): - rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) response = self.client.delete( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), @@ -251,7 +251,7 @@ class NestedRuleListViewTestCase(TestCase): def test_rule_with_unauthenticated_user(self): self.client.logout() - rule = CollectionRuleFactory(user=self.user) + rule = FeedFactory(user=self.user) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) @@ -261,7 +261,7 @@ class NestedRuleListViewTestCase(TestCase): def test_rule_with_unauthorized_user(self): other_user = UserFactory() - rule = CollectionRuleFactory(user=other_user) + rule = FeedFactory(user=other_user) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) @@ -270,26 +270,24 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(response.status_code, 403) def test_posts_ordering(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) posts = [ - PostFactory( + FeedPostFactory( title="I'm the first post", rule=rule, publication_date=datetime.combine( date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc ), ), - PostFactory( + FeedPostFactory( title="I'm the second post", rule=rule, publication_date=datetime.combine( date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc ), ), - PostFactory( + FeedPostFactory( title="I'm the third post", rule=rule, publication_date=datetime.combine( @@ -313,11 +311,11 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(data["results"][2]["id"], posts[0].pk) def test_only_posts_from_rule_are_returned(self): - rule = CollectionRuleFactory.create(user=self.user) - other_rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) + other_rule = FeedFactory.create(user=self.user) - PostFactory.create_batch(size=5, rule=rule) - PostFactory.create_batch(size=5, rule=other_rule) + FeedPostFactory.create_batch(size=5, rule=rule) + FeedPostFactory.create_batch(size=5, rule=other_rule) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) @@ -334,10 +332,10 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(post["rule"], rule.pk) def test_unread_posts(self): - rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) - PostFactory.create_batch(size=10, rule=rule, read=False) - PostFactory.create_batch(size=10, rule=rule, read=True) + FeedPostFactory.create_batch(size=10, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), @@ -354,10 +352,10 @@ class NestedRuleListViewTestCase(TestCase): self.assertEquals(post["read"], False) def test_read_posts(self): - rule = CollectionRuleFactory.create(user=self.user) + rule = FeedFactory.create(user=self.user) - PostFactory.create_batch(size=20, rule=rule, read=False) - PostFactory.create_batch(size=10, rule=rule, read=True) + FeedPostFactory.create_batch(size=20, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}), diff --git a/src/newsreader/news/collection/tests/feed/builder/tests.py b/src/newsreader/news/collection/tests/feed/builder/tests.py index 7069f96..c3e60e0 100644 --- a/src/newsreader/news/collection/tests/feed/builder/tests.py +++ b/src/newsreader/news/collection/tests/feed/builder/tests.py @@ -11,7 +11,7 @@ from freezegun import freeze_time from newsreader.news.collection.feed import FeedBuilder from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.models import Post -from newsreader.news.core.tests.factories import PostFactory +from newsreader.news.core.tests.factories import FeedPostFactory from .mocks import * @@ -287,11 +287,11 @@ class FeedBuilderTestCase(TestCase): rule = FeedFactory() mock_stream = MagicMock(rule=rule) - existing_first_post = PostFactory.create( + existing_first_post = FeedPostFactory.create( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule ) - existing_second_post = PostFactory.create( + existing_second_post = FeedPostFactory.create( remote_identifier="a5479c66-8fae-11e9-8422-00163ef6bee7", rule=rule ) diff --git a/src/newsreader/news/collection/tests/feed/collector/tests.py b/src/newsreader/news/collection/tests/feed/collector/tests.py index b0fc7cf..5a1bac1 100644 --- a/src/newsreader/news/collection/tests/feed/collector/tests.py +++ b/src/newsreader/news/collection/tests/feed/collector/tests.py @@ -21,7 +21,7 @@ from newsreader.news.collection.feed import FeedCollector from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.collection.utils import build_publication_date from newsreader.news.core.models import Post -from newsreader.news.core.tests.factories import PostFactory +from newsreader.news.core.tests.factories import FeedPostFactory from .mocks import duplicate_mock, empty_mock, multiple_mock, multiple_update_mock @@ -143,7 +143,7 @@ class FeedCollectorTestCase(TestCase): struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), pytz.utc ) - first_post = PostFactory( + first_post = FeedPostFactory( url="https://www.bbc.co.uk/news/world-us-canada-48338168", title="Trump's 'genocidal taunts' will not end Iran - Zarif", body="Foreign Minister Mohammad Javad Zarif says the US " @@ -156,7 +156,7 @@ class FeedCollectorTestCase(TestCase): struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)), pytz.utc ) - second_post = PostFactory( + second_post = FeedPostFactory( url="https://www.bbc.co.uk/news/technology-48334739", title="Huawei's Android loss: How it affects you", body="Google's move to end business ties with Huawei will " @@ -169,7 +169,7 @@ class FeedCollectorTestCase(TestCase): struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)), pytz.utc ) - third_post = PostFactory( + third_post = FeedPostFactory( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Police are investigating the messages while an MP " @@ -194,7 +194,7 @@ class FeedCollectorTestCase(TestCase): self.mocked_parse.return_value = multiple_update_mock rule = FeedFactory() - first_post = PostFactory( + first_post = FeedPostFactory( remote_identifier="https://www.bbc.co.uk/news/world-us-canada-48338168", url="https://www.bbc.co.uk/", title="Trump", @@ -203,7 +203,7 @@ class FeedCollectorTestCase(TestCase): rule=rule, ) - second_post = PostFactory( + second_post = FeedPostFactory( remote_identifier="https://www.bbc.co.uk/news/technology-48334739", url="https://www.bbc.co.uk/", title="Huawei's Android loss: How it affects you", @@ -212,7 +212,7 @@ class FeedCollectorTestCase(TestCase): rule=rule, ) - third_post = PostFactory( + third_post = FeedPostFactory( remote_identifier="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", diff --git a/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py b/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py index 18a6c6c..941de66 100644 --- a/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py +++ b/src/newsreader/news/collection/tests/feed/duplicate_handler/tests.py @@ -8,7 +8,7 @@ from freezegun import freeze_time from newsreader.news.collection.feed import FeedDuplicateHandler from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.models import Post -from newsreader.news.core.tests.factories import PostFactory +from newsreader.news.core.tests.factories import FeedPostFactory @freeze_time("2019-10-30 12:30:00") @@ -19,17 +19,17 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_with_remote_identifiers(self): rule = FeedFactory() - existing_post = PostFactory.create( + existing_post = FeedPostFactory.create( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule ) - new_posts = PostFactory.build_batch( + new_posts = FeedPostFactory.build_batch( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", publication_date=timezone.now() - timedelta(days=7), rule=rule, size=5, ) - last_post = PostFactory.build( + last_post = FeedPostFactory.build( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", publication_date=timezone.now(), rule=rule, @@ -54,7 +54,7 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_with_different_remote_identifiers(self): rule = FeedFactory() - existing_post = PostFactory( + existing_post = FeedPostFactory( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", url="https://bbc.com", title="New post", @@ -63,7 +63,7 @@ class FeedDuplicateHandlerTestCase(TestCase): rule=rule, ) - new_posts = PostFactory.build_batch( + new_posts = FeedPostFactory.build_batch( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7Q", url="https://bbc.com", title="New post", @@ -72,7 +72,7 @@ class FeedDuplicateHandlerTestCase(TestCase): rule=rule, size=5, ) - last_post = PostFactory.build( + last_post = FeedPostFactory.build( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7Q", url="https://bbc.com", title="New post", @@ -100,7 +100,7 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_in_recent_database(self): rule = FeedFactory() - existing_post = PostFactory( + existing_post = FeedPostFactory( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", @@ -109,7 +109,7 @@ class FeedDuplicateHandlerTestCase(TestCase): rule=rule, ) - new_posts = PostFactory.build_batch( + new_posts = FeedPostFactory.build_batch( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", @@ -119,7 +119,7 @@ class FeedDuplicateHandlerTestCase(TestCase): size=5, ) - last_post = PostFactory.build( + last_post = FeedPostFactory.build( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", @@ -147,17 +147,17 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_multiple_existing_entries_with_identifier(self): rule = FeedFactory() - PostFactory.create_batch( + FeedPostFactory.create_batch( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule, size=5 ) - new_posts = PostFactory.build_batch( + new_posts = FeedPostFactory.build_batch( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", publication_date=timezone.now() - timedelta(hours=5), rule=rule, size=5, ) - last_post = PostFactory.build( + last_post = FeedPostFactory.build( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", publication_date=timezone.now() - timedelta(minutes=5), rule=rule, @@ -189,7 +189,7 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_outside_time_slot(self): rule = FeedFactory() - existing_post = PostFactory( + existing_post = FeedPostFactory( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", @@ -198,7 +198,7 @@ class FeedDuplicateHandlerTestCase(TestCase): rule=rule, ) - new_posts = PostFactory.build_batch( + new_posts = FeedPostFactory.build_batch( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", @@ -207,7 +207,7 @@ class FeedDuplicateHandlerTestCase(TestCase): rule=rule, size=5, ) - last_post = PostFactory.build( + last_post = FeedPostFactory.build( url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", title="Birmingham head teacher threatened over LGBT lessons", body="Google's move to end business ties with Huawei will affect current devices", @@ -235,14 +235,14 @@ class FeedDuplicateHandlerTestCase(TestCase): def test_duplicate_entries_in_collected_entries(self): rule = FeedFactory() - post_1 = PostFactory.build( + post_1 = FeedPostFactory.build( title="title got updated", body="body", url="https://bbc.com", publication_date=timezone.now(), rule=rule, ) - duplicate_post_1 = PostFactory.build( + duplicate_post_1 = FeedPostFactory.build( title="title got updated", body="body", url="https://bbc.com", @@ -250,11 +250,11 @@ class FeedDuplicateHandlerTestCase(TestCase): rule=rule, ) - post_2 = PostFactory.build( + post_2 = FeedPostFactory.build( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", publication_date=timezone.now(), ) - duplicate_post_2 = PostFactory.build( + duplicate_post_2 = FeedPostFactory.build( remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", publication_date=timezone.now() - timedelta(minutes=5), ) diff --git a/src/newsreader/news/collection/tests/reddit/builder/mocks.py b/src/newsreader/news/collection/tests/reddit/builder/mocks.py index fabc802..625ced3 100644 --- a/src/newsreader/news/collection/tests/reddit/builder/mocks.py +++ b/src/newsreader/news/collection/tests/reddit/builder/mocks.py @@ -741,7 +741,7 @@ unsanitized_mock = { "author_flair_richtext": [], "gildings": {}, "content_categories": None, - "is_self": False, + "is_self": True, "mod_note": None, "crosspost_parent_list": [ { @@ -1709,3 +1709,2192 @@ duplicate_mock = { "before": None, }, } + +image_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "SamLynn79", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_6c9cj", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594777552.0, + "created_utc": 1594748752.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 1, + "gildings": {"gid_2": 1}, + "hidden": False, + "hide_score": False, + "id": "hr64xh", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": True, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr64xh", + "no_follow": False, + "num_comments": 579, + "num_crossposts": 2, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "xWBh4hObZx0zmG_IDOHBLNN-_NZzEss2dAgm1sm9p1w", + "resolutions": [ + { + "height": 135, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=108&crop=smart&auto=webp&s=5374b8f3dff520eba8cf97b589ebc67206f130dc", + "width": 108, + }, + { + "height": 270, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=216&crop=smart&auto=webp&s=09d937a8db6f843d9fd34ee024cdfc6432dc0a13", + "width": 216, + }, + { + "height": 400, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=320&crop=smart&auto=webp&s=9ba3654c12cb54f6d9c2dce1b07c80ecd6ca9d06", + "width": 320, + }, + { + "height": 800, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=640&crop=smart&auto=webp&s=8c53747ae0f92b65fdd41f3aab60ebb8f8d4b1ca", + "width": 640, + }, + { + "height": 1200, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=960&crop=smart&auto=webp&s=5668a626da6cd69e23b6c01587783c6cc5817bea", + "width": 960, + }, + { + "height": 1350, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?width=1080&crop=smart&auto=webp&s=8fdd61aed8718109f3739cb532d96be31192b9a0", + "width": 1080, + }, + ], + "source": { + "height": 1800, + "url": "https://preview.redd.it/cm2qybia1va51.jpg?auto=webp&s=17b817b8d0e35bddc7f605d242cd7d116ef8e235", + "width": 1440, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 23419, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/0X39S2jBL66zQCUbJAtlRKeswI8uUxf3-7vmog0VLjc.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Ya’ll, I just can’t... this is my " + "son, Judah. My wife and I have no " + "idea how we created such a " + "beautiful child.", + "top_awarded_type": None, + "total_awards_received": 4, + "treatment_tags": [], + "ups": 23419, + "upvote_ratio": 0.72, + "url": "https://i.redd.it/cm2qybia1va51.jpg", + "url_overridden_by_dest": "https://i.redd.it/cm2qybia1va51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "0_GG_0", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_70k94sn8", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594771808.0, + "created_utc": 1594743008.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr4bxo", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr4bxo", + "no_follow": False, + "num_comments": 248, + "num_crossposts": 4, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr4bxo/just_thought_yall_would_enjoy_my_goat_dressed_as/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "TSXyc6ZJGdCcHk7-wuWnJdVpqsa_t8hmVd4k_e3ofCA", + "resolutions": [ + { + "height": 144, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=108&crop=smart&auto=webp&s=ed5a11a7637acc66de48e30fd51d5019fa0c69f7", + "width": 108, + }, + { + "height": 288, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=216&crop=smart&auto=webp&s=a812bec268d8ea31dbb9dfe696e0798490538f5a", + "width": 216, + }, + { + "height": 426, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=320&crop=smart&auto=webp&s=1be4e3bdea19243b0a627bacb4c9e04f2d3569a7", + "width": 320, + }, + { + "height": 853, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=640&crop=smart&auto=webp&s=e73755c3f0b27bb0435d07aa60b32e091bed7957", + "width": 640, + }, + { + "height": 1280, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=960&crop=smart&auto=webp&s=8ab6972fffc4786503284a0253e91e9104f2d01e", + "width": 960, + }, + { + "height": 1440, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?width=1080&crop=smart&auto=webp&s=a1e554889179a7599786985679304fda706d83d6", + "width": 1080, + }, + ], + "source": { + "height": 4032, + "url": "https://preview.redd.it/4udujbu6kua51.jpg?auto=webp&s=3eefdef653e0a3a8a10090b804f0888ee6a1a163", + "width": 3024, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 16684, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/h3Ylp4kb0uJzAsST4ZZGsGN8WGxK4wjK2XrM9uUH5uc.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Just thought y’all would enjoy my " + "goat dressed as a tractor", + "top_awarded_type": None, + "total_awards_received": 2, + "treatment_tags": [], + "ups": 16684, + "upvote_ratio": 0.98, + "url": "https://i.redd.it/4udujbu6kua51.jpg", + "url_overridden_by_dest": "https://i.redd.it/4udujbu6kua51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "Mechanic619", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_4ptrdtz5", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594760700.0, + "created_utc": 1594731900.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {"gid_1": 1}, + "hidden": False, + "hide_score": False, + "id": "hr14y5", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr14y5", + "no_follow": False, + "num_comments": 1439, + "num_crossposts": 20, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr14y5/mosque_security_on_patrol/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "Qs_FmhJgYT8GWyxmDQ8kjBCs_w2V_77cvHvdqLJ7i4s", + "resolutions": [ + { + "height": 135, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=108&crop=smart&auto=webp&s=cf4c24ef4f9be86d186c143296bd1e14f15f960a", + "width": 108, + }, + { + "height": 270, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=216&crop=smart&auto=webp&s=308e2367a849334c32b579265ed738d9937bed71", + "width": 216, + }, + { + "height": 400, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=320&crop=smart&auto=webp&s=bc890f054dc34bb3f8607a70d088926afe113ff1", + "width": 320, + }, + { + "height": 800, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=640&crop=smart&auto=webp&s=e23a9bc2d8d1ac6ccefab7f30cfa9def741aaa25", + "width": 640, + }, + { + "height": 1201, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=960&crop=smart&auto=webp&s=4d294d1626046d27edc2a281c21ab10502b9ca4c", + "width": 960, + }, + { + "height": 1351, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?width=1080&crop=smart&auto=webp&s=a801e5d9d703204e8b1497d3038d6405b2ed1157", + "width": 1080, + }, + ], + "source": { + "height": 1413, + "url": "https://preview.redd.it/jk08ge66nta51.jpg?auto=webp&s=f4e87e2ad0f0e40ca4f7a08c2a894b234601f3ce", + "width": 1129, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 89133, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/GGHjIElMHDgefR0UdMXVk8CHeDUBhuZMY_QHjls4ynA.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Mosque security on patrol", + "top_awarded_type": None, + "total_awards_received": 3, + "treatment_tags": [], + "ups": 89133, + "upvote_ratio": 0.93, + "url": "https://i.redd.it/jk08ge66nta51.jpg", + "url_overridden_by_dest": "https://i.redd.it/jk08ge66nta51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "Amnesia19", + "author_cakeday": True, + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_1rqe7gk1", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594765470.0, + "created_utc": 1594736670.0, + "discussion_type": None, + "distinguished": None, + "domain": "i.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr2fv0", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr2fv0", + "no_follow": False, + "num_comments": 71, + "num_crossposts": 1, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr2fv0/the_look_my_dog_gives_my_grandpa/", + "pinned": False, + "post_hint": "image", + "preview": { + "enabled": True, + "images": [ + { + "id": "v0BbkKy6haXmUxmHz4oXygoR0E-cHkvZDACWL_s7STw", + "resolutions": [ + { + "height": 144, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=108&crop=smart&auto=webp&s=4e65e8ff55c02de0ebe79763c91fe43f51216717", + "width": 108, + }, + { + "height": 288, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=216&crop=smart&auto=webp&s=e2006e5fe7ac43f911c17dc7f185f33db24e3b52", + "width": 216, + }, + { + "height": 426, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=320&crop=smart&auto=webp&s=3dad39d5e48a1b176f7e87b2dd110fb0044b32d7", + "width": 320, + }, + { + "height": 853, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=640&crop=smart&auto=webp&s=2f8e86a3feca27a23a72d10b92aba1b79b80f7be", + "width": 640, + }, + { + "height": 1280, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=960&crop=smart&auto=webp&s=5ecdd44b728031f8e109f41f99841a1d6c8e86c8", + "width": 960, + }, + { + "height": 1440, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?width=1080&crop=smart&auto=webp&s=49555499040c0ac9958dabd98cbe4e90c054b2a7", + "width": 1080, + }, + ], + "source": { + "height": 4032, + "url": "https://preview.redd.it/y6q7bgzc1ua51.jpg?auto=webp&s=443e98e46a8a096e426ebdc256c45682f46ebe2a", + "width": 3024, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 13614, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/RWRuGJ7ZyBtjO6alY1vbc65TQzgng8RFRWnPG7WUkhE.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "The look my dog gives my grandpa", + "top_awarded_type": None, + "total_awards_received": 0, + "treatment_tags": [], + "ups": 13614, + "upvote_ratio": 0.99, + "url": "https://i.redd.it/y6q7bgzc1ua51.jpg", + "url_overridden_by_dest": "https://i.redd.it/y6q7bgzc1ua51.jpg", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +external_image_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "Captainbuttsreads", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_5qaat4af", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594770844.0, + "created_utc": 1594742044.0, + "crosspost_parent": "t3_gc6eq2", + "crosspost_parent_list": [], + "discussion_type": None, + "distinguished": None, + "domain": "gfycat.com", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr41am", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": False, + "is_robot_indexable": True, + "is_self": False, + "is_video": False, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": None, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr41am", + "no_follow": False, + "num_comments": 45, + "num_crossposts": 0, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr41am/excited_cows_have_a_new_brush/", + "pinned": False, + "post_hint": "link", + "preview": { + "enabled": False, + "images": [ + { + "id": "l5tVSe6B4QDc7wk6Z9WfCXr20D_rAOHerf6i0N53nNc", + "resolutions": [ + { + "height": 108, + "url": "https://external-preview.redd.it/R51JAzaGbva91vYxn9uWL3NwCzWJW5mrdVxb1idjtBg.jpg?width=108&crop=smart&auto=webp&s=f908e1fb9403194a31f9a0c1f056f59e0718201e", + "width": 108, + }, + { + "height": 216, + "url": "https://external-preview.redd.it/R51JAzaGbva91vYxn9uWL3NwCzWJW5mrdVxb1idjtBg.jpg?width=216&crop=smart&auto=webp&s=de377df68832a52419d83c06ea74a13de28b96e0", + "width": 216, + }, + ], + "source": { + "height": 250, + "url": "https://external-preview.redd.it/R51JAzaGbva91vYxn9uWL3NwCzWJW5mrdVxb1idjtBg.jpg?auto=webp&s=b4166cb5a350e6d0197381cdf8db702f8a760493", + "width": 250, + }, + "variants": {}, + } + ], + "reddit_video_preview": { + "dash_url": "https://v.redd.it/mimyo7z6ppa51/DASHPlaylist.mpd", + "duration": 33, + "fallback_url": "https://v.redd.it/mimyo7z6ppa51/DASH_480.mp4", + "height": 640, + "hls_url": "https://v.redd.it/mimyo7z6ppa51/HLSPlaylist.m3u8", + "is_gif": True, + "scrubber_media_url": "https://v.redd.it/mimyo7z6ppa51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + }, + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 3219, + "secure_media": None, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": False, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/NKTwvIU2xxoOMpzYNlYYstS2586x64Gi--52N0M-OJY.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Excited cows have a new brush!", + "top_awarded_type": None, + "total_awards_received": 0, + "treatment_tags": [], + "ups": 3219, + "upvote_ratio": 0.99, + "url": "http://gfycat.com/thatalivedogwoodclubgall", + "url_overridden_by_dest": "http://gfycat.com/thatalivedogwoodclubgall", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_78ni2", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallas’s cat kittens", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 93, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_huoldn", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.99, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 1933, + "total_awards_received": 0, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 1933, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://a.thumbs.redditmedia.com/j-D-Z79QQ6tGk0E3SGdb8GzqbLVUY3lu59tDaXbOYl8.jpg", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "image", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1595292144, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.imgur.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.imgur.com/usfMVUJ.jpg", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": False, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?auto=webp&s=2126d34a0134efa94ecab03917944709c8bc3305", + "width": 1024, + "height": 682, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=108&crop=smart&auto=webp&s=710a44f787b98a0a37ca543b7428917ee55b3c46", + "width": 108, + "height": 71, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=216&crop=smart&auto=webp&s=b1bcdd7734a3a569f99fa88c6be9447105e58276", + "width": 216, + "height": 143, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=320&crop=smart&auto=webp&s=1671bf09a7b73d0ca51cf2de884b37d6a3591d6a", + "width": 320, + "height": 213, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=640&crop=smart&auto=webp&s=9fcdddbaeaad13273e0b53a862c73c4fee9f7e3d", + "width": 640, + "height": 426, + }, + { + "url": "https://external-preview.redd.it/iPfDCL-dfB4_TmnGsMEXWJMzxXmBEMPuV5ejLkYBNVo.jpg?width=960&crop=smart&auto=webp&s=e531480236c0ae72b78f27dd88f2cedc9f73cccc", + "width": 960, + "height": 639, + }, + ], + "variants": {}, + "id": "oJ9pHVA-JhoodtgNlku8ZQv8FhtadS2r36wGLAriUtY", + } + ], + "enabled": True, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": False, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "huoldn", + "is_robot_indexable": True, + "report_reasons": None, + "author": "Ben_zyl", + "discussion_type": None, + "num_comments": 20, + "send_replies": True, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": None, + "permalink": "/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.imgur.com/usfMVUJ.jpg", + "subreddit_subscribers": 25723833, + "created_utc": 1595263344, + "num_crossposts": 0, + "media": None, + "is_video": False, + }, + }, + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +video_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "TommyLondoner", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_75bis9gi", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594767660.0, + "created_utc": 1594738860.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 1, + "gildings": {"gid_2": 1}, + "hidden": False, + "hide_score": False, + "id": "hr32jf", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/9avhmd5s7ua51/DASHPlaylist.mpd?a=1597351258%2CODVjMjcyMDkzOWE1NDBiNzUwNzVhNDUwYmE0MGNiNzk5MGRmZmZmMzBhZjIzNDAzYzczY2NkNzRjNTgyMjAzNQ%3D%3D&v=1&f=sd", + "duration": 78, + "fallback_url": "https://v.redd.it/9avhmd5s7ua51/DASH_360.mp4?source=fallback", + "height": 428, + "hls_url": "https://v.redd.it/9avhmd5s7ua51/HLSPlaylist.m3u8?a=1597351258%2CNjE4YTA0NjUwZWNmNjhjNTRhNmU4ZjBmNDMyYWYxOGYzZTNkZWM2YjViM2I2ZDZjZWNhYzY0ZGVmOWU0Y2EyYg%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/9avhmd5s7ua51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 258, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr32jf", + "no_follow": False, + "num_comments": 150, + "num_crossposts": 2, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr32jf/this_guy_definitely_loves_his_job/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "dX_mx_ZfJMwVn_pak9ZPQq8rMT_gPkW0_4gOzDxPSHM", + "resolutions": [ + { + "height": 179, + "url": "https://external-preview.redd.it/PMy-Z___DIG6aWnoyEy1VKottxLQFWCRSdHDV1a9N8w.png?width=108&crop=smart&format=pjpg&auto=webp&s=e0b8b68a78a8e9071bf56417ac6589bc8aff7634", + "width": 108, + }, + { + "height": 358, + "url": "https://external-preview.redd.it/PMy-Z___DIG6aWnoyEy1VKottxLQFWCRSdHDV1a9N8w.png?width=216&crop=smart&format=pjpg&auto=webp&s=8668c3c7ccbdacfe3376d8af4b1b49df9d6aec97", + "width": 216, + }, + ], + "source": { + "height": 428, + "url": "https://external-preview.redd.it/PMy-Z___DIG6aWnoyEy1VKottxLQFWCRSdHDV1a9N8w.png?format=pjpg&auto=webp&s=b0b6439fbe01c3f5d1bf1eae54a588cc745d3415", + "width": 258, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 9324, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/9avhmd5s7ua51/DASHPlaylist.mpd?a=1597351258%2CODVjMjcyMDkzOWE1NDBiNzUwNzVhNDUwYmE0MGNiNzk5MGRmZmZmMzBhZjIzNDAzYzczY2NkNzRjNTgyMjAzNQ%3D%3D&v=1&f=sd", + "duration": 78, + "fallback_url": "https://v.redd.it/9avhmd5s7ua51/DASH_360.mp4?source=fallback", + "height": 428, + "hls_url": "https://v.redd.it/9avhmd5s7ua51/HLSPlaylist.m3u8?a=1597351258%2CNjE4YTA0NjUwZWNmNjhjNTRhNmU4ZjBmNDMyYWYxOGYzZTNkZWM2YjViM2I2ZDZjZWNhYzY0ZGVmOWU0Y2EyYg%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/9avhmd5s7ua51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 258, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/ibsS3H5xMLDSVglh8NBYJ4cgIsXuqYVLJWbiYVTykXg.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "This guy definitely loves his job !", + "top_awarded_type": None, + "total_awards_received": 1, + "treatment_tags": [], + "ups": 9324, + "upvote_ratio": 0.96, + "url": "https://v.redd.it/9avhmd5s7ua51", + "url_overridden_by_dest": "https://v.redd.it/9avhmd5s7ua51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "LucileEsparza", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_5loa1v96", + "author_patreon_flair": False, + "author_premium": False, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594762969.0, + "created_utc": 1594734169.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr1r00", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": "lc", + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/eyvbxaeqtta51/DASHPlaylist.mpd?a=1597351258%2CYjJmMWE3ZGJmM2FhMzVkYzZlNjIzOTAwM2ZmZTBkYjAxMzE0NDY2MDIyNGRhOWViMTViZTE0NTlmMzkzM2JlYg%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback", + "height": 640, + "hls_url": "https://v.redd.it/eyvbxaeqtta51/HLSPlaylist.m3u8?a=1597351258%2CY2JiMmQ0MjliNmE5NTA5MDE3YjAyNmVkYTg2Yjg1YWYwYmJlNDE4ZGM1NjE4ZDU3YjkzYjJlMDE2ZmM4Yzk5MQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/eyvbxaeqtta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr1r00", + "no_follow": False, + "num_comments": 63, + "num_crossposts": 3, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "wrscJ_l9A6Q_Mn1NAg06I4o3W39bbNgTBYg2Xm_Vl8U", + "resolutions": [ + { + "height": 108, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=108&crop=smart&format=pjpg&auto=webp&s=f285ef95065be8a340e1cb7792d80a9640564eb6", + "width": 108, + }, + { + "height": 216, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=216&crop=smart&format=pjpg&auto=webp&s=6d26b4f8d7b16f0f02bc6ce6f35af889b43cf026", + "width": 216, + }, + { + "height": 320, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=320&crop=smart&format=pjpg&auto=webp&s=5d081467da187bd8c24e9c524583513ee6afe388", + "width": 320, + }, + { + "height": 640, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?width=640&crop=smart&format=pjpg&auto=webp&s=557369f302f18b35284ffaacaccf09986f755187", + "width": 640, + }, + ], + "source": { + "height": 640, + "url": "https://external-preview.redd.it/GkeXMqmDn03NOYgnIk0QDQAS8HfRacPrmYvyTUSv6w0.png?format=pjpg&auto=webp&s=cb0a79a2effe0323e862fb713dab76b39051afbb", + "width": 640, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 11007, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/eyvbxaeqtta51/DASHPlaylist.mpd?a=1597351258%2CYjJmMWE3ZGJmM2FhMzVkYzZlNjIzOTAwM2ZmZTBkYjAxMzE0NDY2MDIyNGRhOWViMTViZTE0NTlmMzkzM2JlYg%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback", + "height": 640, + "hls_url": "https://v.redd.it/eyvbxaeqtta51/HLSPlaylist.m3u8?a=1597351258%2CY2JiMmQ0MjliNmE5NTA5MDE3YjAyNmVkYTg2Yjg1YWYwYmJlNDE4ZGM1NjE4ZDU3YjkzYjJlMDE2ZmM4Yzk5MQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/eyvbxaeqtta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": False, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/WSBiDcoWPwAgSkt08uCI6TK7v_tdAdHmQHv7TePyTOs.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Cool catt and his clingy girlfriend", + "top_awarded_type": None, + "total_awards_received": 1, + "treatment_tags": [], + "ups": 11007, + "upvote_ratio": 0.99, + "url": "https://v.redd.it/eyvbxaeqtta51", + "url_overridden_by_dest": "https://v.redd.it/eyvbxaeqtta51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": False, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "memezzer", + "author_flair_background_color": "", + "author_flair_css_class": "k", + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": "dark", + "author_flair_type": "text", + "author_fullname": "t2_41jaebm4", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594759625.0, + "created_utc": 1594730825.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 0, + "gildings": {}, + "hidden": False, + "hide_score": False, + "id": "hr0uzh", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/y0mavwswjta51/DASHPlaylist.mpd?a=1597351258%2CYjU1NzFjOTE0YzY2OTdmODk3MGRiMGU4MjdhOGE5ODk2YWNiODQyMGUyOWRhNzI1M2U1MTEyZjBhOWZkZTZmMw%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/y0mavwswjta51/DASH_720.mp4?source=fallback", + "height": 960, + "hls_url": "https://v.redd.it/y0mavwswjta51/HLSPlaylist.m3u8?a=1597351258%2CODk4NTdhMzA3NmY2ZmY2NGQxMmI2ZjcyMzk0ZTFhOTdhOGI4NGQ1NjBiMzNiMmVmZDBhMTQ4MGRkOWJlOWU1YQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/y0mavwswjta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 960, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hr0uzh", + "no_follow": False, + "num_comments": 86, + "num_crossposts": 3, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hr0uzh/good_pillow/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "neoTdGv5lMArlfu6euGUK_v_O87Lfmdrrz1ePTwzp1w", + "resolutions": [ + { + "height": 108, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=108&crop=smart&format=pjpg&auto=webp&s=dcc1172b7ace007e8c72080519a16a487596d7e2", + "width": 108, + }, + { + "height": 216, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=216&crop=smart&format=pjpg&auto=webp&s=a7968ce1aa34957a7f7103d06a66d4f9df95d437", + "width": 216, + }, + { + "height": 320, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=320&crop=smart&format=pjpg&auto=webp&s=a2302d80948fba08e91db0a10db579341e1df712", + "width": 320, + }, + { + "height": 640, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=640&crop=smart&format=pjpg&auto=webp&s=a8487450d38d14bcdfda2aeb659b453d8b1cacab", + "width": 640, + }, + { + "height": 960, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?width=960&crop=smart&format=pjpg&auto=webp&s=d371bee68cab49130babe4b890c6323db128c214", + "width": 960, + }, + ], + "source": { + "height": 960, + "url": "https://external-preview.redd.it/E3alyJV8xErhjjDt_Qujcd0ZqmUhGOSFINXALm2FZAQ.png?format=pjpg&auto=webp&s=ff90de8f0a693afeca69dc85dbecb6af9783c769", + "width": 960, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 13271, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/y0mavwswjta51/DASHPlaylist.mpd?a=1597351258%2CYjU1NzFjOTE0YzY2OTdmODk3MGRiMGU4MjdhOGE5ODk2YWNiODQyMGUyOWRhNzI1M2U1MTEyZjBhOWZkZTZmMw%3D%3D&v=1&f=sd", + "duration": 8, + "fallback_url": "https://v.redd.it/y0mavwswjta51/DASH_720.mp4?source=fallback", + "height": 960, + "hls_url": "https://v.redd.it/y0mavwswjta51/HLSPlaylist.m3u8?a=1597351258%2CODk4NTdhMzA3NmY2ZmY2NGQxMmI2ZjcyMzk0ZTFhOTdhOGI4NGQ1NjBiMzNiMmVmZDBhMTQ4MGRkOWJlOWU1YQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/y0mavwswjta51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 960, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": "confidence", + "thumbnail": "https://b.thumbs.redditmedia.com/sxFESWCVsSf4ij5_-a1xdJaFhSU2MjJ5T_TVFbook6Q.jpg", + "thumbnail_height": 140, + "thumbnail_width": 140, + "title": "Good pillow", + "top_awarded_type": None, + "total_awards_received": 0, + "treatment_tags": [], + "ups": 13271, + "upvote_ratio": 0.99, + "url": "https://v.redd.it/y0mavwswjta51", + "url_overridden_by_dest": "https://v.redd.it/y0mavwswjta51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + { + "data": { + "all_awardings": [], + "allow_live_comments": True, + "approved_at_utc": None, + "approved_by": None, + "archived": False, + "author": "asdfpartyy", + "author_flair_background_color": None, + "author_flair_css_class": None, + "author_flair_richtext": [], + "author_flair_template_id": None, + "author_flair_text": None, + "author_flair_text_color": None, + "author_flair_type": "text", + "author_fullname": "t2_t0ay0", + "author_patreon_flair": False, + "author_premium": True, + "awarders": [], + "banned_at_utc": None, + "banned_by": None, + "can_gild": True, + "can_mod_post": False, + "category": None, + "clicked": False, + "content_categories": None, + "contest_mode": False, + "created": 1594745472.0, + "created_utc": 1594716672.0, + "discussion_type": None, + "distinguished": None, + "domain": "v.redd.it", + "downs": 0, + "edited": False, + "gilded": 1, + "gildings": {"gid_2": 1}, + "hidden": False, + "hide_score": False, + "id": "hqy0ny", + "is_crosspostable": True, + "is_meta": False, + "is_original_content": False, + "is_reddit_media_domain": True, + "is_robot_indexable": True, + "is_self": False, + "is_video": True, + "likes": None, + "link_flair_background_color": "", + "link_flair_css_class": None, + "link_flair_richtext": [], + "link_flair_text": None, + "link_flair_text_color": "dark", + "link_flair_type": "text", + "locked": False, + "media": { + "reddit_video": { + "dash_url": "https://v.redd.it/asj4p03rdsa51/DASHPlaylist.mpd?a=1597351258%2CY2VmYTAyMWNmZjIwZjQ4YTBmMDc5MTRjOTU0NjliZWU3MDE2YTU3NjJiYzQxZWRiODY4ZTc1YWI1NDY4MWIxNA%3D%3D&v=1&f=sd", + "duration": 30, + "fallback_url": "https://v.redd.it/asj4p03rdsa51/DASH_360.mp4?source=fallback", + "height": 360, + "hls_url": "https://v.redd.it/asj4p03rdsa51/HLSPlaylist.m3u8?a=1597351258%2CY2QxM2I4Njk5MmIyOTRiZTBhNDQ2MDg0ZTM2NTllYzBjODBlYjNiNDc1Mzg2ODIxNDk4MTAzMzYyNzlmNjI1NQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/asj4p03rdsa51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "media_embed": {}, + "media_only": False, + "mod_note": None, + "mod_reason_by": None, + "mod_reason_title": None, + "mod_reports": [], + "name": "t3_hqy0ny", + "no_follow": False, + "num_comments": 849, + "num_crossposts": 24, + "num_reports": None, + "over_18": False, + "parent_whitelist_status": "all_ads", + "permalink": "/r/aww/comments/hqy0ny/bunnies_flop_over_when_they_feel_completely_safe/", + "pinned": False, + "post_hint": "hosted:video", + "preview": { + "enabled": False, + "images": [ + { + "id": "eMi5JzdWDMeDALsqK8bVceX3jbXTWS_S1D-Ie1hQxnc", + "resolutions": [ + { + "height": 60, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=108&crop=smart&format=pjpg&auto=webp&s=5c6d61e0d4934df3c1f4b7a4c3c3afdd4c31c037", + "width": 108, + }, + { + "height": 121, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=216&crop=smart&format=pjpg&auto=webp&s=24586000b5821e23ce78f395c1f294bbe3fa3945", + "width": 216, + }, + { + "height": 180, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=320&crop=smart&format=pjpg&auto=webp&s=dcaed0109703cbddd4914e138afdb61086cffd81", + "width": 320, + }, + { + "height": 360, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?width=640&crop=smart&format=pjpg&auto=webp&s=ef4f6dc33fe582b93e954114e9eb1447bbbc197b", + "width": 640, + }, + ], + "source": { + "height": 360, + "url": "https://external-preview.redd.it/e67OvQTnkypoYCuC4ApCsgdzVGjPLX3986XW_Ttm9bU.png?format=pjpg&auto=webp&s=b6e8cba9d25c684ecb7104c1e1c454dba7fd3f2f", + "width": 640, + }, + "variants": {}, + } + ], + }, + "pwls": 6, + "quarantine": False, + "removal_reason": None, + "removed_by": None, + "removed_by_category": None, + "report_reasons": None, + "saved": False, + "score": 112661, + "secure_media": { + "reddit_video": { + "dash_url": "https://v.redd.it/asj4p03rdsa51/DASHPlaylist.mpd?a=1597351258%2CY2VmYTAyMWNmZjIwZjQ4YTBmMDc5MTRjOTU0NjliZWU3MDE2YTU3NjJiYzQxZWRiODY4ZTc1YWI1NDY4MWIxNA%3D%3D&v=1&f=sd", + "duration": 30, + "fallback_url": "https://v.redd.it/asj4p03rdsa51/DASH_360.mp4?source=fallback", + "height": 360, + "hls_url": "https://v.redd.it/asj4p03rdsa51/HLSPlaylist.m3u8?a=1597351258%2CY2QxM2I4Njk5MmIyOTRiZTBhNDQ2MDg0ZTM2NTllYzBjODBlYjNiNDc1Mzg2ODIxNDk4MTAzMzYyNzlmNjI1NQ%3D%3D&v=1&f=sd", + "is_gif": False, + "scrubber_media_url": "https://v.redd.it/asj4p03rdsa51/DASH_96.mp4", + "transcoding_status": "completed", + "width": 640, + } + }, + "secure_media_embed": {}, + "selftext": "", + "selftext_html": None, + "send_replies": True, + "spoiler": False, + "stickied": False, + "subreddit": "aww", + "subreddit_id": "t5_2qh1o", + "subreddit_name_prefixed": "r/aww", + "subreddit_subscribers": 25634399, + "subreddit_type": "public", + "suggested_sort": None, + "thumbnail": "https://b.thumbs.redditmedia.com/l_4Yk7NC8hz2HM0D3Hv2dK_nZBjpL8FL3NPv9WkRo8k.jpg", + "thumbnail_height": 78, + "thumbnail_width": 140, + "title": "Bunnies flop over when they feel " + "completely safe beside their " + "protectors", + "top_awarded_type": None, + "total_awards_received": 12, + "treatment_tags": [], + "ups": 112661, + "upvote_ratio": 0.94, + "url": "https://v.redd.it/asj4p03rdsa51", + "url_overridden_by_dest": "https://v.redd.it/asj4p03rdsa51", + "user_reports": [], + "view_count": None, + "visited": False, + "whitelist_status": "all_ads", + "wls": 6, + }, + "kind": "t3", + }, + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +external_video_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_ot2b2", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "Dog splashing in water", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 140, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_hulh8k", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.94, + "author_flair_background_color": None, + "subreddit_type": "public", + "ups": 1142, + "total_awards_received": 0, + "media_embed": { + "content": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "width": 400, + "scrolling": False, + "height": 400, + }, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": { + "oembed": { + "provider_url": "https://gfycat.com", + "description": 'Hi! We use cookies and similar technologies ("cookies"), including third-party cookies, on this website to help operate and improve your experience on our site, monitor our site performance, and for advertising purposes. By clicking "Accept Cookies" below, you are giving us consent to use cookies (except consent is not required for cookies necessary to run our site).', + "title": "97991217 286625482366728 7551185146460766208 n", + "author_name": "Gfycat", + "height": 400, + "width": 400, + "html": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "thumbnail_width": 250, + "version": "1.0", + "provider_name": "Gfycat", + "thumbnail_url": "https://thumbs.gfycat.com/ExcellentInfantileAmericanwigeon-size_restricted.gif", + "type": "video", + "thumbnail_height": 250, + }, + "type": "gfycat.com", + }, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": { + "content": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "width": 400, + "scrolling": False, + "media_domain_url": "https://www.redditmedia.com/mediaembed/hulh8k", + "height": 400, + }, + "link_flair_text": None, + "can_mod_post": False, + "score": 1142, + "approved_by": None, + "author_premium": False, + "thumbnail": "https://b.thumbs.redditmedia.com/eR_Cu4w1l9PwaM14RTEpnKD20EaK5mMxUbyK8BBDo_M.jpg", + "edited": False, + "author_flair_css_class": None, + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "rich:video", + "content_categories": None, + "is_self": False, + "mod_note": None, + "crosspost_parent_list": [], + "created": 1595281442, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "gfycat.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": None, + "banned_at_utc": None, + "url_overridden_by_dest": "https://gfycat.com/excellentinfantileamericanwigeon", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": True, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/rZXN_aGbww8NNlhGWB-5cjHPonSST4S7aS6uaZyb_W4.jpg?auto=webp&s=2a2d3a1e0a06742bf752c1c4e1582c2fa49793a3", + "width": 250, + "height": 250, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/rZXN_aGbww8NNlhGWB-5cjHPonSST4S7aS6uaZyb_W4.jpg?width=108&crop=smart&auto=webp&s=35f61b003416516f664682717876a94d186793ae", + "width": 108, + "height": 108, + }, + { + "url": "https://external-preview.redd.it/rZXN_aGbww8NNlhGWB-5cjHPonSST4S7aS6uaZyb_W4.jpg?width=216&crop=smart&auto=webp&s=842416c1b8f8fae758a7ba6eb98af93ee2404a8d", + "width": 216, + "height": 216, + }, + ], + "variants": {}, + "id": "IVorc9dV9K9nJhhSVFKST92dfGfmhgBQjw257DWmJcE", + } + ], + "reddit_video_preview": { + "fallback_url": "https://v.redd.it/syp9pkiu00c51/DASH_360.mp4", + "height": 400, + "width": 400, + "scrubber_media_url": "https://v.redd.it/syp9pkiu00c51/DASH_96.mp4", + "dash_url": "https://v.redd.it/syp9pkiu00c51/DASHPlaylist.mpd", + "duration": 21, + "hls_url": "https://v.redd.it/syp9pkiu00c51/HLSPlaylist.m3u8", + "is_gif": True, + "transcoding_status": "completed", + }, + "enabled": False, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": True, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "hulh8k", + "is_robot_indexable": True, + "report_reasons": None, + "author": "TheRikari", + "discussion_type": None, + "num_comments": 21, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "crosspost_parent": "t3_hujqxu", + "author_flair_text_color": None, + "permalink": "/r/aww/comments/hulh8k/dog_splashing_in_water/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://gfycat.com/excellentinfantileamericanwigeon", + "subreddit_subscribers": 25721914, + "created_utc": 1595252642, + "num_crossposts": 0, + "media": { + "oembed": { + "provider_url": "https://gfycat.com", + "description": 'Hi! We use cookies and similar technologies ("cookies"), including third-party cookies, on this website to help operate and improve your experience on our site, monitor our site performance, and for advertising purposes. By clicking "Accept Cookies" below, you are giving us consent to use cookies (except consent is not required for cookies necessary to run our site).', + "title": "97991217 286625482366728 7551185146460766208 n", + "author_name": "Gfycat", + "height": 400, + "width": 400, + "html": '<iframe class="embedly-embed" src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgfycat.com%2Fifr%2Fexcellentinfantileamericanwigeon&display_name=Gfycat&url=https%3A%2F%2Fgfycat.com%2Fexcellentinfantileamericanwigeon&image=https%3A%2F%2Fthumbs.gfycat.com%2FExcellentInfantileAmericanwigeon-size_restricted.gif&key=ed8fa8699ce04833838e66ce79ba05f1&type=text%2Fhtml&schema=gfycat" width="400" height="400" scrolling="no" title="Gfycat embed" frameborder="0" allow="autoplay; fullscreen" allowfullscreen="True"></iframe>', + "thumbnail_width": 250, + "version": "1.0", + "provider_name": "Gfycat", + "thumbnail_url": "https://thumbs.gfycat.com/ExcellentInfantileAmericanwigeon-size_restricted.gif", + "type": "video", + "thumbnail_height": 250, + }, + "type": "gfycat.com", + }, + "is_video": False, + }, + } + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +external_gifv_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "kind": "t3", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_ygx0p1u", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "if i fits i sits", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 74, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_humdlf", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.97, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 7512, + "total_awards_received": 1, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 7512, + "approved_by": None, + "author_premium": True, + "thumbnail": "https://b.thumbs.redditmedia.com/QHK44nUFZup-hfFX2Z1dXhk-1lPEmROUCB3bBujvTck.jpg", + "edited": False, + "author_flair_css_class": "k", + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "link", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1595284712, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.imgur.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": "confidence", + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.imgur.com/grVh2AG.gifv", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": False, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?auto=webp&s=c4ba246318b3502b080d37fcbdb12e07221401a9", + "width": 638, + "height": 338, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=108&crop=smart&auto=webp&s=c9c340a60ba3da1af3f5d5c08f3ed618ebd567d4", + "width": 108, + "height": 57, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=216&crop=smart&auto=webp&s=d05c0415e3dc63d097264bfb1b35b09676bd24f6", + "width": 216, + "height": 114, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=320&crop=smart&auto=webp&s=5c236179ccfff29e9ba980f31d5a6a9905adbe86", + "width": 320, + "height": 169, + }, + ], + "variants": {}, + "id": "4Z8zF5e4sZJnX4vWH7pZkbqiDPMCuh2J4kNotV9AGSI", + } + ], + "reddit_video_preview": { + "fallback_url": "https://v.redd.it/zzctc8y2dzb51/DASH_240.mp4", + "height": 338, + "width": 638, + "scrubber_media_url": "https://v.redd.it/zzctc8y2dzb51/DASH_96.mp4", + "dash_url": "https://v.redd.it/zzctc8y2dzb51/DASHPlaylist.mpd", + "duration": 44, + "hls_url": "https://v.redd.it/zzctc8y2dzb51/HLSPlaylist.m3u8", + "is_gif": True, + "transcoding_status": "completed", + }, + "enabled": False, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": False, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "humdlf", + "is_robot_indexable": True, + "report_reasons": None, + "author": "jasontaken", + "discussion_type": None, + "num_comments": 67, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/aww/comments/humdlf/if_i_fits_i_sits/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.imgur.com/grVh2AG.gifv", + "subreddit_subscribers": 25723833, + "created_utc": 1595255912, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + } + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} + +unknown_mock = { + "data": { + "after": "t3_hr3mhe", + "before": None, + "children": [ + { + "kind": "t1", + "data": { + "approved_at_utc": None, + "subreddit": "aww", + "selftext": "", + "author_fullname": "t2_ygx0p1u", + "saved": False, + "mod_reason_title": None, + "gilded": 0, + "clicked": False, + "title": "if i fits i sits", + "link_flair_richtext": [], + "subreddit_name_prefixed": "r/aww", + "hidden": False, + "pwls": 6, + "link_flair_css_class": None, + "downs": 0, + "thumbnail_height": 74, + "top_awarded_type": None, + "hide_score": False, + "name": "t3_humdlf", + "quarantine": False, + "link_flair_text_color": "dark", + "upvote_ratio": 0.97, + "author_flair_background_color": "", + "subreddit_type": "public", + "ups": 7512, + "total_awards_received": 1, + "media_embed": {}, + "thumbnail_width": 140, + "author_flair_template_id": None, + "is_original_content": False, + "user_reports": [], + "secure_media": None, + "is_reddit_media_domain": False, + "is_meta": False, + "category": None, + "secure_media_embed": {}, + "link_flair_text": None, + "can_mod_post": False, + "score": 7512, + "approved_by": None, + "author_premium": True, + "thumbnail": "https://b.thumbs.redditmedia.com/QHK44nUFZup-hfFX2Z1dXhk-1lPEmROUCB3bBujvTck.jpg", + "edited": False, + "author_flair_css_class": "k", + "author_flair_richtext": [], + "gildings": {}, + "post_hint": "link", + "content_categories": None, + "is_self": False, + "mod_note": None, + "created": 1595284712, + "link_flair_type": "text", + "wls": 6, + "removed_by_category": None, + "banned_by": None, + "author_flair_type": "text", + "domain": "i.imgur.com", + "allow_live_comments": False, + "selftext_html": None, + "likes": None, + "suggested_sort": "confidence", + "banned_at_utc": None, + "url_overridden_by_dest": "https://i.imgur.com/grVh2AG.gifv", + "view_count": None, + "archived": False, + "no_follow": False, + "is_crosspostable": False, + "pinned": False, + "over_18": False, + "preview": { + "images": [ + { + "source": { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?auto=webp&s=c4ba246318b3502b080d37fcbdb12e07221401a9", + "width": 638, + "height": 338, + }, + "resolutions": [ + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=108&crop=smart&auto=webp&s=c9c340a60ba3da1af3f5d5c08f3ed618ebd567d4", + "width": 108, + "height": 57, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=216&crop=smart&auto=webp&s=d05c0415e3dc63d097264bfb1b35b09676bd24f6", + "width": 216, + "height": 114, + }, + { + "url": "https://external-preview.redd.it/XDHMFAMlcn3iuSqmWeDIc4yG-q-6xnTIdGivYX-cus4.jpg?width=320&crop=smart&auto=webp&s=5c236179ccfff29e9ba980f31d5a6a9905adbe86", + "width": 320, + "height": 169, + }, + ], + "variants": {}, + "id": "4Z8zF5e4sZJnX4vWH7pZkbqiDPMCuh2J4kNotV9AGSI", + } + ], + "reddit_video_preview": { + "fallback_url": "https://v.redd.it/zzctc8y2dzb51/DASH_240.mp4", + "height": 338, + "width": 638, + "scrubber_media_url": "https://v.redd.it/zzctc8y2dzb51/DASH_96.mp4", + "dash_url": "https://v.redd.it/zzctc8y2dzb51/DASHPlaylist.mpd", + "duration": 44, + "hls_url": "https://v.redd.it/zzctc8y2dzb51/HLSPlaylist.m3u8", + "is_gif": True, + "transcoding_status": "completed", + }, + "enabled": False, + }, + "all_awardings": [], + "awarders": [], + "media_only": False, + "can_gild": False, + "spoiler": False, + "locked": False, + "author_flair_text": None, + "treatment_tags": [], + "visited": False, + "removed_by": None, + "num_reports": None, + "distinguished": None, + "subreddit_id": "t5_2qh1o", + "mod_reason_by": None, + "removal_reason": None, + "link_flair_background_color": "", + "id": "humdlf", + "is_robot_indexable": True, + "report_reasons": None, + "author": "jasontaken", + "discussion_type": None, + "num_comments": 67, + "send_replies": False, + "whitelist_status": "all_ads", + "contest_mode": False, + "mod_reports": [], + "author_patreon_flair": False, + "author_flair_text_color": "dark", + "permalink": "/r/aww/comments/humdlf/if_i_fits_i_sits/", + "parent_whitelist_status": "all_ads", + "stickied": False, + "url": "https://i.imgur.com/grVh2AG.gifv", + "subreddit_subscribers": 25723833, + "created_utc": 1595255912, + "num_crossposts": 1, + "media": None, + "is_video": False, + }, + } + ], + "dist": 25, + "modhash": None, + }, + "kind": "Listing", +} diff --git a/src/newsreader/news/collection/tests/reddit/builder/tests.py b/src/newsreader/news/collection/tests/reddit/builder/tests.py index eb8182a..9c1a046 100644 --- a/src/newsreader/news/collection/tests/reddit/builder/tests.py +++ b/src/newsreader/news/collection/tests/reddit/builder/tests.py @@ -86,7 +86,7 @@ class RedditBuilderTestCase(TestCase): def test_update_posts(self): subreddit = SubredditFactory() existing_post = RedditPostFactory( - remote_identifier="hngsj8", + remote_identifier="hm0qct", author="Old author", title="Old title", body="Old body", @@ -108,17 +108,24 @@ class RedditBuilderTestCase(TestCase): existing_post.refresh_from_db() - self.assertEquals(existing_post.remote_identifier, "hngsj8") - self.assertEquals(existing_post.author, "nixcraft") - self.assertEquals(existing_post.title, "KeePassXC 2.6.0 released") - self.assertEquals(existing_post.body, "") + self.assertEquals(existing_post.remote_identifier, "hm0qct") + self.assertEquals(existing_post.author, "AutoModerator") + self.assertEquals( + existing_post.title, + "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", + ) + self.assertIn( + "This megathread is also to hear opinions from anyone just starting out " + "with Linux or those that have used Linux (GNU or otherwise) for a long time.", + existing_post.body, + ) self.assertEquals( existing_post.publication_date, - pytz.utc.localize(datetime(2020, 7, 8, 15, 11, 6)), + pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22)), ) self.assertEquals( existing_post.url, - "https://www.reddit.com/r/linux/comments/hngsj8/" "keepassxc_260_released/", + "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", ) def test_html_sanitizing(self): @@ -219,3 +226,183 @@ class RedditBuilderTestCase(TestCase): duplicate_post.title, "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", ) + + def test_image_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = MagicMock(rule=subreddit) + + with builder((image_mock, mock_stream)) as builder: + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hr64xh", "hr4bxo", "hr14y5", "hr2fv0"), posts.keys()) + + post = posts["hr64xh"] + + title = ( + "Ya’ll, I just can’t... this is my " + "son, Judah. My wife and I have no " + "idea how we created such a " + "beautiful child." + ) + url = "https://i.redd.it/cm2qybia1va51.jpg" + + self.assertEquals( + "https://www.reddit.com/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/", + post.url, + ) + self.assertEquals( + f"
    {title}
    ", post.body + ) + + def test_external_image_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = MagicMock(rule=subreddit) + + with builder((external_image_mock, mock_stream)) as builder: + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hr41am", "huoldn"), posts.keys()) + + post = posts["hr41am"] + + url = "http://gfycat.com/thatalivedogwoodclubgall" + title = "Excited cows have a new brush!" + + self.assertEquals( + f"", + post.body, + ) + self.assertEquals( + "https://www.reddit.com/r/aww/comments/hr41am/excited_cows_have_a_new_brush/", + post.url, + ) + + post = posts["huoldn"] + + url = "https://i.imgur.com/usfMVUJ.jpg" + title = "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallas’s cat kittens" + + self.assertEquals( + f"
    {title}
    ", post.body + ) + self.assertEquals( + "https://www.reddit.com/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/", + post.url, + ) + + def test_video_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = MagicMock(rule=subreddit) + + with builder((video_mock, mock_stream)) as builder: + builder.save() + + posts = {post.remote_identifier: post for post in Post.objects.all()} + + self.assertCountEqual(("hr32jf", "hr1r00", "hqy0ny", "hr0uzh"), posts.keys()) + + post = posts["hr1r00"] + + url = "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback" + + self.assertEquals( + post.url, + "https://www.reddit.com/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/", + ) + self.assertEquals( + f"
    ", + post.body, + ) + + def test_external_video_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = MagicMock(rule=subreddit) + + with builder((external_video_mock, mock_stream)) as builder: + builder.save() + + post = Post.objects.get() + + self.assertEquals(post.remote_identifier, "hulh8k") + + self.assertEquals( + post.url, + "https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/", + ) + + title = "Dog splashing in water" + url = "https://gfycat.com/excellentinfantileamericanwigeon" + + self.assertEquals( + f"", + post.body, + ) + + def test_external_gifv_video_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = MagicMock(rule=subreddit) + + with builder((external_gifv_mock, mock_stream)) as builder: + builder.save() + + post = Post.objects.get() + + self.assertEquals(post.remote_identifier, "humdlf") + + self.assertEquals( + post.url, "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/" + ) + + self.assertEquals( + "
    ", + post.body, + ) + + def test_link_only_post(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = MagicMock(rule=subreddit) + + with builder((simple_mock, mock_stream)) as builder: + builder.save() + + post = Post.objects.get(remote_identifier="hngsj8") + + title = "KeePassXC 2.6.0 released" + url = "https://keepassxc.org/blog/2020-07-07-2.6.0-released/" + + self.assertIn( + f"", + post.body, + ) + + self.assertEquals( + post.url, + "https://www.reddit.com/r/linux/comments/hngsj8/keepassxc_260_released/", + ) + + def test_skip_not_known_post_type(self): + builder = RedditBuilder + + subreddit = SubredditFactory() + mock_stream = MagicMock(rule=subreddit) + + with builder((unknown_mock, mock_stream)) as builder: + builder.save() + + self.assertEquals(Post.objects.count(), 0) diff --git a/src/newsreader/news/collection/tests/views/test_subreddit_views.py b/src/newsreader/news/collection/tests/views/test_subreddit_views.py index a8de55e..0dff663 100644 --- a/src/newsreader/news/collection/tests/views/test_subreddit_views.py +++ b/src/newsreader/news/collection/tests/views/test_subreddit_views.py @@ -1,11 +1,12 @@ from django.test import TestCase from django.urls import reverse +from django.utils.translation import gettext as _ import pytz from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.models import CollectionRule -from newsreader.news.collection.reddit import REDDIT_URL +from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL from newsreader.news.collection.tests.factories import SubredditFactory from newsreader.news.collection.tests.views.base import CollectionRuleViewTestCase from newsreader.news.core.tests.factories import CategoryFactory @@ -17,7 +18,7 @@ class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase): self.form_data = { "name": "new rule", - "url": "https://www.reddit.com/r/aww", + "url": f"{REDDIT_API_URL}/r/aww", "category": str(self.category.pk), } @@ -31,12 +32,19 @@ class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase): rule = CollectionRule.objects.get(name="new rule") self.assertEquals(rule.type, RuleTypeChoices.subreddit) - self.assertEquals(rule.url, "https://www.reddit.com/r/aww.json") + self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") self.assertEquals(rule.timezone, str(pytz.utc)) self.assertEquals(rule.favicon, None) self.assertEquals(rule.category.pk, self.category.pk) self.assertEquals(rule.user.pk, self.user.pk) + def test_regular_reddit_url(self): + self.form_data.update(url=f"{REDDIT_URL}/r/aww") + + response = self.client.post(self.url, self.form_data) + + self.assertContains(response, _("This does not look like an Reddit API URL")) + class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): def setUp(self): @@ -44,7 +52,7 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): self.rule = SubredditFactory( name="Python", - url=f"{REDDIT_URL}/r/python.json", + url=f"{REDDIT_API_URL}/r/python.json", user=self.user, category=self.category, type=RuleTypeChoices.subreddit, @@ -97,7 +105,7 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): self.assertEquals(response.status_code, 404) def test_url_change(self): - self.form_data.update(name="aww", url=f"{REDDIT_URL}/r/aww") + self.form_data.update(name="aww", url=f"{REDDIT_API_URL}/r/aww") response = self.client.post(self.url, self.form_data) @@ -106,8 +114,15 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): rule = CollectionRule.objects.get(name="aww") self.assertEquals(rule.type, RuleTypeChoices.subreddit) - self.assertEquals(rule.url, f"{REDDIT_URL}/r/aww.json") + self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") self.assertEquals(rule.timezone, str(pytz.utc)) self.assertEquals(rule.favicon, None) self.assertEquals(rule.category.pk, self.category.pk) self.assertEquals(rule.user.pk, self.user.pk) + + def test_regular_reddit_url(self): + self.form_data.update(url=f"{REDDIT_URL}/r/aww") + + response = self.client.post(self.url, self.form_data) + + self.assertContains(response, _("This does not look like an Reddit API URL")) diff --git a/src/newsreader/news/core/tests/endpoints/category/detail/tests.py b/src/newsreader/news/core/tests/endpoints/category/detail/tests.py index 864a144..1f42a20 100644 --- a/src/newsreader/news/core/tests/endpoints/category/detail/tests.py +++ b/src/newsreader/news/core/tests/endpoints/category/detail/tests.py @@ -4,8 +4,8 @@ from django.test import TestCase from django.urls import reverse from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.tests.factories import CollectionRuleFactory -from newsreader.news.core.tests.factories import CategoryFactory, PostFactory +from newsreader.news.collection.tests.factories import FeedFactory +from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory class CategoryDetailViewTestCase(TestCase): @@ -116,11 +116,11 @@ class CategoryDetailViewTestCase(TestCase): def test_read_count(self): category = CategoryFactory(user=self.user) - unread_rule = CollectionRuleFactory(category=category) - read_rule = CollectionRuleFactory(category=category) + unread_rule = FeedFactory(category=category) + read_rule = FeedFactory(category=category) - PostFactory.create_batch(size=20, read=False, rule=unread_rule) - PostFactory.create_batch(size=20, read=True, rule=read_rule) + FeedPostFactory.create_batch(size=20, read=False, rule=unread_rule) + FeedPostFactory.create_batch(size=20, read=True, rule=read_rule) response = self.client.get( reverse("api:news:core:categories-detail", args=[category.pk]) @@ -139,8 +139,8 @@ class CategoryReadTestCase(TestCase): def test_category_read(self): category = CategoryFactory(user=self.user) rules = [ - PostFactory.create_batch(size=5, read=False, rule=rule) - for rule in CollectionRuleFactory.create_batch(size=5, category=category) + FeedPostFactory.create_batch(size=5, read=False, rule=rule) + for rule in FeedFactory.create_batch(size=5, category=category) ] response = self.client.post( @@ -165,8 +165,8 @@ class CategoryReadTestCase(TestCase): category = CategoryFactory(user=self.user) rules = [ - PostFactory.create_batch(size=5, read=False, rule=rule) - for rule in CollectionRuleFactory.create_batch( + FeedPostFactory.create_batch(size=5, read=False, rule=rule) + for rule in FeedFactory.create_batch( size=5, category=category, user=self.user ) ] @@ -182,8 +182,8 @@ class CategoryReadTestCase(TestCase): category = CategoryFactory(user=other_user) rules = [ - PostFactory.create_batch(size=5, read=False, rule=rule) - for rule in CollectionRuleFactory.create_batch( + FeedPostFactory.create_batch(size=5, read=False, rule=rule) + for rule in FeedFactory.create_batch( size=5, category=category, user=other_user ) ] diff --git a/src/newsreader/news/core/tests/endpoints/category/list/tests.py b/src/newsreader/news/core/tests/endpoints/category/list/tests.py index 4d5f0e6..15fb166 100644 --- a/src/newsreader/news/core/tests/endpoints/category/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/category/list/tests.py @@ -8,8 +8,8 @@ from django.urls import reverse import pytz from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.tests.factories import CollectionRuleFactory -from newsreader.news.core.tests.factories import CategoryFactory, PostFactory +from newsreader.news.collection.tests.factories import FeedFactory +from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory class CategoryListViewTestCase(TestCase): @@ -125,7 +125,7 @@ class NestedCategoryListViewTestCase(TestCase): def test_simple(self): category = CategoryFactory.create(user=self.user) - rules = CollectionRuleFactory.create_batch(size=5, category=category) + rules = FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) @@ -219,7 +219,7 @@ class NestedCategoryListViewTestCase(TestCase): self.client.logout() category = CategoryFactory.create(user=self.user) - rules = CollectionRuleFactory.create_batch(size=5, category=category) + rules = FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) @@ -231,7 +231,7 @@ class NestedCategoryListViewTestCase(TestCase): other_user = UserFactory.create() category = CategoryFactory.create(user=other_user) - rules = CollectionRuleFactory.create_batch(size=5, category=category) + rules = FeedFactory.create_batch(size=5, category=category) response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) @@ -242,9 +242,9 @@ class NestedCategoryListViewTestCase(TestCase): def test_ordering(self): category = CategoryFactory.create(user=self.user) rules = [ - CollectionRuleFactory.create(category=category, name="Durp"), - CollectionRuleFactory.create(category=category, name="Slurp"), - CollectionRuleFactory.create(category=category, name="Burp"), + FeedFactory.create(category=category, name="Durp"), + FeedFactory.create(category=category, name="Slurp"), + FeedFactory.create(category=category, name="Burp"), ] response = self.client.get( @@ -261,13 +261,13 @@ class NestedCategoryListViewTestCase(TestCase): def test_only_rules_from_category_are_returned(self): other_category = CategoryFactory(user=self.user) - CollectionRuleFactory.create_batch(size=5, category=other_category) + FeedFactory.create_batch(size=5, category=other_category) category = CategoryFactory.create(user=self.user) rules = [ - CollectionRuleFactory.create(category=category, name="Durp"), - CollectionRuleFactory.create(category=category, name="Slurp"), - CollectionRuleFactory.create(category=category, name="Burp"), + FeedFactory.create(category=category, name="Durp"), + FeedFactory.create(category=category, name="Slurp"), + FeedFactory.create(category=category, name="Burp"), ] response = self.client.get( @@ -291,8 +291,8 @@ class NestedCategoryPostView(TestCase): def test_simple(self): category = CategoryFactory.create(user=self.user) rules = { - rule.pk: PostFactory.create_batch(size=5, rule=rule) - for rule in CollectionRuleFactory.create_batch( + rule.pk: FeedPostFactory.create_batch(size=5, rule=rule) + for rule in FeedFactory.create_batch( size=5, category=category, user=self.user ) } @@ -327,9 +327,7 @@ class NestedCategoryPostView(TestCase): def test_no_posts(self): category = CategoryFactory.create(user=self.user) - rules = CollectionRuleFactory.create_batch( - size=5, user=self.user, category=category - ) + rules = FeedFactory.create_batch(size=5, user=self.user, category=category) response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) @@ -427,25 +425,23 @@ class NestedCategoryPostView(TestCase): def test_ordering(self): category = CategoryFactory.create(user=self.user) - bbc_rule = CollectionRuleFactory.create( - name="BBC", category=category, user=self.user - ) - guardian_rule = CollectionRuleFactory.create( + bbc_rule = FeedFactory.create(name="BBC", category=category, user=self.user) + guardian_rule = FeedFactory.create( name="The Guardian", category=category, user=self.user ) - reuters_rule = CollectionRuleFactory.create( + reuters_rule = FeedFactory.create( name="Reuters", category=category, user=self.user ) reuters_rule = [ - PostFactory.create( + FeedPostFactory.create( title="Second Reuters post", rule=reuters_rule, publication_date=datetime.combine( date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc ), ), - PostFactory.create( + FeedPostFactory.create( title="First Reuters post", rule=reuters_rule, publication_date=datetime.combine( @@ -455,14 +451,14 @@ class NestedCategoryPostView(TestCase): ] guardian_posts = [ - PostFactory.create( + FeedPostFactory.create( title="Second Guardian post", rule=guardian_rule, publication_date=datetime.combine( date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc ), ), - PostFactory.create( + FeedPostFactory.create( title="First Guardian post", rule=guardian_rule, publication_date=datetime.combine( @@ -472,14 +468,14 @@ class NestedCategoryPostView(TestCase): ] bbc_posts = [ - PostFactory.create( + FeedPostFactory.create( title="Second BBC post", rule=bbc_rule, publication_date=datetime.combine( date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc ), ), - PostFactory.create( + FeedPostFactory.create( title="First BBC post", rule=bbc_rule, publication_date=datetime.combine( @@ -509,19 +505,19 @@ class NestedCategoryPostView(TestCase): category = CategoryFactory.create(user=self.user) other_category = CategoryFactory.create(user=self.user) - guardian_rule = CollectionRuleFactory.create( + guardian_rule = FeedFactory.create( name="BBC", category=category, user=self.user ) - other_rule = CollectionRuleFactory.create(name="The Guardian", user=self.user) + other_rule = FeedFactory.create(name="The Guardian", user=self.user) guardian_posts = [ - PostFactory.create(rule=guardian_rule), - PostFactory.create(rule=guardian_rule), + FeedPostFactory.create(rule=guardian_rule), + FeedPostFactory.create(rule=guardian_rule), ] other_posts = [ - PostFactory.create(rule=other_rule), - PostFactory.create(rule=other_rule), + FeedPostFactory.create(rule=other_rule), + FeedPostFactory.create(rule=other_rule), ] response = self.client.get( @@ -538,10 +534,10 @@ class NestedCategoryPostView(TestCase): def test_unread_posts(self): category = CategoryFactory.create(user=self.user) - rule = CollectionRuleFactory(category=category) + rule = FeedFactory(category=category) - PostFactory.create_batch(size=10, rule=rule, read=False) - PostFactory.create_batch(size=10, rule=rule, read=True) + FeedPostFactory.create_batch(size=10, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) response = self.client.get( reverse( @@ -561,10 +557,10 @@ class NestedCategoryPostView(TestCase): def test_read_posts(self): category = CategoryFactory.create(user=self.user) - rule = CollectionRuleFactory(category=category) + rule = FeedFactory(category=category) - PostFactory.create_batch(size=20, rule=rule, read=False) - PostFactory.create_batch(size=10, rule=rule, read=True) + FeedPostFactory.create_batch(size=20, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) response = self.client.get( reverse( diff --git a/src/newsreader/news/core/tests/endpoints/post/detail/tests.py b/src/newsreader/news/core/tests/endpoints/post/detail/tests.py index c804ff5..2d25a89 100644 --- a/src/newsreader/news/core/tests/endpoints/post/detail/tests.py +++ b/src/newsreader/news/core/tests/endpoints/post/detail/tests.py @@ -4,8 +4,8 @@ from django.test import TestCase from django.urls import reverse from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.tests.factories import CollectionRuleFactory -from newsreader.news.core.tests.factories import CategoryFactory, PostFactory +from newsreader.news.collection.tests.factories import FeedFactory +from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory class PostDetailViewTestCase(TestCase): @@ -14,10 +14,8 @@ class PostDetailViewTestCase(TestCase): self.client.force_login(self.user) def test_simple(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule) response = self.client.get( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -43,10 +41,8 @@ class PostDetailViewTestCase(TestCase): self.assertEquals(data["detail"], "Not found.") def test_post(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule) response = self.client.post( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -57,10 +53,8 @@ class PostDetailViewTestCase(TestCase): self.assertEquals(data["detail"], 'Method "POST" not allowed.') def test_patch(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(title="This is clickbait for sure", rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(title="This is clickbait for sure", rule=rule) response = self.client.patch( reverse("api:news:core:posts-detail", args=[post.pk]), @@ -73,10 +67,8 @@ class PostDetailViewTestCase(TestCase): self.assertEquals(data["title"], "This title is very accurate") def test_identifier_cannot_be_changed(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(title="This is clickbait for sure", rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(title="This is clickbait for sure", rule=rule) response = self.client.patch( reverse("api:news:core:posts-detail", args=[post.pk]), @@ -89,13 +81,9 @@ class PostDetailViewTestCase(TestCase): self.assertEquals(data["id"], post.pk) def test_rule_cannot_be_changed(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - new_rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(title="This is clickbait for sure", rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + new_rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(title="This is clickbait for sure", rule=rule) response = self.client.patch( reverse("api:news:core:posts-detail", args=[post.pk]), @@ -115,10 +103,8 @@ class PostDetailViewTestCase(TestCase): self.assertTrue(data["rule"], rule.pk) def test_put(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(title="This is clickbait for sure", rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(title="This is clickbait for sure", rule=rule) response = self.client.put( reverse("api:news:core:posts-detail", args=[post.pk]), @@ -131,10 +117,8 @@ class PostDetailViewTestCase(TestCase): self.assertEquals(data["title"], "This title is very accurate") def test_delete(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule) response = self.client.delete( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -147,8 +131,8 @@ class PostDetailViewTestCase(TestCase): def test_post_with_unauthenticated_user_without_category(self): self.client.logout() - rule = CollectionRuleFactory(user=self.user, category=None) - post = PostFactory(rule=rule) + rule = FeedFactory(user=self.user, category=None) + post = FeedPostFactory(rule=rule) response = self.client.get( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -159,10 +143,8 @@ class PostDetailViewTestCase(TestCase): def test_post_with_unauthenticated_user_with_category(self): self.client.logout() - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule) response = self.client.get( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -172,8 +154,8 @@ class PostDetailViewTestCase(TestCase): def test_post_with_unauthorized_user_without_category(self): other_user = UserFactory() - rule = CollectionRuleFactory(user=other_user, category=None) - post = PostFactory(rule=rule) + rule = FeedFactory(user=other_user, category=None) + post = FeedPostFactory(rule=rule) response = self.client.get( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -183,10 +165,8 @@ class PostDetailViewTestCase(TestCase): def test_post_with_unauthorized_user_with_category(self): other_user = UserFactory() - rule = CollectionRuleFactory( - user=other_user, category=CategoryFactory(user=other_user) - ) - post = PostFactory(rule=rule) + rule = FeedFactory(user=other_user, category=CategoryFactory(user=other_user)) + post = FeedPostFactory(rule=rule) response = self.client.get( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -196,10 +176,8 @@ class PostDetailViewTestCase(TestCase): def test_post_with_different_user_for_category_and_rule(self): other_user = UserFactory() - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=other_user) - ) - post = PostFactory(rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=other_user)) + post = FeedPostFactory(rule=rule) response = self.client.get( reverse("api:news:core:posts-detail", args=[post.pk]) @@ -208,10 +186,8 @@ class PostDetailViewTestCase(TestCase): self.assertEquals(response.status_code, 403) def test_mark_read(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(rule=rule, read=False) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule, read=False) response = self.client.patch( reverse("api:news:core:posts-detail", args=[post.pk]), @@ -224,10 +200,8 @@ class PostDetailViewTestCase(TestCase): self.assertEquals(data["read"], True) def test_mark_unread(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - post = PostFactory(rule=rule, read=True) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + post = FeedPostFactory(rule=rule, read=True) response = self.client.patch( reverse("api:news:core:posts-detail", args=[post.pk]), diff --git a/src/newsreader/news/core/tests/endpoints/post/list/tests.py b/src/newsreader/news/core/tests/endpoints/post/list/tests.py index 3800b64..3bf9d17 100644 --- a/src/newsreader/news/core/tests/endpoints/post/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/post/list/tests.py @@ -6,8 +6,8 @@ from django.urls import reverse import pytz from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.tests.factories import CollectionRuleFactory -from newsreader.news.core.tests.factories import CategoryFactory, PostFactory +from newsreader.news.collection.tests.factories import FeedFactory +from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory class PostListViewTestCase(TestCase): @@ -16,10 +16,8 @@ class PostListViewTestCase(TestCase): self.client.force_login(self.user) def test_simple(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - PostFactory.create_batch(size=3, rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + FeedPostFactory.create_batch(size=3, rule=rule) response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() @@ -30,26 +28,24 @@ class PostListViewTestCase(TestCase): self.assertEquals(data["count"], 3) def test_ordering(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) posts = [ - PostFactory( + FeedPostFactory( title="I'm the first post", rule=rule, publication_date=datetime.combine( date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc ), ), - PostFactory( + FeedPostFactory( title="I'm the second post", rule=rule, publication_date=datetime.combine( date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc ), ), - PostFactory( + FeedPostFactory( title="I'm the third post", rule=rule, publication_date=datetime.combine( @@ -71,10 +67,8 @@ class PostListViewTestCase(TestCase): self.assertEquals(data["results"][2]["id"], posts[0].pk) def test_pagination_count(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) - PostFactory.create_batch(size=80, rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) + FeedPostFactory.create_batch(size=80, rule=rule) page_size = 50 response = self.client.get(reverse("api:news:core:posts-list"), {"count": 50}) @@ -126,7 +120,7 @@ class PostListViewTestCase(TestCase): def test_posts_with_unauthenticated_user_without_category(self): self.client.logout() - PostFactory.create_batch(size=3, rule=CollectionRuleFactory(user=self.user)) + FeedPostFactory.create_batch(size=3, rule=FeedFactory(user=self.user)) response = self.client.get(reverse("api:news:core:posts-list")) @@ -137,8 +131,8 @@ class PostListViewTestCase(TestCase): category = CategoryFactory(user=self.user) - PostFactory.create_batch( - size=3, rule=CollectionRuleFactory(user=self.user, category=category) + FeedPostFactory.create_batch( + size=3, rule=FeedFactory(user=self.user, category=category) ) response = self.client.get(reverse("api:news:core:posts-list")) @@ -148,8 +142,8 @@ class PostListViewTestCase(TestCase): def test_posts_with_unauthorized_user_without_category(self): other_user = UserFactory() - rule = CollectionRuleFactory(user=other_user, category=None) - PostFactory.create_batch(size=3, rule=rule) + rule = FeedFactory(user=other_user, category=None) + FeedPostFactory.create_batch(size=3, rule=rule) response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() @@ -162,8 +156,8 @@ class PostListViewTestCase(TestCase): other_user = UserFactory() category = CategoryFactory(user=other_user) - PostFactory.create_batch( - size=3, rule=CollectionRuleFactory(user=other_user, category=category) + FeedPostFactory.create_batch( + size=3, rule=FeedFactory(user=other_user, category=category) ) response = self.client.get(reverse("api:news:core:posts-list")) @@ -178,10 +172,8 @@ class PostListViewTestCase(TestCase): def test_posts_with_authorized_rule_unauthorized_category(self): other_user = UserFactory() - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=other_user) - ) - PostFactory.create_batch(size=3, rule=rule) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=other_user)) + FeedPostFactory.create_batch(size=3, rule=rule) response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() @@ -192,8 +184,8 @@ class PostListViewTestCase(TestCase): self.assertEquals(data["count"], 0) def test_posts_with_authorized_user_without_category(self): - rule = CollectionRuleFactory(user=self.user, category=None) - PostFactory.create_batch(size=3, rule=rule) + rule = FeedFactory(user=self.user, category=None) + FeedPostFactory.create_batch(size=3, rule=rule) response = self.client.get(reverse("api:news:core:posts-list")) data = response.json() @@ -204,12 +196,10 @@ class PostListViewTestCase(TestCase): self.assertEquals(data["count"], 3) def test_unread_posts(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - PostFactory.create_batch(size=10, rule=rule, read=False) - PostFactory.create_batch(size=10, rule=rule, read=True) + FeedPostFactory.create_batch(size=10, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) response = self.client.get( reverse("api:news:core:posts-list"), {"read": "false"} @@ -225,12 +215,10 @@ class PostListViewTestCase(TestCase): self.assertEquals(post["read"], False) def test_read_posts(self): - rule = CollectionRuleFactory( - user=self.user, category=CategoryFactory(user=self.user) - ) + rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - PostFactory.create_batch(size=20, rule=rule, read=False) - PostFactory.create_batch(size=10, rule=rule, read=True) + FeedPostFactory.create_batch(size=20, rule=rule, read=False) + FeedPostFactory.create_batch(size=10, rule=rule, read=True) response = self.client.get( reverse("api:news:core:posts-list"), {"read": "true"} diff --git a/src/newsreader/news/core/tests/factories.py b/src/newsreader/news/core/tests/factories.py index 966e70b..520f940 100644 --- a/src/newsreader/news/core/tests/factories.py +++ b/src/newsreader/news/core/tests/factories.py @@ -3,7 +3,7 @@ import factory.fuzzy import pytz from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.reddit import REDDIT_URL +from newsreader.news.collection.reddit import REDDIT_API_URL from newsreader.news.core.models import Category, Post @@ -33,9 +33,12 @@ class PostFactory(factory.django.DjangoModelFactory): model = Post +class FeedPostFactory(PostFactory): + rule = factory.SubFactory("newsreader.news.collection.tests.factories.FeedFactory") + + class RedditPostFactory(PostFactory): - remote_identifier = factory.Faker("uuid4") - url = factory.fuzzy.FuzzyText(length=10, prefix=f"{REDDIT_URL}/") + url = factory.fuzzy.FuzzyText(length=10, prefix=f"{REDDIT_API_URL}/") rule = factory.SubFactory( "newsreader.news.collection.tests.factories.SubredditFactory" ) diff --git a/src/newsreader/scss/components/post/_post.scss b/src/newsreader/scss/components/post/_post.scss index 46d389d..6b41844 100644 --- a/src/newsreader/scss/components/post/_post.scss +++ b/src/newsreader/scss/components/post/_post.scss @@ -68,14 +68,9 @@ margin: 20px 0 5px 0; } - & img { - padding: 10px 10px 30px 10px; - - max-width: 70%; - width: inherit; - height: 100%; - - align-self: center; + & img, video { + padding: 10px 0; + max-width: 100%; } }