From 1b0671b34c591f58a5ba0346b39566d17153da2b Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Sun, 23 Mar 2025 21:20:21 +0100 Subject: [PATCH] Initial commit --- docker-compose.yml | 5 - src/newsreader/conf/base.py | 10 - src/newsreader/conf/production.py | 5 - .../js/pages/homepage/components/PostModal.js | 213 +++++++++--------- .../homepage/components/postlist/PostItem.js | 9 +- src/newsreader/js/pages/homepage/constants.js | 1 - src/newsreader/news/collection/models.py | 8 - src/newsreader/news/collection/tasks.py | 86 ------- 8 files changed, 107 insertions(+), 230 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f29e719..02f1fab 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,11 +29,6 @@ x-django-env: &django-env EMAIL_USE_SSL: EMAIL_DEFAULT_FROM: - # Reddit - REDDIT_CLIENT_ID: - REDDIT_CLIENT_SECRET: - REDDIT_CALLBACK_URL: - # Sentry SENTRY_DSN: diff --git a/src/newsreader/conf/base.py b/src/newsreader/conf/base.py index d17234a..5bee027 100644 --- a/src/newsreader/conf/base.py +++ b/src/newsreader/conf/base.py @@ -209,16 +209,6 @@ STATICFILES_FINDERS = [ # Email EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -# Reddit integration -REDDIT_CLIENT_ID = "CLIENT_ID" -REDDIT_CLIENT_SECRET = "CLIENT_SECRET" -REDDIT_REDIRECT_URL = ( - "http://127.0.0.1:8000/accounts/settings/integrations/reddit/callback/" -) - -# Twitter integration -TWITTER_URL = "https://twitter.com" - # Third party settings AXES_HANDLER = "axes.handlers.cache.AxesCacheHandler" AXES_CACHE = "axes" diff --git a/src/newsreader/conf/production.py b/src/newsreader/conf/production.py index 8615aa2..ea22f30 100644 --- a/src/newsreader/conf/production.py +++ b/src/newsreader/conf/production.py @@ -48,11 +48,6 @@ EMAIL_USE_SSL = bool(os.environ.get("EMAIL_USE_SSL")) VERSION = get_current_version(debug=False) ENVIRONMENT = "production" -# Reddit integration -REDDIT_CLIENT_ID = os.environ.get("REDDIT_CLIENT_ID", "") -REDDIT_CLIENT_SECRET = os.environ.get("REDDIT_CLIENT_SECRET", "") -REDDIT_REDIRECT_URL = os.environ.get("REDDIT_CALLBACK_URL", "") - # Third party settings AXES_HANDLER = "axes.handlers.database.AxesDatabaseHandler" diff --git a/src/newsreader/js/pages/homepage/components/PostModal.js b/src/newsreader/js/pages/homepage/components/PostModal.js index 46410b7..4cdc78b 100644 --- a/src/newsreader/js/pages/homepage/components/PostModal.js +++ b/src/newsreader/js/pages/homepage/components/PostModal.js @@ -3,139 +3,136 @@ import { connect } from 'react-redux'; import Cookies from 'js-cookie'; import { unSelectPost, markPostRead, toggleSaved } from '../actions/posts.js'; -import { SAVED_TYPE, SUBREDDIT } from '../constants.js'; +import { SAVED_TYPE } from '../constants.js'; import { formatDatetime } from '../../../utils.js'; class PostModal extends React.Component { modalListener = ::this.modalListener; - readTimer = null; +readTimer = null; - componentDidMount() { - const post = { ...this.props.post }; - const markPostRead = this.props.markPostRead; - const token = Cookies.get('csrftoken'); +componentDidMount() { + const post = { ...this.props.post }; + const markPostRead = this.props.markPostRead; + const token = Cookies.get('csrftoken'); - if (this.props.autoMarking && this.props.selectedType != SAVED_TYPE && !post.read) { - this.readTimer = setTimeout(markPostRead, 3000, post, token); - } - - window.addEventListener('click', this.modalListener); + if (this.props.autoMarking && this.props.selectedType != SAVED_TYPE && !post.read) { + this.readTimer = setTimeout(markPostRead, 3000, post, token); } - componentWillUnmount() { - if (this.readTimer) { - clearTimeout(this.readTimer); - } + window.addEventListener('click', this.modalListener); +} - this.readTimer = null; - - window.removeEventListener('click', this.modalListener); +componentWillUnmount() { + if (this.readTimer) { + clearTimeout(this.readTimer); } - modalListener(e) { - const targetClassName = e.target.className; + this.readTimer = null; - if (this.props.post && targetClassName == 'modal post-modal') { - this.props.unSelectPost(); - } + window.removeEventListener('click', this.modalListener); +} + +modalListener(e) { + const targetClassName = e.target.className; + + if (this.props.post && targetClassName == 'modal post-modal') { + this.props.unSelectPost(); + } +} + +render() { + const post = this.props.post; + const token = Cookies.get('csrftoken'); + const publicationDate = formatDatetime(post.publicationDate); + const titleClassName = post.read ? 'post__title post__title--read' : 'post__title'; + const readButtonDisabled = + post.read || this.props.isUpdating || this.props.selectedType === SAVED_TYPE; + const savedIconClass = post.saved + ? 'post__save post__save--saved saved-icon saved-icon--saved' + : 'post__save saved-icon'; + + let ruleUrl = ''; + + switch (this.props.rule.type) { + default: + ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`; + break; } - render() { - const post = this.props.post; - const token = Cookies.get('csrftoken'); - const publicationDate = formatDatetime(post.publicationDate); - const titleClassName = post.read ? 'post__title post__title--read' : 'post__title'; - const readButtonDisabled = - post.read || this.props.isUpdating || this.props.selectedType === SAVED_TYPE; - const savedIconClass = post.saved - ? 'post__save post__save--saved saved-icon saved-icon--saved' - : 'post__save saved-icon'; + return ( +
+
+
+
+
+ + +
+
+

