def form(category_id=None): action = gettext("Add a category") head_titles = [action] form = CategoryForm() form.set_feed_choices(FeedController(current_user.id).read()) if category_id is None: return render_template( "edit_category.html", action=action, head_titles=head_titles, form=form, ) category = CategoryController(current_user.id).get(id=category_id) action = gettext("Edit category") head_titles = [action] if category.name: head_titles.append(category.name) form = CategoryForm(obj=category) form.set_feed_choices(FeedController(current_user.id).read()) form.feeds.data = [feed.id for feed in category.feeds] return render_template( "edit_category.html", action=action, head_titles=head_titles, category=category, form=form, )
def export(): """ Export feeds to OPML. """ include_disabled = request.args.get("includedisabled", "") == "on" include_private = request.args.get("includeprivate", "") == "on" include_exceeded_error_count = (request.args.get( "includeexceedederrorcount", "") == "on") filter = {} if not include_disabled: filter["enabled"] = True if not include_private: filter["private"] = False if not include_exceeded_error_count: filter["error_count__lt"] = application.config["DEFAULT_MAX_ERROR"] user = UserController(current_user.id).get(id=current_user.id) feeds = FeedController(current_user.id).read(**filter) categories = { cat.id: cat.dump() for cat in CategoryController(user.id).read() } response = make_response( render_template( "opml.xml", user=user, feeds=feeds, categories=categories, now=datetime.now(), )) response.headers["Content-Type"] = "application/xml" response.headers["Content-Disposition"] = "attachment; filename=feeds.opml" return response
def form(feed_id=None): action = gettext("Add a feed") categories = CategoryController(current_user.id).read() head_titles = [action] if feed_id is None: form = AddFeedForm() form.set_category_choices(categories) return render_template("edit_feed.html", action=action, head_titles=head_titles, form=form) feed = FeedController(current_user.id).get(id=feed_id) form = AddFeedForm(obj=feed) form.set_category_choices(categories) action = gettext("Edit feed") head_titles = [action] if feed.title: head_titles.append(feed.title) return render_template( "edit_feed.html", action=action, head_titles=head_titles, categories=categories, form=form, feed=feed, )
def process_form(feed_id=None): form = AddFeedForm() feed_contr = FeedController(current_user.id) form.set_category_choices(CategoryController(current_user.id).read()) if not form.validate(): return render_template("edit_feed.html", form=form) existing_feeds = list(feed_contr.read(link=form.link.data)) if existing_feeds and feed_id is None: flash(gettext("Couldn't add feed: feed already exists."), "warning") return redirect(url_for("feed.form", feed_id=existing_feeds[0].id)) # Edit an existing feed feed_attr = { "title": form.title.data, "enabled": form.enabled.data, "link": form.link.data, "site_link": form.site_link.data, "filters": [], "category_id": form.category_id.data, "private": form.private.data, } if not feed_attr["category_id"] or feed_attr["category_id"] == "0": del feed_attr["category_id"] for filter_attr in ("type", "pattern", "action on", "action"): for i, value in enumerate( request.form.getlist(filter_attr.replace(" ", "_"))): if i >= len(feed_attr["filters"]): feed_attr["filters"].append({}) feed_attr["filters"][i][filter_attr] = value if feed_id is not None: feed_contr.update({"id": feed_id}, feed_attr) flash( gettext( "Feed %(feed_title)r successfully updated.", feed_title=feed_attr["title"], ), "success", ) return redirect(url_for("feed.form", feed_id=feed_id)) # Create a new feed new_feed = feed_contr.create(**feed_attr) flash( gettext("Feed %(feed_title)r successfully created.", feed_title=new_feed.title), "success", ) if application.config["CRAWLING_METHOD"] == "default": misc_utils.fetch(current_user.id, new_feed.id) flash(gettext("Downloading articles for the new feed..."), "info") return redirect(url_for("feed.form", feed_id=new_feed.id))
def delete(category_id=None): category = CategoryController(current_user.id).delete(category_id) flash( gettext( "Category %(category_name)s successfully deleted.", category_name=category.name, ), "success", ) return redirect(redirect_url())
def list_(): "Lists the subscribed feeds in a table." art_contr = ArticleController(current_user.id) return render_template( "categories.html", categories=list( CategoryController(current_user.id).read().order_by("name")), feeds_count=FeedController(current_user.id).count_by_category(), unread_article_count=art_contr.count_by_category(readed=False), article_count=art_contr.count_by_category(), )
def update(self, filters, attrs): user_id = attrs.get("user_id", self.user_id) if "feed_id" in attrs: feed = FeedController().get(id=attrs["feed_id"]) assert feed.user_id == user_id, "no right on feed %r" % feed.id attrs["category_id"] = feed.category_id if attrs.get("category_id"): cat = CategoryController().get(id=attrs["category_id"]) assert self.user_id is None or cat.user_id == user_id, ( "no right on cat %r" % cat.id) return super().update(filters, attrs)
def management(): """ Display the management page. """ if request.method == "POST": if None != request.files.get("opmlfile", None): # Import an OPML file data = request.files.get("opmlfile", None) if not misc_utils.allowed_file(data.filename): flash(gettext("File not allowed."), "danger") else: try: nb = import_opml(current_user.nickname, data.read()) if application.config["CRAWLING_METHOD"] == "classic": misc_utils.fetch(current_user.id, None) flash( str(nb) + " " + gettext("feeds imported."), "success") flash(gettext("Downloading articles..."), "info") except: flash(gettext("Impossible to import the new feeds."), "danger") elif None != request.files.get("jsonfile", None): # Import an account data = request.files.get("jsonfile", None) if not misc_utils.allowed_file(data.filename): flash(gettext("File not allowed."), "danger") else: try: nb = import_json(current_user.nickname, data.read()) flash(gettext("Account imported."), "success") except: flash(gettext("Impossible to import the account."), "danger") else: flash(gettext("File not allowed."), "danger") nb_feeds = FeedController(current_user.id).read().count() art_contr = ArticleController(current_user.id) nb_articles = art_contr.read().count() nb_unread_articles = art_contr.read(readed=False).count() nb_categories = CategoryController(current_user.id).read().count() nb_bookmarks = BookmarkController(current_user.id).read().count() return render_template( "management.html", user=current_user, nb_feeds=nb_feeds, nb_articles=nb_articles, nb_unread_articles=nb_unread_articles, nb_categories=nb_categories, nb_bookmarks=nb_bookmarks, )
def user_stream(per_page, nickname=None): """ Display the stream of a user (list of articles of public feed). """ user_contr = UserController() user = user_contr.get(nickname=nickname) if not user.is_public_profile: if current_user.is_authenticated and current_user.id == user.id: flash(gettext("You must set your profile to public."), "info") return redirect(url_for("user.profile")) category_id = int(request.args.get("category_id", 0)) category = CategoryController().read(id=category_id).first() # Load the public feeds filters = {} filters["private"] = False if category_id: filters["category_id"] = category_id feeds = FeedController().read(**filters).all() # Re-initializes the filters to load the articles filters = {} filters["feed_id__in"] = [feed.id for feed in feeds] if category: filters["category_id"] = category_id articles = ArticleController(user.id).read_ordered(**filters) # Server-side pagination page, per_page, offset = get_page_args(per_page_parameter="per_page") pagination = Pagination( page=page, total=articles.count(), css_framework="bootstrap4", search=False, record_name="articles", per_page=per_page, ) return render_template( "user_stream.html", user=user, articles=articles.offset(offset).limit(per_page), category=category, pagination=pagination, )
def feed_view(feed_id=None, user_id=None): feed = FeedController(user_id).get(id=feed_id) category = None if feed.category_id: category = CategoryController(user_id).get(id=feed.category_id) filters = {} filters["feed_id"] = feed_id articles = ArticleController(user_id).read_light(**filters) # Server-side pagination page, per_page, offset = get_page_args(per_page_parameter="per_page") pagination = Pagination( page=page, total=articles.count(), css_framework="bootstrap4", search=False, record_name="articles", per_page=per_page, ) today = datetime.now() try: last_article = articles[0].date first_article = articles[-1].date delta = last_article - first_article average = round(float(articles.count()) / abs(delta.days), 2) except Exception: last_article = datetime.fromtimestamp(0) first_article = datetime.fromtimestamp(0) delta = last_article - first_article average = 0 elapsed = today - last_article return render_template( "feed.html", head_titles=[utils.clear_string(feed.title)], feed=feed, category=category, articles=articles.offset(offset).limit(per_page), pagination=pagination, first_post_date=first_article, end_post_date=last_article, average=average, delta=delta, elapsed=elapsed, )
def process_form(category_id=None): form = CategoryForm() form.set_feed_choices(FeedController(current_user.id).read()) cat_contr = CategoryController(current_user.id) feed_contr = FeedController(current_user.id) if not form.validate(): return render_template("edit_category.html", form=form) existing_cats = list(cat_contr.read(name=form.name.data)) if existing_cats and category_id is None: flash(gettext("Couldn't add category: already exists."), "warning") return redirect( url_for("category.form", category_id=existing_cats[0].id)) # Edit an existing category category_attr = {"name": form.name.data} if category_id is not None: cat_contr.update({"id": category_id}, category_attr) # Update the relation with feeds for feed_id in form.feeds.data: feed_contr.update({"id": feed_id}, {"category_id": category_id}) for feed in current_user.feeds: if feed.category_id == category_id and feed.id not in form.feeds.data: feed_contr.update({"id": feed.id}, {"category_id": None}) flash( gettext( "Category %(cat_name)r successfully updated.", cat_name=category_attr["name"], ), "success", ) return redirect(url_for("category.form", category_id=category_id)) # Create a new category new_category = cat_contr.create(**category_attr) # Update the relation with feeds for feed_id in form.feeds.data: feed_contr.update({"id": feed_id}, {"category_id": new_category.id}) flash( gettext( "Category %(category_name)r successfully created.", category_name=new_category.name, ), "success", ) return redirect(url_for("category.form", category_id=new_category.id))
def process_form(feed_id=None): form = AddFeedForm() feed_contr = FeedController(current_user.id) form.set_category_choices(CategoryController(current_user.id).read()) if not form.validate(): return render_template("edit_feed.html", form=form) existing_feeds = list( feed_contr.read(link=form.link.data, site_link=form.site_link.data)) if existing_feeds and feed_id is None: flash(gettext("Couldn't add feed: feed already exists."), "warning") return redirect(url_for("feed.form", feed_id=existing_feeds[0].id)) feed_attr = { "title": form.title.data, "enabled": form.enabled.data, "link": form.link.data, "site_link": form.site_link.data, "filters": [], "category_id": form.category_id.data, "private": form.private.data, } if not feed_attr["category_id"] or feed_attr["category_id"] == "0": del feed_attr["category_id"] for filter_attr in ("type", "pattern", "action on", "action"): for i, value in enumerate( request.form.getlist(filter_attr.replace(" ", "_"))): if i >= len(feed_attr["filters"]): feed_attr["filters"].append({}) feed_attr["filters"][i][filter_attr] = value if feed_id is not None: # Edit an existing feed feed_contr.update({"id": feed_id}, feed_attr) flash( gettext( "Feed %(feed_title)r successfully updated.", feed_title=feed_attr["title"], ), "success", ) return redirect(url_for("feed.form", feed_id=feed_id)) # Create a new feed url = form.site_link.data if form.site_link.data else form.link.data try: feed = construct_feed_from(url) except requests.exceptions.ConnectionError: flash(gettext("Impossible to connect to the address: {}.".format(url)), "danger") return redirect(url_for("home")) except Exception: logger.exception("something bad happened when fetching %r", url) return redirect(url_for("home")) del feed_attr["link"] del feed_attr["site_link"] # remove keys with empty strings feed_attr = {k: v for k, v in feed_attr.items() if v != ""} feed.update(feed_attr) new_feed = feed_contr.create(**feed) flash( gettext("Feed %(feed_title)r successfully created.", feed_title=new_feed.title), "success", ) if application.config["CRAWLING_METHOD"] == "default": misc_utils.fetch(current_user.id, new_feed.id) flash(gettext("Downloading articles for the new feed..."), "info") return redirect(url_for("feed.form", feed_id=new_feed.id))
def home(): """Displays the home page of the connected user. """ filters = _get_filters(request.args) category_contr = CategoryController(current_user.id) art_contr = ArticleController(current_user.id) categories = {cat.id: cat for cat in category_contr.read().all()} unread = art_contr.count_by_feed(readed=False) nb_unread = art_contr.read_light(readed=False).count() unread_by_cat = art_contr.count_by_category(readed=False) feeds = { feed.id: feed for feed in sorted( current_user.feeds, key=lambda x: x.title.lower(), reverse=False ) } filter_ = request.args.get("filter_", "unread") feed_id = int(request.args.get("feed", 0)) liked = int(request.args.get("liked", 0)) == 1 limit = request.args.get("limit", 1000) query = request.args.get("query", "") search_title = request.args.get("search_title", "off") search_content = request.args.get("search_content", "off") if filter_ in ["read", "unread"]: filters["readed"] = filter_ == "read" if feed_id: filters["feed_id"] = feed_id if liked: filters["like"] = int(liked) == 1 articles = art_contr.read_ordered(**filters) if limit != "all": limit = int(limit) articles = articles.limit(limit) in_error = { feed.id: feed.error_count for feed in FeedController(current_user.id).read(error_count__gt=0).all() } def gen_url( filter_=filter_, limit=limit, feed=feed_id, liked=liked, query=query, search_title=search_title, search_content=search_content, ): return ( "?filter_=%s&limit=%s&feed=%d&liked=%s&query=%s&search_title=%s&search_content=%s" % ( filter_, limit, feed, 1 if liked else 0, query, search_title, search_content, ) ) return render_template( "home.html", nb_unread=nb_unread, gen_url=gen_url, feed_id=feed_id, filter_=filter_, limit=limit, feeds=feeds, categories=categories, unread_by_cat=unread_by_cat, liked=liked, unread=dict(unread), articles=articles.all(), in_error=in_error, )