diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..540a77b --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +# Sphinx documentation +docs/_build/ + +Test_file.json \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/final_task/README.md b/final_task/README.md index 7af281f..001e0cf 100644 --- a/final_task/README.md +++ b/final_task/README.md @@ -1,3 +1,110 @@ -# Your readme here -Some text. -Checkout how to write this file using *markdown*. +#The best RSS-reader! +Program is named rss_reader.py. +##Instalation +The recommended way to install rss-reader is with pip: +* pip install rss_reader-5.0.tar.gz - for Windows +* sudo pip install rss_reader-5.0.tar.gz - for Linux and Mac + +##Description and Functions +``` +usage: rss_reader.py [-h] [--limit LIMIT] [--version] [--json] [--verbose] + [--date DATE] [--to_pdf TO_PDF] [--to_epub TO_EPUB] + [--colorize] + [source] + +Pure Python command-line RSS reader. + +positional arguments: + source RSS URL + +optional arguments: + -h, --help show this help message and exit + --limit LIMIT Limit news topics if this parameter provided + --version Print version info + --json Print result as JSON in stdout + --verbose Outputs verbose status messages + --date DATE Return news from cache with that date + --to_pdf TO_PDF Conversion of news in the pdf format + --to_epub TO_EPUB Conversion of news in the epub format + --colorize Print news in colorized mode in stdout +``` + +To run the program from the command line, you must write the file name and string with URL-address of news site. + +It's print a brief description of the news information that appeared on the news site in human-readable +format and save them in cache. + +Example: +```python rss_reader.py "https://news.yahoo.com/rss/" --limit 1 + +Feed: Yahoo News - Latest News & Headlines + +Updated: Sun, 24 Nov 2019 21:19:04 GMT + +Version: rss20 +-------------------------------------------------------- +Title: Ukraine's President Zelensky said he didn't feel pressured by Trump. Here +'s why that's bogus. +Date: Sat, 23 Nov 2019 08:57:00 -0500 +Link: https://news.yahoo.com/ukraines-president-zelensky-said-didnt-135700678.ht +ml +Summary: [Image: Ukraine's President Zelensky said he didn't feel pressured by +Trump. Here's why that's bogus.] The US government's assistance to Ukraine is vi +tal as it contends with an ongoing conflict with pro-Russian separatists in the +Donbas region. +Source of image: http://l2.yimg.com/uu/api/res/1.2/3h5JxcDjAP1pHR6KUD2JMQ--/YXBw +aWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/business_insider_ +articles_888/7a21ff2ee09c3b4285094c4e64e9602c +``` +###Nice features +To please the eyes you can read news from console in a colorful format. +For this enter such parameter like --colorize and enjoy. + +But this parameter work only for MAC and Linux console. + +###How to read saving news: +If you want to read saved news of a certain date, you must enter a --date in %Y%m%d format and the program will work +without any connecting to the Internet. + +It is also possible to select news from a specific source of a certain date, for this you must enter the url-address +and --date that you want. + +###What formats can you convert to? How to do it. +Program can convert news in ```--to_pdf``` and ```--to_epub``` format. +Specify the path to the file yourself. Also program convert news in ```--json``` format +but print result as JSON in stdout. + +JSON format example: +```json +[ + { + "Feed": "Yahoo", + "Updated": "Fri, 08 Nov 2019 17:04:06 GMT", + "Version": "rss20" + }, + { + "Title": "Some title", + "Date": "Thu, 07 Nov 2019 09:59:52 -0500", + "Summary": "Some summary", + "Link": "Link to news", + "Image": "Some image information" + } +] +``` +#####Ways for converting: +- *You can convert from cache* + +For this case news conversion is not depend on internet. +With ```rss_reader.py --date 20191117 --limit 1 --to_pdf /folder1/folder2/file_name.pdf``` one news for the specified day +would be converted (the same with ```--to_epub```) and would be generated or overwritten file 'file_name.pdf' +in folder what you choose. + +- *You can convert news from the Internet* + +For this case Internet is required. +With ```rss_reader.py https://news.yahoo.com/rss/ --limit 1 --to_pdf /folder1/folder2/file_name.pdf``` one news +from specified link would be converted (the same with ```--to_epub```)and would be generated or overwritten +file 'file_name.epub' in folder what you choose. + + + diff --git a/final_task/rss_reader/NewsCache.py b/final_task/rss_reader/NewsCache.py new file mode 100644 index 0000000..bdf69df --- /dev/null +++ b/final_task/rss_reader/NewsCache.py @@ -0,0 +1,49 @@ +import json +import os.path +import logging + + +class NewsCache: + """Class which create json-file with cached news, + saved all fresh news and return them with certain date + and from certain url-address if such url will be introduced.""" + + def __init__(self, file_name): + self.logger = logging.getLogger(__name__) + self.file_name = file_name + self.logger.info("Check for cache file...") + if os.path.isfile(file_name): + try: + with open(file_name, "r") as json_file: + self.cache_news = json.load(json_file) + self.logger.info("Reading cache file...") + except Exception as ex: + self.logger.error("Error reading file: {} {}".format(type(ex), ex)) + self.cache_news = {} + else: + self.logger.warning("Warning! Caching file is not found!") + self.cache_news = {} + + def caching(self, all_news, url): + """Function which create JSON-file + and save news.""" + for dictionary in all_news: + date_in_news = dictionary.pop('Date key') + title = dictionary['Title'] + self.cache_news.setdefault(date_in_news, {}).setdefault(url, {}).setdefault(title, dictionary) + with open(self.file_name, "w") as file_to_write: + self.logger.info("News caching...") + file_to_write.write(json.dumps(self.cache_news)) + + def returning(self, desired_date, desired_url=None): + """Function which return news from cache-file with certain date + and from certain url-address if such url will be introduced.""" + self.logger.info('News search in cache...') + caching_news_list = [] + if desired_url is not None: + caching_news_list = list(self.cache_news.get(desired_date, {}).get(desired_url, {}).values()) + else: + answer = self.cache_news.get(desired_date, {}) + for title in answer.values(): + caching_news_list += title.values() + return caching_news_list diff --git a/final_task/rss_reader/RSSReader.py b/final_task/rss_reader/RSSReader.py new file mode 100644 index 0000000..f9b4e32 --- /dev/null +++ b/final_task/rss_reader/RSSReader.py @@ -0,0 +1,126 @@ +import datetime +import feedparser +import logging +from bs4 import BeautifulSoup +from RSSReaderException import RSSReaderException +from NewsCache import NewsCache +from unidecode import unidecode +from output import get_output_function + +CACHE_FILE_NAME = "Cache file.json" + + +class RSSReader: + + def __init__(self, date, url, limit, is_json, pdf_file_name, epub_file_name, color): + self.cache = NewsCache(CACHE_FILE_NAME) + self.date = date + self.url = url + self.is_json = is_json + self.limit = limit + self.pdf_file_name = pdf_file_name + self.epub_file_name = epub_file_name + self.color = color + self.logger = logging.getLogger(__name__) + + def url_parsing(self): + parsed_url = feedparser.parse(self.url) + if parsed_url.status != 200: # URL-access check + self.logger.error("Can't load RSS feed.") + raise RSSReaderException('RSS-reader failed. Status: {}.'.format(parsed_url.status)) + else: + self.logger.info('RSS parsed successfully!') + return parsed_url + + def information_about_site(self, parsed_url): + """Function which make dictionary with data of website""" + date = parsed_url.get('updated', str(datetime.datetime.now())) + return { + 'Feed': unidecode(parsed_url['feed']['title']), + 'Updated': date, + 'Version': unidecode(parsed_url['version']), + } + + def make_news_data(self, news): + """ + Function which parsed HTML summary of news and make dictionary with all the necessary news data. + :return dictionary + """ + image_link = 'No image' + if 'summary' in news: + # convert_to_epub(news['summary']) + bs = BeautifulSoup(news['summary'], 'html.parser') + img = bs.find_all('img') + if img: + image_description = unidecode(img[0].get('alt', 'no description')) + image_link = img[0].get('src') + img[0].replaceWith(f' [Image: {image_description}] ') + summary = unidecode(str(bs.text)) + else: + summary = news['title'] + date_info = news['published_parsed'] if 'published' in news else news['updated_parsed'] + date_key = str("{}{}{}".format(date_info.tm_year, date_info.tm_mon, date_info.tm_mday)) + news_summary = { + 'Title': unidecode(news['title']), + 'Date': news['published'] if 'published' in news else news['updated'], + 'Link': news['link'], + 'Summary': summary, + 'Source of image': image_link, + 'Date key': date_key + } + return news_summary + + def news_data_collection(self, parsed_url): + """Function that collects data from all news by calling make_news_data. + :return string of dictionaries + """ + all_information_about_news = parsed_url['entries'] + # amount_of_news = len(parsed_url.entries) + all_news = [] + self.logger.info('News gathering') + for news in all_information_about_news[:self.limit]: + try: # Exception when news is failed + dictionary_of_news_data = self.make_news_data(news) + except Exception as ex: + self.logger.error("Error processing news: {} {}".format(type(ex), ex)) + else: + all_news.append(dictionary_of_news_data) # make list of dictionaries of news data for optional output + return all_news + + def get_news(self): + """Get news!""" + if self.date is None: + if self.url is None: + self.logger.error("URL is not provided.") + return + self.logger.info('Start parsing...') + try: + parsed_url = self.url_parsing() + about_website = self.information_about_site(parsed_url) + except Exception as ex: # for any exception after website parsing + self.logger.error("Error reading site data: {}, {}.".format(type(ex), ex)) + return + string_of_news_dictionaries = self.news_data_collection(parsed_url) + self.cache.caching(string_of_news_dictionaries, self.url) + else: + string_of_news_dictionaries = self.cache.returning(self.date, self.url)[:self.limit] + about_website = None + if not string_of_news_dictionaries: + self.logger.error('There is no news.') + return + file_name = None + if self.is_json: + converter = 'json' + elif self.pdf_file_name: + converter = 'pdf' + file_name = self.pdf_file_name + elif self.epub_file_name: + converter = 'epub' + file_name = self.epub_file_name + elif self.color: + converter = 'color' + else: + self.logger.info('Output news') + converter = 'text' + output = get_output_function(converter) + output(self.logger, string_of_news_dictionaries, about_website, file_name) diff --git a/final_task/rss_reader/RSSReaderException.py b/final_task/rss_reader/RSSReaderException.py new file mode 100644 index 0000000..b3750dd --- /dev/null +++ b/final_task/rss_reader/RSSReaderException.py @@ -0,0 +1,2 @@ +class RSSReaderException(Exception): + pass diff --git a/final_task/rss_reader/__init__.py b/final_task/rss_reader/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/final_task/rss_reader/output.py b/final_task/rss_reader/output.py new file mode 100644 index 0000000..0b02197 --- /dev/null +++ b/final_task/rss_reader/output.py @@ -0,0 +1,143 @@ +import os +import json +import urllib +from colored import attr, fg, bg +from ebooklib import epub +from fpdf import FPDF + + +def get_output_function(converter): + """Output factory""" + if converter == 'json': + return output_json + elif converter == 'pdf': + return output_pdf + elif converter == 'epub': + return output_epub + elif converter == 'color': + return colored_output + elif converter == 'text': + return output + + +def colored_output(logger, all_news, about_website=None, file_name=None): + """Function which print information about site and a set of news with multi-colored text.""" + logger.info('Output multi-colored news =)') + red = fg(1) + black = fg(0) + green = fg(28) + blue = fg(21) + purple = fg(55) + background_site = bg(225) + background_news = bg(195) + reset = attr(0) + if about_website is not None: + for key, value in about_website.items(): + print(f'{background_site}\n{red}{key}: {purple}{value}{reset}') + for number_of_news in all_news: + print(f"{background_news}{black}--------------------------------------------------------{reset}") + for key, value in number_of_news.items(): + print(f'{background_news}{green}{key}: {blue}{value}{reset}') + + +def output(logger, all_news, about_website=None, file_name=None): + """Function which print information about site and a set of news.""" + logger.info('Output news') + if about_website is not None: + for key, value in about_website.items(): + print(f'\n{key}: {value}') + for number_of_news in all_news: + print("--------------------------------------------------------") + for key, value in number_of_news.items(): + print(f'{key}: {value}') + + +def parse_to_json(dictionary): + return json.dumps(dictionary, indent=4) + + +def output_json(logger, all_news, about_website=None, file_name=None): + """Function which print news in stdout in json-format.""" + if about_website is not None: + logger.info('Convert to JSON-format') + print(parse_to_json([about_website] + all_news)) + else: + logger.info('Convert news to JSON-format fom cache') + print(parse_to_json(all_news)) + + +def output_pdf(logger, all_news, about_website=None, file_name=None): + """Function which create or overwrites PDF-file with selected fresh or cached news""" + logger.info('Convert to PDF-format') + pdf = FPDF() + pdf.add_page() + if about_website is not None: + pdf.set_font("Arial", "B", size=14) + pdf.set_fill_color(200, 220, 255) + for value in about_website.values(): + line = 1 + pdf.cell(190, 8, txt=value, ln=line, align="C") + line += 1 + pdf.set_font("Arial", size=10) + pdf.set_line_width(1) + pdf.set_draw_color(35, 41, 153) + for news in all_news: + link = news['Source of image'] + for key, value in news.items(): + if key != 'Summary': + pdf.multi_cell(190, 6, txt=f'{key}: {value}', align="L") + else: + position_y = pdf.get_y() + try: + filename, _ = urllib.request.urlretrieve(link) + pdf.image(filename, 80, position_y, h=30, type='jpeg', link=link) + pdf.ln(31) + os.remove(filename) + except Exception as ex: + logger.warning("Error finding image: {}, {}.".format(type(ex), ex)) + pdf.multi_cell(190, 6, txt=f'{key}: {value}', align="L") + position_y = pdf.get_y() + pdf.set_line_width(1) + pdf.set_draw_color(35, 41, 153) + pdf.line(10, position_y, 200, position_y) + logger.info('Creating of PDF-file') + try: + pdf.output(file_name) + logger.info('Converted successfully!') + except Exception as ex: + logger.error("PDF file writing error : {}, {}.".format(type(ex), ex)) + + +def output_epub(logger, all_news, about_website=None, file_name=None): + """Function which create or overwrites EPUB-file with selected fresh or cached news""" + logger.info('Convert to EPUB-format') + book = epub.EpubBook() + if about_website is not None: + book.set_title(f'RSS news from {about_website["Feed"]}') + else: + book.set_title('RSS news') + book.set_language('en') + chapters = [] + for news in all_news: + chapter = epub.EpubHtml(title=f'Chapter of {news["Date"]}', file_name=f'chap {news["Title"]}.xhtml') + title = f'