{`${post.title} `}

+
+
+ {publicationDate} + {post.author && {post.author}} +
- let ruleUrl = ''; - - switch (this.props.rule.type) { - case SUBREDDIT: - ruleUrl = `${this.props.subredditUrl}/${this.props.rule.id}/`; - break; - default: - ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`; - break; - } - - return ( -
-
-
-
-
- - -
-
-

{`${post.title} `}

-
-
- {publicationDate} - {post.author && {post.author}} -
- -
- {this.props.category && ( - + {this.props.category && ( + + - - {this.props.category.name} - - - )} - - - {this.props.rule.name} + {this.props.category.name} - - + )} + + + {this.props.rule.name} - this.props.toggleSaved(post, token)} - /> -
+ + + + + this.props.toggleSaved(post, token)} + />
- - {/* HTML is sanitized by the collectors */} -
+ + {/* HTML is sanitized by the collectors */} +
- ); - } +
+ ); +} } const mapDispatchToProps = dispatch => ({ diff --git a/src/newsreader/js/pages/homepage/components/postlist/PostItem.js b/src/newsreader/js/pages/homepage/components/postlist/PostItem.js index 9322730..495f87b 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/PostItem.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostItem.js @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import Cookies from 'js-cookie'; -import { CATEGORY_TYPE, SAVED_TYPE, SUBREDDIT } from '../../constants.js'; +import { CATEGORY_TYPE, SAVED_TYPE } from '../../constants.js'; import { selectPost, toggleSaved } from '../../actions/posts.js'; import { formatDatetime } from '../../../../utils.js'; @@ -18,12 +18,7 @@ class PostItem extends React.Component { : 'posts__header'; const savedIconClass = post.saved ? 'saved-icon saved-icon--saved' : 'saved-icon'; - let ruleUrl = ''; - if (rule.type === SUBREDDIT) { - ruleUrl = `${this.props.subredditUrl}/${rule.id}/`; - } else { - ruleUrl = `${this.props.feedUrl}/${rule.id}/`; - } + const ruleUrl = `${this.props.feedUrl}/${rule.id}/`; return (
  • diff --git a/src/newsreader/js/pages/homepage/constants.js b/src/newsreader/js/pages/homepage/constants.js index c089954..31524f7 100644 --- a/src/newsreader/js/pages/homepage/constants.js +++ b/src/newsreader/js/pages/homepage/constants.js @@ -2,5 +2,4 @@ export const RULE_TYPE = 'RULE'; export const CATEGORY_TYPE = 'CATEGORY'; export const SAVED_TYPE = 'SAVED'; -export const SUBREDDIT = 'subreddit'; export const FEED = 'feed'; diff --git a/src/newsreader/news/collection/models.py b/src/newsreader/news/collection/models.py index 1eb148c..1937c0d 100644 --- a/src/newsreader/news/collection/models.py +++ b/src/newsreader/news/collection/models.py @@ -1,4 +1,3 @@ -from django.conf import settings from django.db import models from django.urls import reverse from django.utils.translation import gettext as _ @@ -84,13 +83,6 @@ class CollectionRule(TimeStampedModel): @property def source_url(self): - if self.type == RuleTypeChoices.subreddit: - from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL - - return self.url.replace(REDDIT_API_URL, REDDIT_URL) - elif self.type == RuleTypeChoices.twitter_timeline: - return f"{settings.TWITTER_URL}/{self.screen_name}" - return self.url @property diff --git a/src/newsreader/news/collection/tasks.py b/src/newsreader/news/collection/tasks.py index e091ffd..61d163d 100644 --- a/src/newsreader/news/collection/tasks.py +++ b/src/newsreader/news/collection/tasks.py @@ -1,19 +1,13 @@ -from django.conf import settings from django.core.exceptions import ObjectDoesNotExist -from django.core.mail import send_mail from django.utils.translation import gettext as _ -import requests - from celery.exceptions import Reject from celery.utils.log import get_task_logger from newsreader.accounts.models import User from newsreader.celery import app from newsreader.news.collection.choices import RuleTypeChoices -from newsreader.news.collection.exceptions.stream import StreamException from newsreader.news.collection.feed import FeedCollector -from newsreader.news.collection.utils import post from newsreader.utils.celery import MemCacheLock @@ -49,84 +43,6 @@ class FeedTask(app.Task): raise Reject(reason="Task already running", requeue=False) -class RedditTask(app.Task): - name = "RedditTask" - ignore_result = True - - def run(self): - from newsreader.news.collection.reddit import RedditCollector, RedditScheduler - - with MemCacheLock("reddit-task", self.app.oid) as acquired: - if acquired: - logger.info("Running reddit task") - - scheduler = RedditScheduler() - subreddits = scheduler.get_scheduled_rules() - - collector = RedditCollector() - collector.collect(rules=subreddits) - else: - logger.warning("Cancelling task due to existing lock") - - raise Reject(reason="Task already running", requeue=False) - - -class RedditTokenTask(app.Task): - name = "RedditTokenTask" - ignore_result = True - - def run(self, user_pk): - from newsreader.news.collection.reddit import REDDIT_URL - - try: - user = User.objects.get(pk=user_pk) - except ObjectDoesNotExist: - message = f"User {user_pk} does not exist" - logger.exception(message) - - raise Reject(reason=message, requeue=False) - - if not user.reddit_refresh_token: - raise Reject(reason=f"User {user_pk} has no refresh token", requeue=False) - - client_auth = requests.auth.HTTPBasicAuth( - settings.REDDIT_CLIENT_ID, settings.REDDIT_CLIENT_SECRET - ) - - try: - response = post( - f"{REDDIT_URL}/api/v1/access_token", - data={ - "grant_type": "refresh_token", - "refresh_token": user.reddit_refresh_token, - }, - auth=client_auth, - ) - except StreamException: - logger.exception( - f"Failed refreshing reddit access token for user {user_pk}" - ) - - user.reddit_refresh_token = None - user.save() - - message = _( - "Your Reddit account credentials have expired. Re-authenticate in" - " the settings page to keep retrieving Reddit specific information" - " from your account." - ) - - send_mail( - "Reddit account needs re-authentication", message, None, [user.email] - ) - return - - response_data = response.json() - - user.reddit_access_token = response_data["access_token"] - user.save() - - class FaviconTask(app.Task): name = "FaviconTask" ignore_result = True @@ -167,5 +83,3 @@ class FaviconTask(app.Task): FeedTask = app.register_task(FeedTask()) FaviconTask = app.register_task(FaviconTask()) -RedditTask = app.register_task(RedditTask()) -RedditTokenTask = app.register_task(RedditTokenTask())