diff --git a/README.md b/README.md new file mode 100644 index 0000000..15cb98c --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# PythonHomework +[Introduction to Python] Homework Repository +# How to use +* pip install . +* python rss-reader https://news.tut.by/rss/economics.rss --limit 2 --json +# Parameters +* --help (Show this help message and exit) +* source (RSS URL) +* --limit LIMIT (Limit news topics if this parameter provided) +* --json (Prints result as JSON in stdout) +* --verbose (Outputs verbose status messages) +* --version (Print version info) +* --date () +* --to-pdf (Converter in pdf) +* --to-html (Converter in html) +* --colorize +# JSON structure +news = {"Title": "title", "Date":"date", "Alt image":"alt", "Discription":"discription", "Links":{"News":"link", "Image":"src"} } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..db46317 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +bs4 # for xml and html +feedparser # rss parsing +requests # http requests +python-dateutil +fpdf +colorama \ No newline at end of file diff --git a/rss_app/RSS.py b/rss_app/RSS.py new file mode 100644 index 0000000..0693a3f --- /dev/null +++ b/rss_app/RSS.py @@ -0,0 +1,213 @@ +""" +A file with the RssAggregator class that parses the URL +and performs various actions with the received data +""" + +import feedparser +from bs4 import BeautifulSoup +import json +from dateutil.parser import parse +import urllib +import httplib2 +import os +from colorama import init +from colorama import Fore + + +class RssAggregator(): + + """ Class for rss feed """ + + feedurl = "" + + def __init__(self, source, limit, date, log, colorize): + self.source = source + self.limit = limit + self.date = date + self.log = log + self.colorize = colorize + init() + + def get_news(self): + + """ Returns parsed news and caches it""" + + self.log.info("Getting rss feed") + thefeed = feedparser.parse(self.source) + self.save_to_json_file(thefeed.entries) + return thefeed.entries[:self.limit] + + def print_news(self, entries): + + """ Print rss news """ + + self.log.info("Printing news") + for thefeedentry in entries: + try: + if self.colorize: + print("--------------------------------------------------") + print(f"{Fore.RED}Title:{Fore.RESET} ", Fore.RED + thefeedentry.title + Fore.RESET) + print(f"{Fore.BLUE}Date:{Fore.RESET} ", Fore.BLUE + thefeedentry.published + Fore.RESET, end="\n\n") + print(f"{Fore.YELLOW}Alt image:{Fore.RESET} ", Fore.YELLOW + BeautifulSoup(thefeedentry.description + Fore.RESET, "html.parser").find('img')['alt']) + print(Fore.GREEN + BeautifulSoup(thefeedentry.description, "html.parser").text + Fore.RESET, end="\n\n") + print("Links:") + print(f"{Fore.YELLOW}News:{Fore.RESET} ", Fore.YELLOW + thefeedentry.link + Fore.RESET) + print(f"{Fore.YELLOW}Image:{Fore.RESET} ", Fore.YELLOW + BeautifulSoup(thefeedentry.description + Fore.RESET, "html.parser").find('img')['src']) + else: + print("Title: ", thefeedentry.title) + print("Date: ", thefeedentry.published, end="\n\n") + print("Alt image: ", BeautifulSoup(thefeedentry.description, "html.parser").find('img')['alt']) + print(BeautifulSoup(thefeedentry.description, "html.parser").text, end="\n\n") + print("Links:") + print("News: ", thefeedentry.link) + print("Image: ", BeautifulSoup(thefeedentry.description, "html.parser").find('img')['src']) + except TypeError: + self.log.info("TypeError: 'NoneType'") + + def print_json(self, entries): + + """ Print rss news in json format""" + + self.log.info("RSS news to json") + for thefeedentry in entries: + try: + news = { + "Title": thefeedentry.title, + "Date": thefeedentry.published, + "Alt image": BeautifulSoup(thefeedentry.description, "html.parser").find('img')['alt'], + "Discription": BeautifulSoup(thefeedentry.description, "html.parser").text, + "Links": { + "News": thefeedentry.link, + "Image": BeautifulSoup(thefeedentry.description, "html.parser").find('img')['src'] + } + } + print(json.dumps(news, indent=3)) + except TypeError: + self.log.info("TypeError: 'NoneType'") + + def save_to_json_file(self, entries): + + """ Save rss news to json file""" + + self.log.info("Save news to json file") + news_list = list() + file_name = self.get_file_name() + with open(file_name, "w", encoding="utf-8") as write_file: + for thefeedentry in entries: + try: + news = { + "Title": thefeedentry.title, + "Date": thefeedentry.published, + "Alt image": BeautifulSoup(thefeedentry.description, "html.parser").find('img')['alt'], + "Discription": BeautifulSoup(thefeedentry.description, "html.parser").text, + "Links": { + "News": thefeedentry.link, + "Image": BeautifulSoup(thefeedentry.description, "html.parser").find('img')['src'] + } + } + self.save_image(thefeedentry, file_name) + news_list.append(news) + except TypeError: + self.log.info("TypeError: 'NoneType'") + json.dump(news_list, write_file, indent=3) + + def get_file_name(self): + + """ Getting the file name for storing news """ + + self.log.info("Getting file name") + file_name_list = self.source.split("//") + file_name = file_name_list[1].replace("/", "") + file_name += ".json" + return file_name + + def save_image(self, thefeedentry, file_name): + + """ Save image to file""" + + file_path = self.get_path_image(thefeedentry) + h = httplib2.Http('.cache') + response, content = h.request(BeautifulSoup(thefeedentry.description, "html.parser").find('img')['src']) + try: + out = open(file_path, "wb") + out.write(content) + out.close() + except FileNotFoundError: + self.log.info("Error: image not found") + except OSError: + self.log.info("[Errno 22] Invalid argument {}".format(file_path)) + + def get_path_image(self, thefeedentry): + + """ Get path image """ + + file_name_list = self.source.split("//") + file_name = file_name_list[1].replace("/", "") + folder_path = "image_" + file_name + os.path.sep + if not os.path.exists(folder_path): + self.log.info('Creating directory images') + os.mkdir(folder_path) + img = BeautifulSoup(thefeedentry.description, "html.parser").find('img')['src'] + image = img.split("/") + file_path = os.path.abspath('') + os.path.sep + folder_path + image[-1] + if ".jpg" or ".gif" or ".png" in file_path: + return file_path + file_path += ".jpg" + return file_path + + def get_from_json_file(self): + + """ Get news on the argument --date from json file""" + + self.log.info("Getting news by date") + file_name = self.get_file_name() + news_by_date = list() + try: + with open(file_name, "r") as read_file: + news = json.load(read_file) + for thefeedentry in news: + published = parse(thefeedentry['Date']).strftime('%Y%m%d') + if published >= self.date: + news_by_date.append(thefeedentry) + return news_by_date + except FileNotFoundError: + self.log.info("File not found error") + + def get_news_for_converter(self): + + """ Get news from json file for converter in pdf or html""" + + self.log.info("Getting news for converter") + file_name = self.get_file_name() + news = list() + try: + with open(file_name, "r") as read_file: + news = json.load(read_file) + return news + except FileNotFoundError: + self.log.info("File not found error") + + def print_news_from_file(self, entries): + + """ Print a certain amount of news by date """ + + self.log.info("Printing news by date") + for thefeedentry in entries[:self.limit]: + if self.colorize: + print("--------------------------------------------------") + print(f"{Fore.RED}Title:{Fore.RESET} ", Fore.RED + thefeedentry['Title'] + Fore.RESET) + print(f"{Fore.BLUE}Date:{Fore.RESET} ", Fore.BLUE + thefeedentry['Date'] + Fore.RESET, end="\n\n") + print(f"{Fore.YELLOW}Alt image:{Fore.RESET} ", Fore.YELLOW + thefeedentry['Alt image'] + Fore.RESET) + print(Fore.GREEN + thefeedentry['Discription'] + Fore.RESET, end="\n\n") + print("Links: ") + print(f"{Fore.YELLOW}News:{Fore.RESET} ", Fore.YELLOW + thefeedentry['Links']['News'] + Fore.RESET) + print(f"{Fore.YELLOW}Image:{Fore.RESET} ", Fore.YELLOW + thefeedentry['Links']['Image'] + Fore.RESET) + else: + print("--------------------------------------------------") + print("Title: ", thefeedentry['Title']) + print("Date: ", thefeedentry['Date'], end="\n\n") + print("Alt image: ", thefeedentry['Alt image']) + print(thefeedentry['Discription'], end="\n\n") + print("Links: ") + print("News: ", thefeedentry['Links']['News']) + print("Image: ", thefeedentry['Links']['Image']) diff --git a/rss_app/__init__.py b/rss_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rss_app/converter.py b/rss_app/converter.py new file mode 100644 index 0000000..cca7f9b --- /dev/null +++ b/rss_app/converter.py @@ -0,0 +1,85 @@ +""" +File with a class Converter designed to convert data to pdf and html formats +""" + + +import fpdf +from bs4 import BeautifulSoup +import os + + +class Converter: + + """ News conversion class """ + + fpdf.set_global("SYSTEM_TTFONTS", os.path.join(os.path.dirname(__file__), 'fonts', 'ttf')) + + def __init__(self, source, limit, to_pdf, to_html, log): + self.source = source + self.limit = limit + self.to_pdf = to_pdf + self.to_html = to_html + self.log = log + + def pdf_converter(self, entries): + + """ Convert data to pdf file """ + + self.log.info("Converter in pdf format") + pdf = fpdf.FPDF() + pdf.add_page() + pdf.add_font('DejaVu', '', 'DejaVuSansCondensed.ttf', uni=True) + pdf.set_font('DejaVu', size=14) + for thefeedentry in entries[:self.limit]: + path = self.get_path_image(thefeedentry) + pdf.multi_cell(0, 10, txt="{}".format(thefeedentry['Title'])) + pdf.multi_cell(0, 10, txt="{}".format(thefeedentry['Links']['News'])) + try: + pdf.image(path) + except RuntimeError: + self.log.info("Error add image") + pdf.multi_cell(0, 10, txt="{}".format(thefeedentry['Alt image'])) + pdf.multi_cell(0, 10, txt="{}".format(thefeedentry['Discription'])) + pdf.multi_cell(0, 10, txt="{}".format(thefeedentry['Date'])) + pdf.ln(10) + pdf.output(self.to_pdf) + print(self.to_pdf) + + def html_converter(self, entries): + + """ Convert data to html file """ + + self.log.info("Converter in html format") + with open(self.to_html, "w", encoding="utf-8") as file_text: + file_text.write("") + file_text.write("") + file_text.write("