{news["Title"]}

' + if news["Source of image"] == "No image": + image = '' + else: + image = f'' + string_of_epub_content = f'Date: {news["Date"]}
' + string_of_epub_content += f'Link: {news["Link"]}
' + string_of_epub_content += f'Summary: {news["Summary"]}
' + string_of_epub_content += f'Source of image: {news["Source of image"]}
' + chapter.content = f'{title}{image}

{string_of_epub_content}

' + book.add_item(chapter) + chapters.append(chapter) + book.add_item(epub.EpubNcx()) + book.add_item(epub.EpubNav()) + book.spine = chapters + logger.info('Creating of PDF-file') + try: + epub.write_epub(file_name, book, {}) + logger.info('Converted successfully!') + except Exception as ex: + logger.error("Error finding image: {}, {}.".format(type(ex), ex)) diff --git a/final_task/rss_reader/python b/final_task/rss_reader/python new file mode 100644 index 0000000..e69de29 diff --git a/final_task/rss_reader/requirements.txt b/final_task/rss_reader/requirements.txt index e69de29..4cf00db 100644 --- a/final_task/rss_reader/requirements.txt +++ b/final_task/rss_reader/requirements.txt @@ -0,0 +1,6 @@ +bs4 +feedparser +unidecode +fpdf +ebooklib +colored \ No newline at end of file diff --git a/final_task/rss_reader/rss_reader.py b/final_task/rss_reader/rss_reader.py index e69de29..26f5d3f 100644 --- a/final_task/rss_reader/rss_reader.py +++ b/final_task/rss_reader/rss_reader.py @@ -0,0 +1,49 @@ +import sys +import os +import argparse +import logging +dir_add = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(dir_add) +from RSSReader import RSSReader + + +VERSION = '5.0' + + +def arg_parse(args): + """Function which parsed command-line arguments.""" + parser = argparse.ArgumentParser(description='Pure Python command-line RSS reader.') + parser.add_argument("source", type=str, nargs='?', + help="RSS URL") + parser.add_argument("--limit", type=int, + help="Limit news topics if this parameter provided") + parser.add_argument("--version", action="store_true", + 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("--date", type=str, + help="Return news from cache with that date") + parser.add_argument("--to_pdf", type=str, + help="Conversion of news in the pdf format") + parser.add_argument("--to_epub", type=str, + help="Conversion of news in the epub format") + parser.add_argument("--colorize", action="store_true", + help="Print news in colorized mode in stdout") + return parser.parse_args(args) + + +def main(): + args = arg_parse(sys.argv[1:]) + level = logging.INFO if args.verbose else logging.ERROR + logging.basicConfig(format='[%(asctime)s][%(levelname)s]%(message)s', stream=sys.stdout, level=level) + if args.version: + print('RSS-reader version {}'.format(VERSION)) # program version call + return + reader = RSSReader(args.date, args.source, args.limit, args.json, args.to_pdf, args.to_epub, args.colorize) + reader.get_news() + + +if __name__ == '__main__': + main() diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..d1fbf38 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,18 @@ +import sys +sys.path.append('rss_reader') +from setuptools import setup, find_packages +from rss_reader import rss_reader + +setup( + name='rss_reader', + version=rss_reader.VERSION, + packages=find_packages(), + install_requires=['feedparser', 'bs4', 'unidecode', 'fpdf', 'ebooklib', 'colored'], + author="Victoria Kondrat'eva", + author_email='sam.kondrateva@gmail.com', + url='https://github.com/Victoria-Sam', + description='RSS Reader', + entry_points={'console_scripts': ['Rss-Reader = rss_reader.rss_reader:main']}, + keywords="rss-reader", + python_requires='>=3.7', +) diff --git a/final_task/tests/NewsCache_test.py b/final_task/tests/NewsCache_test.py new file mode 100644 index 0000000..a452a29 --- /dev/null +++ b/final_task/tests/NewsCache_test.py @@ -0,0 +1,49 @@ +import unittest +import json +import sys +import copy +sys.path.append('../rss_reader') +from NewsCache import NewsCache + +TEST_NEWS_LIST = [ + {"Title": "Air racing tournament unveils an all-electric sports aircraft", + "Date": "Sun, 17 Nov 2019 15:35:00 -0500", + "Link": "link", + "Summary": "Test text", + "Source of image": "img_link", + "Date key": "20191117"}] + +FILE_CONTENTS = {"20191117": + {"https://www.engadget.com/rss.xml": + {"Air racing tournament unveils an all-electric sports aircraft": + {"Title": "Air racing tournament unveils an all-electric sports aircraft", + "Date": "Sun, 17 Nov 2019 15:35:00 -0500", + "Link": "link", + "Summary": "Test text", + "Source of image": "img_link"}}}} + + +class NewsCacheTestCase(unittest.TestCase): + + def setUp(self): + self.cache = NewsCache('Test_file.json') + test_news = copy.deepcopy(TEST_NEWS_LIST) + self.cache.caching(test_news, 'https://www.engadget.com/rss.xml') + + def test_caching(self): + with open('Test_file.json', "r") as test_json: + content = test_json.read() + self.assertEqual(json.loads(content), FILE_CONTENTS) + self.assertEqual(json.loads(content).get('20191117'), FILE_CONTENTS['20191117']) + + def test_returning(self): + result = self.cache.returning('20191117', 'https://www.engadget.com/rss.xml') + self.assertEqual(result[0].get('Title'), "Air racing tournament unveils an all-electric sports aircraft") + self.assertEqual(result[0].get('Date'), "Sun, 17 Nov 2019 15:35:00 -0500") + self.assertEqual(result[0].get('Link'), "link") + self.assertEqual(result[0].get('Summary'), "Test text") + self.assertEqual(result[0].get('Source of image'), "img_link") + + +if __name__ == '__main__': + unittest.main() diff --git a/final_task/tests/RSS_test.py b/final_task/tests/RSS_test.py new file mode 100644 index 0000000..9fd6f72 --- /dev/null +++ b/final_task/tests/RSS_test.py @@ -0,0 +1,84 @@ +import unittest +import sys +import time +sys.path.append('../rss_reader') +from rss_reader import arg_parse +from RSSReader import RSSReader + +RSS_DICT_TEST = { + 'updated': 'Fri, 08 Nov 2019 13:59:06 GMT', + 'href': 'https://news.yahoo.com/rss', + 'feed': {'title': 'Yahoo News - Latest News & Headlines'}, + 'bozo': 0, + 'status': 200, + 'version': 'rss20', + 'etag': '"c3e4279c4a667ef345dcb64d5344199b"', + 'encoding': 'utf-8', + 'entries': [ + { + 'title': 'Graham now says Trump', + 'published': 'Wed, 06 Nov 2019 14:22:10 -0500', + 'published_parsed': time.strptime('06 Nov 2019', '%d %b %Y'), + 'id': 'graham-trump-ukraine-incoherent-quid-pro-quo-192210175.html', + 'summary': '

Trumpday


', + 'link': 'https://news.yahoo.com/graham-trump-ukraine.html' + }, + { + 'title': '2 escaped murder suspects arrested at US-Mexico border', + 'published': 'Thu, 07 Nov 2019 07:25:46 -0500', + 'published_parsed': time.strptime('07 Nov 2019', '%d %b %Y'), + 'id': '2-escaped-murder-suspects-arrested-050940220.html', + 'summary': '

borderare


', + 'link': 'https://news.yahoo.com/2-escaped-murder-suspects-arrested.html' + }, + ], +} + + +class RSSTestCase(unittest.TestCase): + def setUp(self): + self.rss = RSSReader(None, None, None, None, None, None, None) + + def test_make_news_data(self): + result = self.rss.make_news_data(RSS_DICT_TEST['entries'][0]) + self.assertEqual(result.get('Title'), 'Graham now says Trump') + self.assertEqual(result.get('Date'), 'Wed, 06 Nov 2019 14:22:10 -0500') + self.assertEqual(result.get('Summary'), ' [Image: Trump] day') + self.assertEqual(result.get('Link'), 'https://news.yahoo.com/graham-trump-ukraine.html') + self.assertEqual(result.get('Source of image'), 'test2') + + def test_information_about_site(self): + result = self.rss.information_about_site(RSS_DICT_TEST) + self.assertEqual(result.get('Feed'), 'Yahoo News - Latest News & Headlines') + self.assertEqual(result.get('Updated'), 'Fri, 08 Nov 2019 13:59:06 GMT') + self.assertEqual(result.get('Version'), 'rss20') + + def test_news_data_collection(self): + result = self.rss.news_data_collection(RSS_DICT_TEST) + self.assertEqual(result[0].get('Title'), 'Graham now says Trump') + self.assertEqual(result[0].get('Date'), 'Wed, 06 Nov 2019 14:22:10 -0500') + self.assertEqual(result[0].get('Summary'), ' [Image: Trump] day') + self.assertEqual(result[0].get('Link'), 'https://news.yahoo.com/graham-trump-ukraine.html') + self.assertEqual(result[0].get('Source of image'), 'test2') + self.assertEqual(result[1].get('Title'), '2 escaped murder suspects arrested at US-Mexico border') + self.assertEqual(result[1].get('Date'), 'Thu, 07 Nov 2019 07:25:46 -0500') + self.assertEqual(result[1].get('Summary'), ' [Image: border] are') + self.assertEqual(result[1].get('Link'), 'https://news.yahoo.com/2-escaped-murder-suspects-arrested.html') + self.assertEqual(result[1].get('Source of image'), 'test4') + + def test_arg_parse(self): + parser = arg_parse(['source', '--limit', '1', '--json', '--verbose', '--version', + '--date', '20191117', '--to_pdf', 'pdf_file','--to_epub', 'epub_file', '--colorize']) + self.assertTrue(parser.limit == 1) + self.assertTrue(parser.source == 'source') + self.assertTrue(parser.json) + self.assertTrue(parser.verbose) + self.assertTrue(parser.version) + self.assertTrue(parser.date == '20191117') + self.assertTrue(parser.to_pdf == 'pdf_file') + self.assertTrue(parser.to_epub == 'epub_file') + self.assertTrue(parser.colorize) + + +if __name__ == '__main__': + unittest.main() diff --git a/final_task/tests/output_test.py b/final_task/tests/output_test.py new file mode 100644 index 0000000..d6740bd --- /dev/null +++ b/final_task/tests/output_test.py @@ -0,0 +1,69 @@ +import unittest +import sys +from unittest import mock +sys.path.append('../rss_reader') +from output import get_output_function, output, output_json, output_pdf, output_epub, parse_to_json + + +TEST_NEWS_LIST = [ + {"Title": "Some title", + "Date": "Sun, 17 Nov 2019 15:35:00 -0500", + "Link": "link", + "Summary": "Test text", + "Source of image": "img_link"}] + + +class OutputTestCase(unittest.TestCase): + + def test_get_output_function(self): + result = get_output_function('text') + self.assertEqual(result, output) + result = get_output_function('json') + self.assertEqual(result, output_json) + result = get_output_function('pdf') + self.assertEqual(result, output_pdf) + result = get_output_function('epub') + self.assertEqual(result, output_epub) + + def test_output(self): + logger = mock.Mock() + with mock.patch('sys.stdout') as mock_print: + output(logger, TEST_NEWS_LIST) + mock_print.assert_has_calls( + [ + mock.call.write('--------------------------------------------------------'), + mock.call.write('\n'), + mock.call.write("Title: Some title"), + mock.call.write('\n'), + mock.call.write("Date: Sun, 17 Nov 2019 15:35:00 -0500"), + mock.call.write('\n'), + mock.call.write("Link: link"), + mock.call.write('\n'), + mock.call.write("Summary: Test text"), + mock.call.write('\n'), + mock.call.write("Source of image: img_link"), + mock.call.write('\n'), + ] + ) + + def test_output_json(self): + logger = mock.Mock() + with mock.patch('sys.stdout') as mock_print: + output_json(logger, TEST_NEWS_LIST) + mock_print.assert_has_calls( + [ + mock.call.write('[\n ' + '{\n "Title": "Some title",' + '\n "Date": "Sun, 17 Nov 2019 15:35:00 -0500",' + '\n "Link": "link",' + '\n "Summary": "Test text",' + '\n "Source of image": "img_link"' + '\n }' + '\n]'), + mock.call.write('\n'), + ] + ) + + +if __name__ == '__main__': + unittest.main()