") + for thefeedentry in entries[:self.limit]: + file_text.write("{}
".format(thefeedentry['Title'])) + file_text.write("{}
".format(thefeedentry['Links']['News'])) + file_text.write("
".format(thefeedentry['Links']['Image'])) + file_text.write("{}
".format(thefeedentry['Discription'])) + file_text.write("{}

".format(thefeedentry['Date'])) + file_text.write("

") + file_text.write("") + file_text.write("") + + def get_path_image(self, thefeedentry): + + """ Get the path of the image to add to the pdf file """ + + self.log.info("Getting path image") + file_name_list = self.source.split("//") + file_name = file_name_list[1].replace("/", "") + folder_path = "image_" + file_name + os.path.sep + if not os.path.exists(folder_path): + self.log.info('Creating directory images') + os.mkdir(folder_path) + img = thefeedentry['Links']['Image'] + image = img.split("/") + file_path = os.path.abspath('') + os.path.sep + folder_path + image[-1] + if ".jpg" or ".gif" or ".png" in file_path: + print(file_path) + return file_path + file_path += ".jpg" + return file_path diff --git a/rss_app/fonts/fontconfig/20-unhint-small-dejavu-sans-mono.conf b/rss_app/fonts/fontconfig/20-unhint-small-dejavu-sans-mono.conf new file mode 100644 index 0000000..102dbcc --- /dev/null +++ b/rss_app/fonts/fontconfig/20-unhint-small-dejavu-sans-mono.conf @@ -0,0 +1,26 @@ + + + + + + + DejaVu Sans Mono + + + 7.5 + + + false + + + diff --git a/rss_app/fonts/fontconfig/20-unhint-small-dejavu-sans.conf b/rss_app/fonts/fontconfig/20-unhint-small-dejavu-sans.conf new file mode 100644 index 0000000..ee69996 --- /dev/null +++ b/rss_app/fonts/fontconfig/20-unhint-small-dejavu-sans.conf @@ -0,0 +1,26 @@ + + + + + + + DejaVu Sans + + + 7.5 + + + false + + + diff --git a/rss_app/fonts/fontconfig/20-unhint-small-dejavu-serif.conf b/rss_app/fonts/fontconfig/20-unhint-small-dejavu-serif.conf new file mode 100644 index 0000000..cf6caa2 --- /dev/null +++ b/rss_app/fonts/fontconfig/20-unhint-small-dejavu-serif.conf @@ -0,0 +1,26 @@ + + + + + + + DejaVu Serif + + + 7.5 + + + false + + + diff --git a/rss_app/fonts/fontconfig/57-dejavu-sans-mono.conf b/rss_app/fonts/fontconfig/57-dejavu-sans-mono.conf new file mode 100644 index 0000000..cc42561 --- /dev/null +++ b/rss_app/fonts/fontconfig/57-dejavu-sans-mono.conf @@ -0,0 +1,62 @@ + + + + + + + Bepa Mono + + DejaVu Sans Mono + + + + Bitstream Prima Sans Mono + + DejaVu Sans Mono + + + + Bitstream Vera Sans Mono + + DejaVu Sans Mono + + + + DejaVu LGC Sans Mono + + DejaVu Sans Mono + + + + Olwen Sans Mono + + DejaVu Sans Mono + + + + SUSE Sans Mono + + DejaVu Sans Mono + + + + + DejaVu Sans Mono + + monospace + + + + + monospace + + DejaVu Sans Mono + + + diff --git a/rss_app/fonts/fontconfig/57-dejavu-sans.conf b/rss_app/fonts/fontconfig/57-dejavu-sans.conf new file mode 100644 index 0000000..565cab5 --- /dev/null +++ b/rss_app/fonts/fontconfig/57-dejavu-sans.conf @@ -0,0 +1,87 @@ + + + + + + + Arev Sans + + DejaVu Sans + + + + Bepa + + DejaVu Sans + + + + Bitstream Prima Sans + + DejaVu Sans + + + + Bitstream Vera Sans + + DejaVu Sans + + + + DejaVu LGC Sans + + DejaVu Sans + + + + Hunky Sans + + DejaVu Sans + + + + Olwen Sans + + DejaVu Sans + + + + SUSE Sans + + DejaVu Sans + + + + Verajja + + DejaVu Sans + + + + + VerajjaPDA + + DejaVu Sans + + + + + DejaVu Sans + + sans-serif + + + + + sans-serif + + DejaVu Sans + + + diff --git a/rss_app/fonts/fontconfig/57-dejavu-serif.conf b/rss_app/fonts/fontconfig/57-dejavu-serif.conf new file mode 100644 index 0000000..a922e9b --- /dev/null +++ b/rss_app/fonts/fontconfig/57-dejavu-serif.conf @@ -0,0 +1,69 @@ + + + + + + + Bitstream Prima Serif + + DejaVu Serif + + + + Bitstream Vera Serif + + DejaVu Serif + + + + DejaVu LGC Serif + + DejaVu Serif + + + + Hunky Serif + + DejaVu Serif + + + + Olwen Serif + + DejaVu Serif + + + + SUSE Serif + + DejaVu Serif + + + + + Verajja Serif + + DejaVu Serif + + + + + DejaVu Serif + + serif + + + + + serif + + DejaVu Serif + + + diff --git a/rss_app/fonts/ttf/DejaVuMathTeXGyre.ttf b/rss_app/fonts/ttf/DejaVuMathTeXGyre.ttf new file mode 100644 index 0000000..8a24f06 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuMathTeXGyre.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSans-Bold.ttf b/rss_app/fonts/ttf/DejaVuSans-Bold.ttf new file mode 100644 index 0000000..6d65fa7 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSans-Bold.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSans-BoldOblique.ttf b/rss_app/fonts/ttf/DejaVuSans-BoldOblique.ttf new file mode 100644 index 0000000..753f2d8 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSans-BoldOblique.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSans-ExtraLight.ttf b/rss_app/fonts/ttf/DejaVuSans-ExtraLight.ttf new file mode 100644 index 0000000..b09f32d Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSans-ExtraLight.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSans-Oblique.ttf b/rss_app/fonts/ttf/DejaVuSans-Oblique.ttf new file mode 100644 index 0000000..999bac7 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSans-Oblique.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSans.ttf b/rss_app/fonts/ttf/DejaVuSans.ttf new file mode 100644 index 0000000..e5f7eec Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSans.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansCondensed-Bold.ttf b/rss_app/fonts/ttf/DejaVuSansCondensed-Bold.ttf new file mode 100644 index 0000000..22987c6 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansCondensed-Bold.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansCondensed-BoldOblique.ttf b/rss_app/fonts/ttf/DejaVuSansCondensed-BoldOblique.ttf new file mode 100644 index 0000000..f5fa0ca Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansCondensed-BoldOblique.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansCondensed-Oblique.ttf b/rss_app/fonts/ttf/DejaVuSansCondensed-Oblique.ttf new file mode 100644 index 0000000..7fde907 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansCondensed-Oblique.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansCondensed.cw127.pkl b/rss_app/fonts/ttf/DejaVuSansCondensed.cw127.pkl new file mode 100644 index 0000000..aadd269 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansCondensed.cw127.pkl differ diff --git a/rss_app/fonts/ttf/DejaVuSansCondensed.pkl b/rss_app/fonts/ttf/DejaVuSansCondensed.pkl new file mode 100644 index 0000000..a8e73f5 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansCondensed.pkl differ diff --git a/rss_app/fonts/ttf/DejaVuSansCondensed.ttf b/rss_app/fonts/ttf/DejaVuSansCondensed.ttf new file mode 100644 index 0000000..3259bc2 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansCondensed.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansMono-Bold.ttf b/rss_app/fonts/ttf/DejaVuSansMono-Bold.ttf new file mode 100644 index 0000000..8184ced Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansMono-Bold.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansMono-BoldOblique.ttf b/rss_app/fonts/ttf/DejaVuSansMono-BoldOblique.ttf new file mode 100644 index 0000000..754dca7 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansMono-BoldOblique.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansMono-Oblique.ttf b/rss_app/fonts/ttf/DejaVuSansMono-Oblique.ttf new file mode 100644 index 0000000..4c858d4 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansMono-Oblique.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSansMono.ttf b/rss_app/fonts/ttf/DejaVuSansMono.ttf new file mode 100644 index 0000000..f578602 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSansMono.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerif-Bold.ttf b/rss_app/fonts/ttf/DejaVuSerif-Bold.ttf new file mode 100644 index 0000000..3bb755f Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerif-Bold.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerif-BoldItalic.ttf b/rss_app/fonts/ttf/DejaVuSerif-BoldItalic.ttf new file mode 100644 index 0000000..a36dd4b Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerif-BoldItalic.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerif-Italic.ttf b/rss_app/fonts/ttf/DejaVuSerif-Italic.ttf new file mode 100644 index 0000000..805daf2 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerif-Italic.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerif.ttf b/rss_app/fonts/ttf/DejaVuSerif.ttf new file mode 100644 index 0000000..0b803d2 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerif.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerifCondensed-Bold.ttf b/rss_app/fonts/ttf/DejaVuSerifCondensed-Bold.ttf new file mode 100644 index 0000000..222bf13 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerifCondensed-Bold.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerifCondensed-BoldItalic.ttf b/rss_app/fonts/ttf/DejaVuSerifCondensed-BoldItalic.ttf new file mode 100644 index 0000000..e446636 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerifCondensed-BoldItalic.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerifCondensed-Italic.ttf b/rss_app/fonts/ttf/DejaVuSerifCondensed-Italic.ttf new file mode 100644 index 0000000..c529df3 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerifCondensed-Italic.ttf differ diff --git a/rss_app/fonts/ttf/DejaVuSerifCondensed.ttf b/rss_app/fonts/ttf/DejaVuSerifCondensed.ttf new file mode 100644 index 0000000..d3959b3 Binary files /dev/null and b/rss_app/fonts/ttf/DejaVuSerifCondensed.ttf differ diff --git a/rss_app/main.py b/rss_app/main.py new file mode 100644 index 0000000..abf3fbc --- /dev/null +++ b/rss_app/main.py @@ -0,0 +1,87 @@ +""" +The main file to run the script +""" + + +import argparse +from rss_app.RSS import RssAggregator +from rss_app.converter import Converter +import logging +from datetime import datetime + + +__version__ = "0.5.0" + + +def get_args(): + + """ Reads and returns arguments """ + + parser = argparse.ArgumentParser(description="Pure Python command-line RSS reader.") + parser.add_argument('source', type=str, help="RSS URL") + parser.add_argument("-v", "--version", action="version", version="%(prog)s version" + + "{version}".format(version=__version__), default=None, help="Print version info") + parser.add_argument("--json", action="store_true", help="Print result as JSON in stdout") + parser.add_argument("--verbose", action="store_true", help="Outputs verbose status messages") + parser.add_argument("--limit", type=int, default=None, help="Limit news topics if this parameter provided") + parser.add_argument("--date", type=str, help="For example: --date 20191020") + parser.add_argument("--to-pdf", type=str, help="This argument receives the path where new file will" + + "be saved in format pdf. For example: --to-pdf d:/news.pdf") + parser.add_argument("--to-html", type=str, help="This argument receives the path where new file will" + + "be saved in format html. For example: --to-html d:/news.html") + parser.add_argument("--colorize", action="store_true", help="Print the result of the utility in colorized mode") + args = parser.parse_args() + return args + + +def main(): + + """ Reads arguments and print news """ + + args = get_args() + if args.version: + print(args.version) + if args.verbose: + logger = get_log() + else: + logger = logging.getLogger() + rssobject = RssAggregator(args.source, args.limit, args.date, logger, args.colorize) + converter = Converter(args.source, args.limit, args.to_pdf, args.to_html, logger) + news = rssobject.get_news() + if args.to_pdf: + news_for_converter_pdf = rssobject.get_news_for_converter() + converter.pdf_converter(news_for_converter_pdf) + if args.to_html: + news_for_converter_html = rssobject.get_news_for_converter() + converter.html_converter(news_for_converter_html) + if args.date: + try: + datetime.strptime(args.date, "%Y%m%d") + data = rssobject.get_from_json_file() + rssobject.print_news_from_file(data) + return + except ValueError: + print("ValueError: Time data {} does not match format %Y%m%d".format(args.date)) + return + if args.json: + rssobject.print_json(news) + else: + rssobject.print_news(news) + logger.info("Exit") + + +def get_log(): + + """ Returns logger with DEBUG level for creating logs in stdout """ + + logger = logging.getLogger(__name__) + logger.level = logging.DEBUG + formatter = logging.Formatter('%(levelname)s: %(message)s') + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(formatter) + logger.addHandler(stream_handler) + return logger + + +if __name__ == "__main__": + main() diff --git a/rss_app/test_date_html.txt b/rss_app/test_date_html.txt new file mode 100644 index 0000000..d2d63ea --- /dev/null +++ b/rss_app/test_date_html.txt @@ -0,0 +1 @@ +

Почти окончательно. Минфин рассказал, как будут взимать дорожный налог
https://news.tut.by/economics/663393.html?utm_campaign=news-feed&utm_medium=rss&utm_source=rss-news

Министерство финансов подготовило проект указа об изменении существующей системы уплаты госпошлины за доступ транспортных средств к участию в дорожном движении.
Sun, 01 Dec 2019 19:57:00 +0300

\ No newline at end of file diff --git a/rss_app/test_json.json b/rss_app/test_json.json new file mode 100644 index 0000000..b49fdf8 --- /dev/null +++ b/rss_app/test_json.json @@ -0,0 +1,12 @@ +[ + { + "Title": "\u041f\u043e\u0447\u0442\u0438 \u043e\u043a\u043e\u043d\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e. \u041c\u0438\u043d\u0444\u0438\u043d \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u043b, \u043a\u0430\u043a \u0431\u0443\u0434\u0443\u0442 \u0432\u0437\u0438\u043c\u0430\u0442\u044c \u0434\u043e\u0440\u043e\u0436\u043d\u044b\u0439 \u043d\u0430\u043b\u043e\u0433", + "Date": "Sun, 01 Dec 2019 19:57:00 +0300", + "Alt image": "\u0424\u043e\u0442\u043e: \u0413\u043b\u0435\u0431 \u041c\u0430\u043b\u043e\u0444\u0435\u0435\u0432, TUT.BY", + "Discription": "\u041c\u0438\u043d\u0438\u0441\u0442\u0435\u0440\u0441\u0442\u0432\u043e \u0444\u0438\u043d\u0430\u043d\u0441\u043e\u0432 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043b\u043e \u043f\u0440\u043e\u0435\u043a\u0442 \u0443\u043a\u0430\u0437\u0430 \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0443\u043f\u043b\u0430\u0442\u044b \u0433\u043e\u0441\u043f\u043e\u0448\u043b\u0438\u043d\u044b \u0437\u0430 \u0434\u043e\u0441\u0442\u0443\u043f \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u044b\u0445 \u0441\u0440\u0435\u0434\u0441\u0442\u0432 \u043a \u0443\u0447\u0430\u0441\u0442\u0438\u044e \u0432 \u0434\u043e\u0440\u043e\u0436\u043d\u043e\u043c \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0438.", + "Links": { + "News": "https://news.tut.by/economics/663393.html?utm_campaign=news-feed&utm_medium=rss&utm_source=rss-news", + "Image": "https://img.tyt.by/thumbnails/n/avto/0c/4/1593_m6_trassa_ograzhdenie_doroga_20190326_mag_tutby_phsl.jpg" + } + } +] \ No newline at end of file diff --git a/rss_app/unit_test.py b/rss_app/unit_test.py new file mode 100644 index 0000000..e4e5fe5 --- /dev/null +++ b/rss_app/unit_test.py @@ -0,0 +1,45 @@ +"""Unittests are performed in this module.""" + + +import unittest +import logging +import json + +from rss_app.RSS import RssAggregator +from rss_app.converter import Converter + + +class TestRSS(unittest.TestCase): + + def setUp(self): + logger = logging.getLogger() + logger.level = logging.DEBUG + stream_handler = logging.StreamHandler() + logger.addHandler(stream_handler) + + self.url = "http://news.com/rss" + self.to_pdf = "d:/test_html.pdf" + self.to_html = "d:/test_html.html" + self.converter = Converter(self.url, 1, self.to_pdf, self.to_html, logger) + self.test_date = RssAggregator(self.url, 1, 1, logger) + + with open("test_json.json", "r") as read_file: + self.news = json.load(read_file) + + def test_get_file_name(self): + test_url = "news.comrss.json" + self.assertEqual(self.test_date.get_file_name(), test_url) + + def test_html_converter(self): + with open("test_date_html.txt", "r") as rf: + test_html_date = rf.read() + + self.converter.html_converter(self.news) + + with open(self.to_html, "r") as rf: + new_html_date = rf.read() + + self.assertEqual(str(new_html_date), test_html_date) + + if __name__ == "__main__": + unittest.main() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..64b7fd4 --- /dev/null +++ b/setup.py @@ -0,0 +1,38 @@ +from setuptools import setup, find_packages +from os import path + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + + +setup( + name='rss-reader', + version='0.5.0', + description='A simple Python3.8 rss reader', + long_description=long_description, + long_description_content_type='text/markdown', + url='https://github.com/introduction-to-python-bsuir-2019/PythonHomework', + author='ge2nadiy', + author_email='ge2nadiy.k@gmail.com', + keywords='simple rss reader', + packages=find_packages(), + package_data={ + 'rss_app': [ + 'fonts/ttf/DejaVuSansCondensed.ttf' + ] + }, + python_requires='>=3.8', + install_requires=['bs4', 'feedparser', 'requests', 'python-dateutil', 'httplib2', 'fpdf', 'colorama'], + entry_points={ + 'console_scripts': [ + 'rss-reader=rss_app.main:main', + ], + }, + project_urls={ + 'Bug Reports': 'https://github.com/introduction-to-python-bsuir-2019/PythonHomework/issues', + 'Source': 'https://github.com/introduction-to-python-bsuir-2019/PythonHomework', + }, +)