From 8219075f9d2480237525a1ac3ba6650f834ec8fd Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Tue, 29 Oct 2019 21:43:58 +0300 Subject: [PATCH 01/15] first iteration in process --- .gitignore | 4 ++++ iter1.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ parser.log | 24 +++++++++++++++++++ parser.py | 26 +++++++++++++++++++++ setup.py | 6 +++++ 5 files changed, 127 insertions(+) create mode 100644 .gitignore create mode 100644 iter1.py create mode 100644 parser.log create mode 100644 parser.py create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..070871a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +.vscode/ +test.py +test2.py \ No newline at end of file diff --git a/iter1.py b/iter1.py new file mode 100644 index 0000000..b8b130c --- /dev/null +++ b/iter1.py @@ -0,0 +1,67 @@ +from bs4 import BeautifulSoup +import feedparser +import json +import re +import logging + +# url = "https://news.yahoo.com/rss" +# url1 = "https://news.google.com/rss" +# url2 = "https://www.theguardian.com/world/rss" + +logging.basicConfig(format = u'%(levelname)-8s [%(asctime)s] %(message)s', + level = logging.DEBUG, filename = u'parser.log') + +def getEntries(url, json, verbose, limit): + + channel = feedparser.parse(url) + + print("Feed: ", channel.feed.title, '\n') + + limit = limit or len(channel.entries) + + for item in (channel.entries): + + if (limit > 0): + loggingItems(item) + + if (json): + print(getJSON(item)) + + else: + print("Title: ", item.title) + print("Date: ", item.published) + print("Link: ", item.link, '\n') + print("Description: ", getDescription(item.description), '\n') + print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", + item.media_content[0]['url'],'\n') + + limit -= 1 + +def getDescription(item): + return BeautifulSoup(item, features="html.parser").getText() + +def printLogs(): + with open('parser.log', 'r') as f: + for line in f: + print(line) + +def loggingItems(item): + logging.debug("Title: " + str(item.title)) + logging.debug("Date: " + str(item.published)) + logging.debug("Link: " + str(item.link) + '\n') + # logging.debug("Description: ", item.description[1], '\n') + logging.debug("Links:"+"\n[1]: " + str(item.link) + + "(link)\n[2]: " + str(item.media_content[0]['url'])+'\n') + + +def getJSON(item): + + return json.dumps({ + 'Title: ': item.title, + 'Date: ' : item.published, + 'Link: ' : item.link, + #'Description: ' : item + }) + + + diff --git a/parser.log b/parser.log new file mode 100644 index 0000000..fdc94d0 --- /dev/null +++ b/parser.log @@ -0,0 +1,24 @@ +DEBUG [2019-10-29 10:51:47,642] Title: ISIS spokesman reportedly killed during raid in Syria +DEBUG [2019-10-29 10:51:47,643] Date: Sun, 27 Oct 2019 23:17:58 -0400 +DEBUG [2019-10-29 10:51:47,643] Link: https://news.yahoo.com/isis-spokesman-reportedly-killed-during-031758597.html + +DEBUG [2019-10-29 10:51:47,643] Links: +[1]: https://news.yahoo.com/isis-spokesman-reportedly-killed-during-031758597.html(link) +[2]: + +DEBUG [2019-10-29 10:51:47,647] Title: Nunes Aide Is Leaking the Ukraine Whistleblowers Name, Sources Say +DEBUG [2019-10-29 10:51:47,648] Date: Mon, 28 Oct 2019 20:02:34 -0400 +DEBUG [2019-10-29 10:51:47,648] Link: https://news.yahoo.com/nunes-aide-leaking-ukraine-whistleblower-000123329.html + +DEBUG [2019-10-29 10:51:47,648] Links: +[1]: https://news.yahoo.com/nunes-aide-leaking-ukraine-whistleblower-000123329.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/AvQQxwnYPZOrChHWOvLHHg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/thedailybeast.com/1b3e580377a3e24f01013a10fb8d2e36 + +DEBUG [2019-10-29 21:35:59,628] Title: Washington Post changes al-Baghdadi headline that called terror leader an 'austere religious scholar' +DEBUG [2019-10-29 21:35:59,685] Date: Mon, 28 Oct 2019 10:37:26 -0400 +DEBUG [2019-10-29 21:35:59,685] Link: https://news.yahoo.com/washington-post-al-baghdadi-obit-headline-austere-religious-scholar-143726879.html + +DEBUG [2019-10-29 21:35:59,685] Links: +[1]: https://news.yahoo.com/washington-post-al-baghdadi-obit-headline-austere-religious-scholar-143726879.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/7rwCo096GFtbwM3xksJyJw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/e89af620-f98f-11e9-a7fb-7c7ac5390849 + diff --git a/parser.py b/parser.py new file mode 100644 index 0000000..b895663 --- /dev/null +++ b/parser.py @@ -0,0 +1,26 @@ +import argparse +import iter1 + +parser = argparse.ArgumentParser(description='Pure Python command-line RSS reader') + +parser.add_argument('source', action='store', help='RSS URL') +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('--limit', action='store', help='Limit news topics if this parameter provided') + +args = parser.parse_args() + + +if (args.version): + version = 1.0 + print(version) + +elif(args.verbose): + iter1.printLogs() + +else: + if not(args.limit): + args.limit = 0 + iter1.getEntries(url, args.json, args.verbose, int(args.limit)) + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6eac845 --- /dev/null +++ b/setup.py @@ -0,0 +1,6 @@ +from setuptools import setup, find_packages +setup( + name="HelloWorld", + version="0.1", + packages=find_packages(), +) \ No newline at end of file From f631f768f8c3139eebd1917cb7a77b34eb61a866 Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Wed, 30 Oct 2019 21:53:37 +0300 Subject: [PATCH 02/15] second iteration --- .gitignore | 3 +-- LICENSE.txt | 0 parser.log | 24 ------------------------ rss/__init__.py | 3 +++ iter1.py => rss/iter1.py | 26 ++++++++++++++------------ parser.py => rss/rss_parser.py | 1 + setup.py | 33 ++++++++++++++++++++++++++++++--- 7 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 LICENSE.txt delete mode 100644 parser.log create mode 100644 rss/__init__.py rename iter1.py => rss/iter1.py (75%) rename parser.py => rss/rss_parser.py (99%) diff --git a/.gitignore b/.gitignore index 070871a..b96a62b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ __pycache__/ .vscode/ -test.py -test2.py \ No newline at end of file +test.py \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..e69de29 diff --git a/parser.log b/parser.log deleted file mode 100644 index fdc94d0..0000000 --- a/parser.log +++ /dev/null @@ -1,24 +0,0 @@ -DEBUG [2019-10-29 10:51:47,642] Title: ISIS spokesman reportedly killed during raid in Syria -DEBUG [2019-10-29 10:51:47,643] Date: Sun, 27 Oct 2019 23:17:58 -0400 -DEBUG [2019-10-29 10:51:47,643] Link: https://news.yahoo.com/isis-spokesman-reportedly-killed-during-031758597.html - -DEBUG [2019-10-29 10:51:47,643] Links: -[1]: https://news.yahoo.com/isis-spokesman-reportedly-killed-during-031758597.html(link) -[2]: - -DEBUG [2019-10-29 10:51:47,647] Title: Nunes Aide Is Leaking the Ukraine Whistleblowers Name, Sources Say -DEBUG [2019-10-29 10:51:47,648] Date: Mon, 28 Oct 2019 20:02:34 -0400 -DEBUG [2019-10-29 10:51:47,648] Link: https://news.yahoo.com/nunes-aide-leaking-ukraine-whistleblower-000123329.html - -DEBUG [2019-10-29 10:51:47,648] Links: -[1]: https://news.yahoo.com/nunes-aide-leaking-ukraine-whistleblower-000123329.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/AvQQxwnYPZOrChHWOvLHHg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/thedailybeast.com/1b3e580377a3e24f01013a10fb8d2e36 - -DEBUG [2019-10-29 21:35:59,628] Title: Washington Post changes al-Baghdadi headline that called terror leader an 'austere religious scholar' -DEBUG [2019-10-29 21:35:59,685] Date: Mon, 28 Oct 2019 10:37:26 -0400 -DEBUG [2019-10-29 21:35:59,685] Link: https://news.yahoo.com/washington-post-al-baghdadi-obit-headline-austere-religious-scholar-143726879.html - -DEBUG [2019-10-29 21:35:59,685] Links: -[1]: https://news.yahoo.com/washington-post-al-baghdadi-obit-headline-austere-religious-scholar-143726879.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/7rwCo096GFtbwM3xksJyJw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/e89af620-f98f-11e9-a7fb-7c7ac5390849 - diff --git a/rss/__init__.py b/rss/__init__.py new file mode 100644 index 0000000..de736b9 --- /dev/null +++ b/rss/__init__.py @@ -0,0 +1,3 @@ +import parser +from iter1 import getEntries +name = "piter" \ No newline at end of file diff --git a/iter1.py b/rss/iter1.py similarity index 75% rename from iter1.py rename to rss/iter1.py index b8b130c..0b4fb97 100644 --- a/iter1.py +++ b/rss/iter1.py @@ -4,12 +4,13 @@ import re import logging -# url = "https://news.yahoo.com/rss" +url = "https://news.yahoo.com/rss" # url1 = "https://news.google.com/rss" # url2 = "https://www.theguardian.com/world/rss" -logging.basicConfig(format = u'%(levelname)-8s [%(asctime)s] %(message)s', - level = logging.DEBUG, filename = u'parser.log') +logging.basicConfig(format=u'%(levelname)-8s [%(asctime)s] %(message)s', + level=logging.DEBUG, filename=u'parser.log') + def getEntries(url, json, verbose, limit): @@ -32,24 +33,26 @@ def getEntries(url, json, verbose, limit): print("Date: ", item.published) print("Link: ", item.link, '\n') print("Description: ", getDescription(item.description), '\n') - print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", - item.media_content[0]['url'],'\n') - + print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", + item.media_content[0]['url'], '\n') limit -= 1 + def getDescription(item): return BeautifulSoup(item, features="html.parser").getText() + def printLogs(): with open('parser.log', 'r') as f: for line in f: print(line) + def loggingItems(item): logging.debug("Title: " + str(item.title)) logging.debug("Date: " + str(item.published)) logging.debug("Link: " + str(item.link) + '\n') - # logging.debug("Description: ", item.description[1], '\n') + logging.debug("Description: ", getDescription(item.description), '\n') logging.debug("Links:"+"\n[1]: " + str(item.link) + "(link)\n[2]: " + str(item.media_content[0]['url'])+'\n') @@ -58,10 +61,9 @@ def getJSON(item): return json.dumps({ 'Title: ': item.title, - 'Date: ' : item.published, - 'Link: ' : item.link, - #'Description: ' : item + 'Date: ': item.published, + 'Link: ': item.link, + 'Description: ': getDescription(item.description) }) - - +getEntries(url, False, False, 2) \ No newline at end of file diff --git a/parser.py b/rss/rss_parser.py similarity index 99% rename from parser.py rename to rss/rss_parser.py index b895663..7140cd8 100644 --- a/parser.py +++ b/rss/rss_parser.py @@ -9,6 +9,7 @@ parser.add_argument('--verbose', action='store_true', help='Outputs verbose status messages') parser.add_argument('--limit', action='store', help='Limit news topics if this parameter provided') + args = parser.parse_args() diff --git a/setup.py b/setup.py index 6eac845..d0d377c 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,33 @@ from setuptools import setup, find_packages + +with open("README.md", "r") as fh: + long_description = fh.read() + setup( - name="HelloWorld", - version="0.1", + name="rss-reader", + version="1.0", packages=find_packages(), -) \ No newline at end of file + #scripts=[], + install_requires=['beautifulsoup4==4.8.1', 'feedparser==5.2.1'], + author="Ilya Khonenko", + author_email="honenkoi@gmail.com", + description="This is rss-reader", + long_description = long_description, + long_description_content_type="text/markdown", + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + # url="https://github.com" + keywords="rss reader", + python_requires='>=3.8', +) + +#python setup.py sdist bdist_wheel +#(for testing) +#python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* +#python -m pip install --index-url https://test.pypi.org/simple/ --no-deps ilyakhonenko (in venv) +#(for real) +#python -m twine upload dist/* (https://pypi.org by default) +#pip install [your-package] + From 827c70fb4e866d0dcdfd11a0c8303c5a9a7bc8a1 Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Thu, 31 Oct 2019 11:14:00 +0300 Subject: [PATCH 03/15] third iteration in process --- rss/__init__.py | 6 +++--- rss/cache_news.txt | 40 ++++++++++++++++++++++++++++++++++++++++ rss/iter1.py | 21 +++++++++++++++++---- rss/iter3.py | 36 ++++++++++++++++++++++++++++++++++++ rss/parser.log | 40 ++++++++++++++++++++++++++++++++++++++++ rss/rss_parser.py | 8 ++++++-- 6 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 rss/cache_news.txt create mode 100644 rss/iter3.py create mode 100644 rss/parser.log diff --git a/rss/__init__.py b/rss/__init__.py index de736b9..bb12d91 100644 --- a/rss/__init__.py +++ b/rss/__init__.py @@ -1,3 +1,3 @@ -import parser -from iter1 import getEntries -name = "piter" \ No newline at end of file +# import parser +# from iter1 import getEntries +# name = "piter" \ No newline at end of file diff --git a/rss/cache_news.txt b/rss/cache_news.txt new file mode 100644 index 0000000..3cd8afc --- /dev/null +++ b/rss/cache_news.txt @@ -0,0 +1,40 @@ +2019-10-31 +Title: Chilean president cancels Apec and climate summits amid wave of unrest +Description: Sebastián Piñera confirms he will not hold summits in November and December, as government struggles with massive protestsChile’s embattled president has been forced to cancel two major international summits after government concessions failed to defuse weeks of violent protests that have seen thousands of arrests, left at least 20 dead and sent shock waves across Latin America. Related: Chile protesters: 'We are subjugated by the rich. It's time for that to end' Continue reading... +Date: Wed, 30 Oct 2019 16:05:15 GMT +2019-10-31 +Title: Belgian police finds 12 people 'safe and well' in refrigerated lorry +Description: Migrants were found a week after UK police launched murder investigation into death of 39 people in EssexBelgian police say they have found 12 people “safe and well” in a refrigerated lorry, one week after 39 people lost their lives in a similar vehicle that had travelled to Essex via continental Europe.The migrants were discovered in the back of a fruit and vegetable lorry on the motorway, near the Flemish town of Oud-Turnhout, in the early hours of Wednesday. Police said they found 12 adult men: 11 Syrians and one Sudanese citizen. Continue reading... +Date: Wed, 30 Oct 2019 18:23:41 GMT +2019-10-31 +Title: John Bolton reportedly asked to sit for impeachment deposition – live news +Description: House committees leading the impeachment inquiry reportedly intend to call Donald Trump’s former national security adviserSign up for the US briefing and get a new perspective 9.27pm GMT This is Lois Beckett in The Guardian’s West Coast office, taking over our live politics coverage. My colleague Kari Paul will be taking over the coverage shortly.Donald Trump tweeted a fake image of himself awarding a medal of honor to the military dog involved in the raid that killed Isis leader Abu Bakr al-Baghdadi. AMERICAN HERO! pic.twitter.com/XCCa2sGfsZ 9.00pm GMT That’s it from me today. My west coast colleague, Kari Paul, will take over the blog for the next few hours.Here’s where the day stands so far: Continue reading... +Date: Wed, 30 Oct 2019 21:27:30 GMT +2019-10-31 +Title: White House lawyer moved transcript of Trump call to classified server after Ukraine adviser raised alarms - The Washington Post +Date: Thu, 31 Oct 2019 02:50:00 GMT +Description: White House lawyer moved transcript of Trump call to classified server after Ukraine adviser raised alarms  The Washington PostJohn Bolton summoned to testify in impeachment inquiry  ABC NewsWhite House national security official set to testify in impeachment inquiry stepping down soon  CNNWhat Can We Expect from Televised Impeachment Hearings?  The New YorkerNational Security Council aide Tim Morrison will not be part of the Resistance  The Washington PostView full coverage on Google News +2019-10-31 +Title: 'The Wild West': Questions surround Trump legal team payments +Date: Tue, 29 Oct 2019 05:00:53 -0400 +Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. +2019-10-31 +Title: Navy upholds sentencing of Navy SEAL for posing with corpse +Date: Wed, 30 Oct 2019 06:54:52 -0400 +Description: The U.S. chief of naval operations on Tuesday denied a request for clemency and upheld a military jury's sentence that will reduce the rank of a decorated Navy SEAL convicted of posing with a dead Islamic State captive in Iraq in 2017. Adm. Mike Gilday made the decision after carefully reviewing the trial transcripts and the clemency request by the lawyers of Edward Gallagher, said Cmdr. Nate Christensen, spokesman for Gilday, in a statement. Gallagher's lawyer, Timothy Parlatore, said they are disappointed in the ruling that will cost Gallagher up to $200,000 in retirement funds because of his loss of rank from a chief petty officer to a 1st class petty officer. +2019-10-31 +Title: 'The Wild West': Questions surround Trump legal team payments +Date: Tue, 29 Oct 2019 05:00:53 -0400 +Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. +2019-10-31 +Title: 'The Wild West': Questions surround Trump legal team payments +Date: Tue, 29 Oct 2019 05:00:53 -0400 +Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. +2019-10-31 +Title: California Governor Accepted Donations from Utility Company He Now Excoriates for ‘Greed’ +Date: Wed, 30 Oct 2019 09:14:32 -0400 +Description: California governor, Democrat Gavin Newsom, has accepted large donations from Pacific Gas & Electric Co., a utility company he now excoriates for "greed" and "mismanagement."PG&E has faced widespread criticism for implementing blackouts for millions of customers to avoid sparking wildfires in the midst of California's dry and windy fall weather."I have a message for PG&E," Newsom wrote on Twitter on Friday. "Your years and years of greed. Years and years of mismanagement. Years and years of putting shareholders over people. Are OVER."Newsom and allies accepted $208,400 from the utility during his 2018 gubernatorial campaign, according to local affiliate ABC10. Of that total, $150,000 went to a political spending group called “Citizens Supporting Gavin Newsom for Governor 2018,” while the rest went to directly to Newsom's campaign.PG&E filed for bankruptcy in January 2019. Faulty PG&E electricity equipment has been blamed for sparking several wildfires in the past decade.California has consistently shut down proposals to clear dead trees from forests and to trim trees near power lines state wide, creating conditions for a rash of wildfire outbreaks in recent years.The Kincaid Fire currently burning in Sonoma County in the northern part of the state has forced the evacuation of roughly 200,000 people. The fire is twice the size of the city of San Fransisco.Newsom declared a state of emergency on Sunday in response to the Kincaid Fire and several other wildfires throughout the state. He again threatened PG&E in a statement on the situation."There is a plan to get out of this. This is not the new normal,” Newsom said on Sunday at an evacuation center in northern California. “This is not a 10-year process to deal with this. That will not be the case… [PG&E] will be held to account to do something radically different +2019-10-31 +Title: 'The Wild West': Questions surround Trump legal team payments +Date: Tue, 29 Oct 2019 05:00:53 -0400 +Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. diff --git a/rss/iter1.py b/rss/iter1.py index 0b4fb97..de02468 100644 --- a/rss/iter1.py +++ b/rss/iter1.py @@ -3,13 +3,15 @@ import json import re import logging +from datetime import date -url = "https://news.yahoo.com/rss" +# url = "https://news.yahoo.com/rss" # url1 = "https://news.google.com/rss" # url2 = "https://www.theguardian.com/world/rss" logging.basicConfig(format=u'%(levelname)-8s [%(asctime)s] %(message)s', - level=logging.DEBUG, filename=u'parser.log') + level=logging.DEBUG, + handlers=[logging.FileHandler("parser.log", "a", encoding="utf-8")]) def getEntries(url, json, verbose, limit): @@ -23,6 +25,7 @@ def getEntries(url, json, verbose, limit): for item in (channel.entries): if (limit > 0): + cacheNews(item) loggingItems(item) if (json): @@ -38,6 +41,16 @@ def getEntries(url, json, verbose, limit): limit -= 1 +def cacheNews(item): + today = date.today() + d1 = today.strftime("%Y-%m-%d") + with open('cache_news.txt', 'a', encoding="utf-8") as f: + f.write(d1 + '\n' + + "Title: " + item.title + '\n' + + "Date: " + item.published + '\n' + + "Description: " + getDescription(item.description) + '\n') + + def getDescription(item): return BeautifulSoup(item, features="html.parser").getText() @@ -52,7 +65,7 @@ def loggingItems(item): logging.debug("Title: " + str(item.title)) logging.debug("Date: " + str(item.published)) logging.debug("Link: " + str(item.link) + '\n') - logging.debug("Description: ", getDescription(item.description), '\n') + logging.debug("Description: " + getDescription(item.description) + '\n') logging.debug("Links:"+"\n[1]: " + str(item.link) + "(link)\n[2]: " + str(item.media_content[0]['url'])+'\n') @@ -66,4 +79,4 @@ def getJSON(item): 'Description: ': getDescription(item.description) }) -getEntries(url, False, False, 2) \ No newline at end of file +#getEntries(url, False, False, 2) \ No newline at end of file diff --git a/rss/iter3.py b/rss/iter3.py new file mode 100644 index 0000000..c38372f --- /dev/null +++ b/rss/iter3.py @@ -0,0 +1,36 @@ + +class NotFound(Exception): + pass + +def getCache(argDate): + argDate = argDate + '\n' + + f = open('cache_news.txt', encoding='utf-8') + lines = f.readlines() + + i = 0 + while (i < len(lines)): + count = 0 + line = lines[i].replace('-', '') + + if(line == argDate): + count += 1 + print(lines[i+1], '\n', lines[i+2], '\n', lines[i+3]) + i = i + 4 + + if (count == 0): + raise NotFound + + f.close() + + + + +# f = open('parser.log') +# lines = f.readlines() +# for line in lines: +# logDate = line.split()[1] +# logDate = logDate.replace('-', '') + +# if (logDate == argDate): +# print(line) \ No newline at end of file diff --git a/rss/parser.log b/rss/parser.log new file mode 100644 index 0000000..5c4e922 --- /dev/null +++ b/rss/parser.log @@ -0,0 +1,40 @@ +DEBUG [2019-10-31 11:05:23,886] Title: 'The Wild West': Questions surround Trump legal team payments +DEBUG [2019-10-31 11:05:23,887] Date: Tue, 29 Oct 2019 05:00:53 -0400 +DEBUG [2019-10-31 11:05:23,887] Link: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html + +DEBUG [2019-10-31 11:05:23,889] Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. + +DEBUG [2019-10-31 11:05:23,890] Links: +[1]: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/w4.pQJyCxVBIimzQm6W8Tg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/c5063d90-f9b8-11e9-bde9-1758e063f7fe + +DEBUG [2019-10-31 11:05:41,873] Title: 'The Wild West': Questions surround Trump legal team payments +DEBUG [2019-10-31 11:05:41,874] Date: Tue, 29 Oct 2019 05:00:53 -0400 +DEBUG [2019-10-31 11:05:41,875] Link: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html + +DEBUG [2019-10-31 11:05:41,876] Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. + +DEBUG [2019-10-31 11:05:41,877] Links: +[1]: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/w4.pQJyCxVBIimzQm6W8Tg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/c5063d90-f9b8-11e9-bde9-1758e063f7fe + +DEBUG [2019-10-31 11:05:41,892] Title: California Governor Accepted Donations from Utility Company He Now Excoriates for ‘Greed’ +DEBUG [2019-10-31 11:05:41,893] Date: Wed, 30 Oct 2019 09:14:32 -0400 +DEBUG [2019-10-31 11:05:41,894] Link: https://news.yahoo.com/california-governor-accepted-donations-utility-131432537.html + +DEBUG [2019-10-31 11:05:41,895] Description: California governor, Democrat Gavin Newsom, has accepted large donations from Pacific Gas & Electric Co., a utility company he now excoriates for "greed" and "mismanagement."PG&E has faced widespread criticism for implementing blackouts for millions of customers to avoid sparking wildfires in the midst of California's dry and windy fall weather."I have a message for PG&E," Newsom wrote on Twitter on Friday. "Your years and years of greed. Years and years of mismanagement. Years and years of putting shareholders over people. Are OVER."Newsom and allies accepted $208,400 from the utility during his 2018 gubernatorial campaign, according to local affiliate ABC10. Of that total, $150,000 went to a political spending group called “Citizens Supporting Gavin Newsom for Governor 2018,” while the rest went to directly to Newsom's campaign.PG&E filed for bankruptcy in January 2019. Faulty PG&E electricity equipment has been blamed for sparking several wildfires in the past decade.California has consistently shut down proposals to clear dead trees from forests and to trim trees near power lines state wide, creating conditions for a rash of wildfire outbreaks in recent years.The Kincaid Fire currently burning in Sonoma County in the northern part of the state has forced the evacuation of roughly 200,000 people. The fire is twice the size of the city of San Fransisco.Newsom declared a state of emergency on Sunday in response to the Kincaid Fire and several other wildfires throughout the state. He again threatened PG&E in a statement on the situation."There is a plan to get out of this. This is not the new normal,” Newsom said on Sunday at an evacuation center in northern California. “This is not a 10-year process to deal with this. That will not be the case… [PG&E] will be held to account to do something radically different + +DEBUG [2019-10-31 11:05:41,896] Links: +[1]: https://news.yahoo.com/california-governor-accepted-donations-utility-131432537.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/PwVfYG7dQige5oYEYTjwrQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/the_national_review_738/7819ecc3bf346550c726f66c60681b59 + +DEBUG [2019-10-31 11:06:19,913] Title: 'The Wild West': Questions surround Trump legal team payments +DEBUG [2019-10-31 11:06:19,914] Date: Tue, 29 Oct 2019 05:00:53 -0400 +DEBUG [2019-10-31 11:06:19,915] Link: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html + +DEBUG [2019-10-31 11:06:19,916] Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. + +DEBUG [2019-10-31 11:06:19,917] Links: +[1]: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/w4.pQJyCxVBIimzQm6W8Tg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/c5063d90-f9b8-11e9-bde9-1758e063f7fe + diff --git a/rss/rss_parser.py b/rss/rss_parser.py index 7140cd8..42f86a8 100644 --- a/rss/rss_parser.py +++ b/rss/rss_parser.py @@ -1,5 +1,6 @@ import argparse import iter1 +import iter3 parser = argparse.ArgumentParser(description='Pure Python command-line RSS reader') @@ -8,7 +9,7 @@ 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', action='store', help='Limit news topics if this parameter provided') - +parser.add_argument('--date', action='store', help='Print news from the specified day') args = parser.parse_args() @@ -20,8 +21,11 @@ elif(args.verbose): iter1.printLogs() +elif(args.date): + iter3.getCache(args.date) + else: if not(args.limit): args.limit = 0 - iter1.getEntries(url, args.json, args.verbose, int(args.limit)) + iter1.getEntries(args.source, args.json, args.verbose, int(args.limit)) From 4d7ae3bba53b658f0ee3d5b4b0c9af4e64aa982d Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Sat, 2 Nov 2019 01:27:35 +0300 Subject: [PATCH 04/15] made format convertion --- rss/cache_news.txt | 40 ------------ rss/convert.py | 67 ++++++++++++++++++++ rss/{iter3.py => get_cache.py} | 6 +- rss/images/19.jpg | Bin 0 -> 3274 bytes rss/images/68.jpg | Bin 0 -> 8300 bytes rss/images/88.jpg | Bin 0 -> 20179 bytes rss/iter1.py | 82 ------------------------- rss/iter4.py | 47 ++++++++++++++ rss/main.py | 47 ++++++++++++++ rss/parser.log | 40 ------------ rss/pdfs/19.pdf | Bin 0 -> 40989 bytes rss/rss_parser.py | 108 ++++++++++++++++++++++++++------- rss/test.css | 29 +++++++++ 13 files changed, 279 insertions(+), 187 deletions(-) create mode 100644 rss/convert.py rename rss/{iter3.py => get_cache.py} (90%) create mode 100644 rss/images/19.jpg create mode 100644 rss/images/68.jpg create mode 100644 rss/images/88.jpg delete mode 100644 rss/iter1.py create mode 100644 rss/iter4.py create mode 100644 rss/main.py create mode 100644 rss/pdfs/19.pdf create mode 100644 rss/test.css diff --git a/rss/cache_news.txt b/rss/cache_news.txt index 3cd8afc..e69de29 100644 --- a/rss/cache_news.txt +++ b/rss/cache_news.txt @@ -1,40 +0,0 @@ -2019-10-31 -Title: Chilean president cancels Apec and climate summits amid wave of unrest -Description: Sebastián Piñera confirms he will not hold summits in November and December, as government struggles with massive protestsChile’s embattled president has been forced to cancel two major international summits after government concessions failed to defuse weeks of violent protests that have seen thousands of arrests, left at least 20 dead and sent shock waves across Latin America. Related: Chile protesters: 'We are subjugated by the rich. It's time for that to end' Continue reading... -Date: Wed, 30 Oct 2019 16:05:15 GMT -2019-10-31 -Title: Belgian police finds 12 people 'safe and well' in refrigerated lorry -Description: Migrants were found a week after UK police launched murder investigation into death of 39 people in EssexBelgian police say they have found 12 people “safe and well” in a refrigerated lorry, one week after 39 people lost their lives in a similar vehicle that had travelled to Essex via continental Europe.The migrants were discovered in the back of a fruit and vegetable lorry on the motorway, near the Flemish town of Oud-Turnhout, in the early hours of Wednesday. Police said they found 12 adult men: 11 Syrians and one Sudanese citizen. Continue reading... -Date: Wed, 30 Oct 2019 18:23:41 GMT -2019-10-31 -Title: John Bolton reportedly asked to sit for impeachment deposition – live news -Description: House committees leading the impeachment inquiry reportedly intend to call Donald Trump’s former national security adviserSign up for the US briefing and get a new perspective 9.27pm GMT This is Lois Beckett in The Guardian’s West Coast office, taking over our live politics coverage. My colleague Kari Paul will be taking over the coverage shortly.Donald Trump tweeted a fake image of himself awarding a medal of honor to the military dog involved in the raid that killed Isis leader Abu Bakr al-Baghdadi. AMERICAN HERO! pic.twitter.com/XCCa2sGfsZ 9.00pm GMT That’s it from me today. My west coast colleague, Kari Paul, will take over the blog for the next few hours.Here’s where the day stands so far: Continue reading... -Date: Wed, 30 Oct 2019 21:27:30 GMT -2019-10-31 -Title: White House lawyer moved transcript of Trump call to classified server after Ukraine adviser raised alarms - The Washington Post -Date: Thu, 31 Oct 2019 02:50:00 GMT -Description: White House lawyer moved transcript of Trump call to classified server after Ukraine adviser raised alarms  The Washington PostJohn Bolton summoned to testify in impeachment inquiry  ABC NewsWhite House national security official set to testify in impeachment inquiry stepping down soon  CNNWhat Can We Expect from Televised Impeachment Hearings?  The New YorkerNational Security Council aide Tim Morrison will not be part of the Resistance  The Washington PostView full coverage on Google News -2019-10-31 -Title: 'The Wild West': Questions surround Trump legal team payments -Date: Tue, 29 Oct 2019 05:00:53 -0400 -Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. -2019-10-31 -Title: Navy upholds sentencing of Navy SEAL for posing with corpse -Date: Wed, 30 Oct 2019 06:54:52 -0400 -Description: The U.S. chief of naval operations on Tuesday denied a request for clemency and upheld a military jury's sentence that will reduce the rank of a decorated Navy SEAL convicted of posing with a dead Islamic State captive in Iraq in 2017. Adm. Mike Gilday made the decision after carefully reviewing the trial transcripts and the clemency request by the lawyers of Edward Gallagher, said Cmdr. Nate Christensen, spokesman for Gilday, in a statement. Gallagher's lawyer, Timothy Parlatore, said they are disappointed in the ruling that will cost Gallagher up to $200,000 in retirement funds because of his loss of rank from a chief petty officer to a 1st class petty officer. -2019-10-31 -Title: 'The Wild West': Questions surround Trump legal team payments -Date: Tue, 29 Oct 2019 05:00:53 -0400 -Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. -2019-10-31 -Title: 'The Wild West': Questions surround Trump legal team payments -Date: Tue, 29 Oct 2019 05:00:53 -0400 -Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. -2019-10-31 -Title: California Governor Accepted Donations from Utility Company He Now Excoriates for ‘Greed’ -Date: Wed, 30 Oct 2019 09:14:32 -0400 -Description: California governor, Democrat Gavin Newsom, has accepted large donations from Pacific Gas & Electric Co., a utility company he now excoriates for "greed" and "mismanagement."PG&E has faced widespread criticism for implementing blackouts for millions of customers to avoid sparking wildfires in the midst of California's dry and windy fall weather."I have a message for PG&E," Newsom wrote on Twitter on Friday. "Your years and years of greed. Years and years of mismanagement. Years and years of putting shareholders over people. Are OVER."Newsom and allies accepted $208,400 from the utility during his 2018 gubernatorial campaign, according to local affiliate ABC10. Of that total, $150,000 went to a political spending group called “Citizens Supporting Gavin Newsom for Governor 2018,” while the rest went to directly to Newsom's campaign.PG&E filed for bankruptcy in January 2019. Faulty PG&E electricity equipment has been blamed for sparking several wildfires in the past decade.California has consistently shut down proposals to clear dead trees from forests and to trim trees near power lines state wide, creating conditions for a rash of wildfire outbreaks in recent years.The Kincaid Fire currently burning in Sonoma County in the northern part of the state has forced the evacuation of roughly 200,000 people. The fire is twice the size of the city of San Fransisco.Newsom declared a state of emergency on Sunday in response to the Kincaid Fire and several other wildfires throughout the state. He again threatened PG&E in a statement on the situation."There is a plan to get out of this. This is not the new normal,” Newsom said on Sunday at an evacuation center in northern California. “This is not a 10-year process to deal with this. That will not be the case… [PG&E] will be held to account to do something radically different -2019-10-31 -Title: 'The Wild West': Questions surround Trump legal team payments -Date: Tue, 29 Oct 2019 05:00:53 -0400 -Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. diff --git a/rss/convert.py b/rss/convert.py new file mode 100644 index 0000000..562ab7a --- /dev/null +++ b/rss/convert.py @@ -0,0 +1,67 @@ +import urllib.request +import random +import feedparser +import os +from bs4 import BeautifulSoup +import dominate +from dominate.tags import * +import pdfkit +import webbrowser + + +def getDescription(item): + return BeautifulSoup(item, features="html.parser").getText() + + +def checkMediaContent(item): + media_content = '\n' + if(item.has_key('media_content')): + media_content = item.media_content[0]['url'] + return media_content + + +def makeConvertion(url, html_path, pdf_path, limit): + + channel = feedparser.parse(url) + doc = dominate.document(title="HTML document") + + index = 0 + + while (index < limit): + item = channel.entries[index] + + media = checkMediaContent(item) + + rand = random.randint(1, 120) + if not os.path.exists('images/'): + os.makedirs('images/') + filename_ = 'images/' + str(rand) + '.jpg' + urllib.request.urlretrieve(media, filename_) + + with doc: + with div(): + h2(item.title) + p("Date: " + item.published) + img(src=os.path.abspath(filename_)) + p("Description: " + getDescription(item.description)) + index += 1 + + if(html_path): + with open('test.css') as file: + css = file.read() + + with doc.head: + style(css) + + if not os.path.exists(html_path): + os.makedirs(html_path) + html_file = html_path + str(rand) + '.html' + with open(html_file, 'w') as f: + f.write(str(doc)) + webbrowser.open(html_file, new=2) + + elif(pdf_path): + if not os.path.exists(pdf_path): + os.makedirs(pdf_path) + pdf_file = pdf_path + str(rand) + '.pdf' + pdfkit.from_string(str(doc), pdf_file) diff --git a/rss/iter3.py b/rss/get_cache.py similarity index 90% rename from rss/iter3.py rename to rss/get_cache.py index c38372f..8691da5 100644 --- a/rss/iter3.py +++ b/rss/get_cache.py @@ -16,10 +16,10 @@ def getCache(argDate): if(line == argDate): count += 1 print(lines[i+1], '\n', lines[i+2], '\n', lines[i+3]) - i = i + 4 + i = i + 5 - if (count == 0): - raise NotFound + # if (count == 0): + # raise NotFound f.close() diff --git a/rss/images/19.jpg b/rss/images/19.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9ef621810a08b0d49a55b71e613d30136f938080 GIT binary patch literal 3274 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 literal 0 HcmV?d00001 diff --git a/rss/images/88.jpg b/rss/images/88.jpg new file mode 100644 index 0000000000000000000000000000000000000000..940be42e35615114d2e6878519a65d5d4c4ee7b9 GIT binary patch literal 20179 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 D$<%l& literal 0 HcmV?d00001 diff --git a/rss/iter1.py b/rss/iter1.py deleted file mode 100644 index de02468..0000000 --- a/rss/iter1.py +++ /dev/null @@ -1,82 +0,0 @@ -from bs4 import BeautifulSoup -import feedparser -import json -import re -import logging -from datetime import date - -# url = "https://news.yahoo.com/rss" -# url1 = "https://news.google.com/rss" -# url2 = "https://www.theguardian.com/world/rss" - -logging.basicConfig(format=u'%(levelname)-8s [%(asctime)s] %(message)s', - level=logging.DEBUG, - handlers=[logging.FileHandler("parser.log", "a", encoding="utf-8")]) - - -def getEntries(url, json, verbose, limit): - - channel = feedparser.parse(url) - - print("Feed: ", channel.feed.title, '\n') - - limit = limit or len(channel.entries) - - for item in (channel.entries): - - if (limit > 0): - cacheNews(item) - loggingItems(item) - - if (json): - print(getJSON(item)) - - else: - print("Title: ", item.title) - print("Date: ", item.published) - print("Link: ", item.link, '\n') - print("Description: ", getDescription(item.description), '\n') - print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", - item.media_content[0]['url'], '\n') - limit -= 1 - - -def cacheNews(item): - today = date.today() - d1 = today.strftime("%Y-%m-%d") - with open('cache_news.txt', 'a', encoding="utf-8") as f: - f.write(d1 + '\n' + - "Title: " + item.title + '\n' + - "Date: " + item.published + '\n' + - "Description: " + getDescription(item.description) + '\n') - - -def getDescription(item): - return BeautifulSoup(item, features="html.parser").getText() - - -def printLogs(): - with open('parser.log', 'r') as f: - for line in f: - print(line) - - -def loggingItems(item): - logging.debug("Title: " + str(item.title)) - logging.debug("Date: " + str(item.published)) - logging.debug("Link: " + str(item.link) + '\n') - logging.debug("Description: " + getDescription(item.description) + '\n') - logging.debug("Links:"+"\n[1]: " + str(item.link) + - "(link)\n[2]: " + str(item.media_content[0]['url'])+'\n') - - -def getJSON(item): - - return json.dumps({ - 'Title: ': item.title, - 'Date: ': item.published, - 'Link: ': item.link, - 'Description: ': getDescription(item.description) - }) - -#getEntries(url, False, False, 2) \ No newline at end of file diff --git a/rss/iter4.py b/rss/iter4.py new file mode 100644 index 0000000..da41518 --- /dev/null +++ b/rss/iter4.py @@ -0,0 +1,47 @@ +# pdf = FPDF() +# pdf.add_page() + +# def toPdf(item, pdf_path, media): + +# pdf.set_font('Arial', 'B', 16) +# title = item.title +# print(title) +# pdf.cell(40, 10, ln=1, txt=title) +# pdf.set_font('Arial', size=14) +# published = item.published +# pdf.cell(10, 10, ln=2, txt=published) +# description = getDescription(item.description) +# pdf.cell(10, 10, ln=3, txt=description) + +# rand = random.randint(1, 120) +# if not os.path.exists('images/'): +# os.makedirs('images/') +# filename_ = 'images/' + str(rand) + '.jpg' +# urllib.request.urlretrieve(media, filename_) + +# pdf.image(filename_) + +# # name_ = item.title.split()[1] +# if not os.path.exists(pdf_path): +# os.makedirs(pdf_path) +# outfile = pdf_path + 'news' + '.pdf' +# pdf.output(r'outfile', 'F') +# import dominate +# from dominate.tags import * + + +# doc = dominate.document(title='HTML document') + +# with doc: +# with div(): +# h2("A Louisiana woman has been arrested for selling $20 fake doctor&#39;s notes to students trying to skip class") +# p("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") +# # img(src=os.path.abspath(filename_)) + + +# with open('test.html', 'w') as f: +# f.write(str(doc)) + +import os + +print(os.getcwd()) \ No newline at end of file diff --git a/rss/main.py b/rss/main.py new file mode 100644 index 0000000..33302a0 --- /dev/null +++ b/rss/main.py @@ -0,0 +1,47 @@ +import argparse +import rss_parser +import get_cache +import convert + +parser = argparse.ArgumentParser(description='Pure Python command-line RSS reader') + +parser.add_argument('source', action='store', help='RSS URL') +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('--limit', action='store', help='Limit news topics') +parser.add_argument('--date', action='store', help='Print news from the specified day') +parser.add_argument('--tohtml', action='store', help='Convert news in html format') +parser.add_argument('--topdf', action='store', help='Convert news in pdf format') + + +args = parser.parse_args() + + +if (args.version): + version = 1.0 + print(version) + +elif(args.verbose): + rss_parser.printLogs() + +elif(args.date): + get_cache.getCache(args.date) + +elif(args.tohtml or args.topdf): + + if(args.tohtml): + args.topdf = False + elif(args.topdf): + args.tohtml = False + if not(args.limit): + args.limit = 0 + + convert.makeConvertion(args.source, args.tohtml, args.topdf, int(args.limit)) + +else: + if not(args.limit): + args.limit = 0 + + rss_parser.getEntries(args.source, args.json, args.verbose, int(args.limit)) + diff --git a/rss/parser.log b/rss/parser.log index 5c4e922..e69de29 100644 --- a/rss/parser.log +++ b/rss/parser.log @@ -1,40 +0,0 @@ -DEBUG [2019-10-31 11:05:23,886] Title: 'The Wild West': Questions surround Trump legal team payments -DEBUG [2019-10-31 11:05:23,887] Date: Tue, 29 Oct 2019 05:00:53 -0400 -DEBUG [2019-10-31 11:05:23,887] Link: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html - -DEBUG [2019-10-31 11:05:23,889] Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. - -DEBUG [2019-10-31 11:05:23,890] Links: -[1]: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/w4.pQJyCxVBIimzQm6W8Tg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/c5063d90-f9b8-11e9-bde9-1758e063f7fe - -DEBUG [2019-10-31 11:05:41,873] Title: 'The Wild West': Questions surround Trump legal team payments -DEBUG [2019-10-31 11:05:41,874] Date: Tue, 29 Oct 2019 05:00:53 -0400 -DEBUG [2019-10-31 11:05:41,875] Link: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html - -DEBUG [2019-10-31 11:05:41,876] Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. - -DEBUG [2019-10-31 11:05:41,877] Links: -[1]: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/w4.pQJyCxVBIimzQm6W8Tg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/c5063d90-f9b8-11e9-bde9-1758e063f7fe - -DEBUG [2019-10-31 11:05:41,892] Title: California Governor Accepted Donations from Utility Company He Now Excoriates for ‘Greed’ -DEBUG [2019-10-31 11:05:41,893] Date: Wed, 30 Oct 2019 09:14:32 -0400 -DEBUG [2019-10-31 11:05:41,894] Link: https://news.yahoo.com/california-governor-accepted-donations-utility-131432537.html - -DEBUG [2019-10-31 11:05:41,895] Description: California governor, Democrat Gavin Newsom, has accepted large donations from Pacific Gas & Electric Co., a utility company he now excoriates for "greed" and "mismanagement."PG&E has faced widespread criticism for implementing blackouts for millions of customers to avoid sparking wildfires in the midst of California's dry and windy fall weather."I have a message for PG&E," Newsom wrote on Twitter on Friday. "Your years and years of greed. Years and years of mismanagement. Years and years of putting shareholders over people. Are OVER."Newsom and allies accepted $208,400 from the utility during his 2018 gubernatorial campaign, according to local affiliate ABC10. Of that total, $150,000 went to a political spending group called “Citizens Supporting Gavin Newsom for Governor 2018,” while the rest went to directly to Newsom's campaign.PG&E filed for bankruptcy in January 2019. Faulty PG&E electricity equipment has been blamed for sparking several wildfires in the past decade.California has consistently shut down proposals to clear dead trees from forests and to trim trees near power lines state wide, creating conditions for a rash of wildfire outbreaks in recent years.The Kincaid Fire currently burning in Sonoma County in the northern part of the state has forced the evacuation of roughly 200,000 people. The fire is twice the size of the city of San Fransisco.Newsom declared a state of emergency on Sunday in response to the Kincaid Fire and several other wildfires throughout the state. He again threatened PG&E in a statement on the situation."There is a plan to get out of this. This is not the new normal,” Newsom said on Sunday at an evacuation center in northern California. “This is not a 10-year process to deal with this. That will not be the case… [PG&E] will be held to account to do something radically different - -DEBUG [2019-10-31 11:05:41,896] Links: -[1]: https://news.yahoo.com/california-governor-accepted-donations-utility-131432537.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/PwVfYG7dQige5oYEYTjwrQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/the_national_review_738/7819ecc3bf346550c726f66c60681b59 - -DEBUG [2019-10-31 11:06:19,913] Title: 'The Wild West': Questions surround Trump legal team payments -DEBUG [2019-10-31 11:06:19,914] Date: Tue, 29 Oct 2019 05:00:53 -0400 -DEBUG [2019-10-31 11:06:19,915] Link: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html - -DEBUG [2019-10-31 11:06:19,916] Description: Until now, Trump’s legal arrangements have been in a gray area of regulation. But now, critics are trying to change that. - -DEBUG [2019-10-31 11:06:19,917] Links: -[1]: https://news.yahoo.com/the-wild-west-questions-surround-trump-legal-team-payments-090050118.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/w4.pQJyCxVBIimzQm6W8Tg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/c5063d90-f9b8-11e9-bde9-1758e063f7fe - diff --git a/rss/pdfs/19.pdf b/rss/pdfs/19.pdf new file mode 100644 index 0000000000000000000000000000000000000000..940cabe1eb56538b14198ac1afd799598fd6efba GIT binary patch literal 40989 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 0): + cacheNews(item) + loggingItems(item) + + if (json): + print(getJSON(item)) + + else: + print("Title: ", item.title) + print("Date: ", item.published) + print("Link: ", item.link, '\n') + print("Description: ", getDescription(item.description), '\n') + print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", + checkMediaContent(item), '\n') + else: + break + limit -= 1 + + + +def checkMediaContent(item): + media_content = '\n' + if(item.has_key('media_content')): + media_content = '\n' + item.media_content[0]['url'] + '\n' + return media_content + + +def cacheNews(item): + today = date.today() + d1 = today.strftime("%Y-%m-%d") + + with open('cache_news.txt', 'a', encoding="utf-8") as f: + f.write(d1 + '\n' + + "Title: " + item.title + '\n' + + "Link: " + item.link + '\n' + + "Description: " + getDescription(item.description) + + checkMediaContent(item) + '\n') + + +def loggingItems(item): + logging.debug("Title: " + str(item.title)) + logging.debug("Date: " + str(item.published)) + logging.debug("Link: " + str(item.link) + '\n') + logging.debug("Description: " + getDescription(item.description) + '\n') + logging.debug("Links:"+"\n[1]: " + str(item.link) + + "(link)\n[2]: " + str(checkMediaContent(item))+'\n') + + +def getJSON(item): + return json.dumps({ + 'Title: ': item.title, + 'Date: ': item.published, + 'Link: ': item.link, + 'Description: ': getDescription(item.description) + }) + + +def getDescription(item): + return BeautifulSoup(item, features="html.parser").getText() + + +def printLogs(): + with open('parser.log', 'r') as f: + for line in f: + print(line) -elif(args.date): - iter3.getCache(args.date) -else: - if not(args.limit): - args.limit = 0 - iter1.getEntries(args.source, args.json, args.verbose, int(args.limit)) diff --git a/rss/test.css b/rss/test.css new file mode 100644 index 0000000..6307dca --- /dev/null +++ b/rss/test.css @@ -0,0 +1,29 @@ +div { + border: 2px solid black; + border-radius: 10px; + padding: 2px 20px 20px 20px; + margin: auto; + box-sizing: border-box; + + background-color: rgb(20, 166, 192); + + width: 700px; + height: 300px; + + display: flex; + flex-direction: column; + overflow-y: auto; +} +div h3{ + font-family: Courier, monospace; +} +div p { + font-family: Sans MS, Comic Sans, cursive; +} +body { + background-color: black; +} +div img{ + width: 100px; + height: 300px; +} \ No newline at end of file From cc8c3c5c2d2f457341f71fd2bffc9c01c2a99f32 Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Sun, 3 Nov 2019 17:54:52 +0300 Subject: [PATCH 05/15] write docstrings. Update convertion function --- .gitignore | 5 +- rss/cache_news.txt | 44 +++++++++++++ rss/convert.py | 118 ++++++++++++++++++++++------------ rss/get_cache.py | 35 +++++------ rss/images/19.jpg | Bin 3274 -> 0 bytes rss/images/68.jpg | Bin 8300 -> 0 bytes rss/images/88.jpg | Bin 20179 -> 0 bytes rss/iter4.py | 47 -------------- rss/main.py | 11 +++- rss/parser.log | 122 ++++++++++++++++++++++++++++++++++++ rss/pdfs/19.pdf | Bin 40989 -> 0 bytes rss/rss_parser.py | 64 ++++++++++--------- rss/{test.css => style.css} | 0 13 files changed, 307 insertions(+), 139 deletions(-) delete mode 100644 rss/images/19.jpg delete mode 100644 rss/images/68.jpg delete mode 100644 rss/images/88.jpg delete mode 100644 rss/iter4.py delete mode 100644 rss/pdfs/19.pdf rename rss/{test.css => style.css} (100%) diff --git a/.gitignore b/.gitignore index b96a62b..2c09a51 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ __pycache__/ .vscode/ -test.py \ No newline at end of file +test.py +rss/__pycache__/ +rss/.vscode/ + diff --git a/rss/cache_news.txt b/rss/cache_news.txt index e69de29..645c181 100644 --- a/rss/cache_news.txt +++ b/rss/cache_news.txt @@ -0,0 +1,44 @@ +2019-11-02 +Title: Greta Thunberg says meeting with Trump 'would be a waste of time' +Link: https://news.yahoo.com/greta-thunberg-trump-meeting-waste-of-time-ellen-show-145843540.html +Description: The Swedish teenage climate activist says she wouldn’t want to meet with the president even if given the opportunity. +2019-11-02 +Title: Hedge fund billionaire fires back at Warren: 'Your vilification of the rich is misguided' +Link: https://news.yahoo.com/hedge-fund-billionaire-fires-back-at-warren-your-vilification-of-the-rich-is-misguided-223828623.html +Description: Hedge fund mogul Leon Cooperman takes issue with Sen. Warren's plans for a wealth tax. +2019-11-02 +Title: 2 bodies found at Texas beach are New Hampshire couple +Link: https://news.yahoo.com/2-bodies-found-texas-beach-123706918.html +Description: The two bodies found buried at a South Texas beach have been identified as a missing New Hampshire couple, investigators announced Friday. The deaths of James Butler, 48, and Michelle Butler, 46, are being investigated as homicides, the Kleberg County Sheriff's Office in Texas said in a release. A deputy on Sunday located a woman's remains in a shallow grave on Padre Island, near Corpus Christi, the sheriff's office said. +2019-11-03 +Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September +Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html +Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. +2019-11-03 +Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa +Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html +Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? +2019-11-03 +Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September +Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html +Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. +2019-11-03 +Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa +Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html +Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? +2019-11-03 +Title: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL - ABC News +Link: https://news.google.com/__i/rss/rd/articles/CBMiYGh0dHBzOi8vYWJjbmV3cy5nby5jb20vUG9saXRpY3MvYmlkZW4td2FycmVuLXNhbmRlcnMtc3RheS10b3AtaGVhbHRoLW5vdy1pc3N1ZS9zdG9yeT9pZD02NjY2NDc3MdIBAA?oc=5 +Description: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL  ABC News7 takeaways from Iowa Democrats' biggest night of the year  CNNDemocrats speak to Iowa voters  CBS This MorningAre There Any Cheerful Democrats Left? Yes, in Iowa.  BloombergAt critical Iowa dinner, Buttigieg and Harris deliver a punch  The Boston GlobeView full coverage on Google News +2019-11-03 +Title: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm - USA TODAY +Link: https://news.google.com/__i/rss/rd/articles/CBMigQFodHRwczovL3d3dy51c2F0b2RheS5jb20vc3RvcnkvbmV3cy93b3JsZC8yMDE5LzExLzAyL2lyb24tc2Nvdy1uaWFnYXJhLWZhbGxzLWRpc2xvZGdlZC1zZXZlcmUtd2VhdGhlci1hZnRlci0xMDEteWVhcnMvNDE0NDEwNDAwMi_SASdodHRwczovL2FtcC51c2F0b2RheS5jb20vYW1wLzQxNDQxMDQwMDI?oc=5 +Description: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm  USA TODAYWind and rain dislodge boat trapped on rocks above Niagara Falls for 101 years  CNNHistoric iron scow moves for first time in 101 years, closer to Niagara Falls ledge  Fox NewsNiagara Falls' iron scow moves for first time in 101 years, moving it closer to Niagara Falls ledge  Daily MailView full coverage on Google News +2019-11-03 +Title: Johnson ‘knew about Vote Leave’s illegal overspend’, says MP +Link: https://www.theguardian.com/politics/2019/nov/02/boris-johnson-vote-leave-overspend-eu-referendum-labour-mp-ian-lucas +Description: Prime minister accused of ‘sitting on information’ after EU referendumBoris Johnson knew of Vote Leave’s overspend during the 2016 EU referendum, but appears to have failed to tell the authorities, according to explosive new claims from a senior MP. The payment was subsequently ruled to be illegal.Ian Lucas revealed that he has seen correspondence obtained during the parliamentary inquiry into disinformation and democracy which showed that Johnson’s most senior aide, Dominic Cummings, told the Electoral Commission that the prime minister, and his cabinet colleague Michael Gove, knew of the overspend by the pro-Brexit organisation. Continue reading... +2019-11-03 +Title: Airbnb to ban party houses in wake of Halloween shooting in California +Link: https://www.theguardian.com/us-news/2019/nov/03/airbnb-to-ban-party-houses-in-wake-of-halloween-shooting-in-california +Description: Airbnb’s CEO said the company ‘must do better’ after five people were killed at a party in San FranciscoAirbnb’s chief executive has said the company will ban “party houses” following a deadly shooting at a Halloween party held at an Airbnb rental home in California.In a series of tweets, Brian Chesky said on Saturday that the San Francisco-based company would expand manual screening of “high risk” reservations and remove guests who fail to comply with policies banning parties at Airbnb rental homes. Continue reading... diff --git a/rss/convert.py b/rss/convert.py index 562ab7a..a103f14 100644 --- a/rss/convert.py +++ b/rss/convert.py @@ -6,62 +6,100 @@ import dominate from dominate.tags import * import pdfkit -import webbrowser - - -def getDescription(item): - return BeautifulSoup(item, features="html.parser").getText() - - -def checkMediaContent(item): - media_content = '\n' - if(item.has_key('media_content')): - media_content = item.media_content[0]['url'] - return media_content +import webbrowser +# make convertion of news into pdf or html def makeConvertion(url, html_path, pdf_path, limit): - + ''' + 1. create object of feedparser library + 2. create object of dominate library for creating html files + 3. on each iteration + 3.1 check if there is media content + 3.2 write html tags + 3.3 convert format of file + ''' channel = feedparser.parse(url) doc = dominate.document(title="HTML document") - index = 0 + for index, item in enumerate(channel.entries): + if(index == limit): + break - while (index < limit): - item = channel.entries[index] - - media = checkMediaContent(item) - - rand = random.randint(1, 120) - if not os.path.exists('images/'): - os.makedirs('images/') - filename_ = 'images/' + str(rand) + '.jpg' - urllib.request.urlretrieve(media, filename_) + random_name = random.randint(1, 120) + media_file_name = checkMediaContent(item, random_name) with doc: with div(): h2(item.title) p("Date: " + item.published) - img(src=os.path.abspath(filename_)) + if (media_file_name): + img(src=os.path.abspath(media_file_name)) p("Description: " + getDescription(item.description)) - index += 1 + if(html_path): - with open('test.css') as file: - css = file.read() + intoHTML(html_path, doc, random_name) - with doc.head: - style(css) + elif(pdf_path): + intoPDF(pdf_path, doc, random_name) - if not os.path.exists(html_path): - os.makedirs(html_path) - html_file = html_path + str(rand) + '.html' - with open(html_file, 'w') as f: - f.write(str(doc)) - webbrowser.open(html_file, new=2) - elif(pdf_path): - if not os.path.exists(pdf_path): +# return description of news without html tags +def getDescription(item): + return BeautifulSoup(item, features="html.parser").getText() + + +def checkMediaContent(item, random_name): + ''' + 1. create folder with images and return name of image + if there is media content in news + 2. or return nothing + ''' + media_content = '' + if('media_content' in item.keys()): + media_content = item.media_content[0]['url'] + + if not os.path.exists('images/'): + os.makedirs('images/') + filename_ = 'images/' + str(random_name) + '.jpg' + urllib.request.urlretrieve(media, filename_) + return filename_ + + return media_content + + +def intoHTML(html_path, doc, random_name): + ''' + 1. read file with styles for html + 2. write styles in html + 3. create folder for html file if not exists + 4. write html file + 5. open browser with html + ''' + with open('style.css') as file: + css_file = file.read() + + with doc.head: + style(css_file) + + if not os.path.exists(html_path): + os.makedirs(html_path) + html_file = html_path + str(random_name) + '.html' + + with open(html_file, 'w') as f: + f.write(str(doc)) + + webbrowser.open(html_file, new=2) + + +def intoPDF(pdf_path, doc, random_name): + ''' + 1. create folder for pdf files if not exists + 2. write pdf file + ''' + if not os.path.exists(pdf_path): os.makedirs(pdf_path) - pdf_file = pdf_path + str(rand) + '.pdf' - pdfkit.from_string(str(doc), pdf_file) + pdf_file = pdf_path + str(random_name) + '.pdf' + + pdfkit.from_string(str(doc), pdf_file) \ No newline at end of file diff --git a/rss/get_cache.py b/rss/get_cache.py index 8691da5..6d9ab5d 100644 --- a/rss/get_cache.py +++ b/rss/get_cache.py @@ -1,9 +1,15 @@ - class NotFound(Exception): pass -def getCache(argDate): - argDate = argDate + '\n' + +# print cached news +def getCache(arg_date): + ''' + 1. open and read file with news + 2. print news if their date is equal to the date in argument + 3. or raise error + ''' + arg_date = arg_date + '\n' f = open('cache_news.txt', encoding='utf-8') lines = f.readlines() @@ -13,24 +19,13 @@ def getCache(argDate): count = 0 line = lines[i].replace('-', '') - if(line == argDate): + if(line == arg_date): count += 1 print(lines[i+1], '\n', lines[i+2], '\n', lines[i+3]) - i = i + 5 - - # if (count == 0): - # raise NotFound - - f.close() + i = i + 4 + if (count == 0): + print("Not news found on this date") + raise NotFound - - -# f = open('parser.log') -# lines = f.readlines() -# for line in lines: -# logDate = line.split()[1] -# logDate = logDate.replace('-', '') - -# if (logDate == argDate): -# print(line) \ No newline at end of file + f.close() diff --git a/rss/images/19.jpg b/rss/images/19.jpg deleted file mode 100644 index 9ef621810a08b0d49a55b71e613d30136f938080..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3274 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 diff --git a/rss/images/88.jpg b/rss/images/88.jpg deleted file mode 100644 index 940be42e35615114d2e6878519a65d5d4c4ee7b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20179 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 D$<%l& diff --git a/rss/iter4.py b/rss/iter4.py deleted file mode 100644 index da41518..0000000 --- a/rss/iter4.py +++ /dev/null @@ -1,47 +0,0 @@ -# pdf = FPDF() -# pdf.add_page() - -# def toPdf(item, pdf_path, media): - -# pdf.set_font('Arial', 'B', 16) -# title = item.title -# print(title) -# pdf.cell(40, 10, ln=1, txt=title) -# pdf.set_font('Arial', size=14) -# published = item.published -# pdf.cell(10, 10, ln=2, txt=published) -# description = getDescription(item.description) -# pdf.cell(10, 10, ln=3, txt=description) - -# rand = random.randint(1, 120) -# if not os.path.exists('images/'): -# os.makedirs('images/') -# filename_ = 'images/' + str(rand) + '.jpg' -# urllib.request.urlretrieve(media, filename_) - -# pdf.image(filename_) - -# # name_ = item.title.split()[1] -# if not os.path.exists(pdf_path): -# os.makedirs(pdf_path) -# outfile = pdf_path + 'news' + '.pdf' -# pdf.output(r'outfile', 'F') -# import dominate -# from dominate.tags import * - - -# doc = dominate.document(title='HTML document') - -# with doc: -# with div(): -# h2("A Louisiana woman has been arrested for selling $20 fake doctor&#39;s notes to students trying to skip class") -# p("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") -# # img(src=os.path.abspath(filename_)) - - -# with open('test.html', 'w') as f: -# f.write(str(doc)) - -import os - -print(os.getcwd()) \ No newline at end of file diff --git a/rss/main.py b/rss/main.py index 33302a0..afeedf6 100644 --- a/rss/main.py +++ b/rss/main.py @@ -3,6 +3,7 @@ import get_cache import convert + parser = argparse.ArgumentParser(description='Pure Python command-line RSS reader') parser.add_argument('source', action='store', help='RSS URL') @@ -14,12 +15,11 @@ parser.add_argument('--tohtml', action='store', help='Convert news in html format') parser.add_argument('--topdf', action='store', help='Convert news in pdf format') - args = parser.parse_args() if (args.version): - version = 1.0 + version = 4.1 print(version) elif(args.verbose): @@ -29,7 +29,7 @@ get_cache.getCache(args.date) elif(args.tohtml or args.topdf): - + if(args.tohtml): args.topdf = False elif(args.topdf): @@ -45,3 +45,8 @@ rss_parser.getEntries(args.source, args.json, args.verbose, int(args.limit)) + +# fast paste in argument +# source = "https://news.yahoo.com/rss" +# source1 = "https://news.google.com/rss" +# source2 = "https://www.theguardian.com/world/rss" diff --git a/rss/parser.log b/rss/parser.log index e69de29..84c4580 100644 --- a/rss/parser.log +++ b/rss/parser.log @@ -0,0 +1,122 @@ +DEBUG [2019-11-02 22:19:27,660] Title: Greta Thunberg says meeting with Trump 'would be a waste of time' +DEBUG [2019-11-02 22:19:27,660] Date: Fri, 01 Nov 2019 10:58:43 -0400 +DEBUG [2019-11-02 22:19:27,660] Link: https://news.yahoo.com/greta-thunberg-trump-meeting-waste-of-time-ellen-show-145843540.html + +DEBUG [2019-11-02 22:19:27,662] Description: The Swedish teenage climate activist says she wouldn’t want to meet with the president even if given the opportunity. + +DEBUG [2019-11-02 22:19:27,662] Links: +[1]: https://news.yahoo.com/greta-thunberg-trump-meeting-waste-of-time-ellen-show-145843540.html(link) +[2]: +http://l1.yimg.com/uu/api/res/1.2/wW.7oSM3qCipVmF.GW59yQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/c9530760-fcb6-11e9-9b86-71abb69fe041 + + +DEBUG [2019-11-02 22:19:27,808] Title: Hedge fund billionaire fires back at Warren: 'Your vilification of the rich is misguided' +DEBUG [2019-11-02 22:19:27,808] Date: Thu, 31 Oct 2019 18:38:28 -0400 +DEBUG [2019-11-02 22:19:27,809] Link: https://news.yahoo.com/hedge-fund-billionaire-fires-back-at-warren-your-vilification-of-the-rich-is-misguided-223828623.html + +DEBUG [2019-11-02 22:19:27,810] Description: Hedge fund mogul Leon Cooperman takes issue with Sen. Warren's plans for a wealth tax. + +DEBUG [2019-11-02 22:19:27,810] Links: +[1]: https://news.yahoo.com/hedge-fund-billionaire-fires-back-at-warren-your-vilification-of-the-rich-is-misguided-223828623.html(link) +[2]: +http://l2.yimg.com/uu/api/res/1.2/7AlswhC6cT41CFu0dIKPng--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/6b111e90-fc2e-11e9-9cfb-0aed11fd38d1 + + +DEBUG [2019-11-02 22:19:27,861] Title: 2 bodies found at Texas beach are New Hampshire couple +DEBUG [2019-11-02 22:19:27,862] Date: Sat, 02 Nov 2019 12:02:27 -0400 +DEBUG [2019-11-02 22:19:27,863] Link: https://news.yahoo.com/2-bodies-found-texas-beach-123706918.html + +DEBUG [2019-11-02 22:19:27,864] Description: The two bodies found buried at a South Texas beach have been identified as a missing New Hampshire couple, investigators announced Friday. The deaths of James Butler, 48, and Michelle Butler, 46, are being investigated as homicides, the Kleberg County Sheriff's Office in Texas said in a release. A deputy on Sunday located a woman's remains in a shallow grave on Padre Island, near Corpus Christi, the sheriff's office said. + +DEBUG [2019-11-02 22:19:27,864] Links: +[1]: https://news.yahoo.com/2-bodies-found-texas-beach-123706918.html(link) +[2]: + + + +DEBUG [2019-11-03 13:21:26,115] Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September +DEBUG [2019-11-03 13:21:26,115] Date: Fri, 01 Nov 2019 20:10:06 -0400 +DEBUG [2019-11-03 13:21:26,115] Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html + +DEBUG [2019-11-03 13:21:26,116] Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. + +DEBUG [2019-11-03 13:21:26,116] Links: +[1]: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/u.zbZWhagOyVs3mZPs7xDw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/56855c90-fd12-11e9-b7ff-ee5e78520884 + + +DEBUG [2019-11-03 13:21:26,125] Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa +DEBUG [2019-11-03 13:21:26,125] Date: Fri, 01 Nov 2019 15:07:03 -0400 +DEBUG [2019-11-03 13:21:26,125] Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html + +DEBUG [2019-11-03 13:21:26,126] Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? + +DEBUG [2019-11-03 13:21:26,127] Links: +[1]: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/dNE.Y5HcCE5oIQ4z_V58dA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/f185fd60-fcd3-11e9-bed9-65bc5d40927a + + +DEBUG [2019-11-03 13:22:22,932] Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September +DEBUG [2019-11-03 13:22:22,932] Date: Fri, 01 Nov 2019 20:10:06 -0400 +DEBUG [2019-11-03 13:22:22,932] Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html + +DEBUG [2019-11-03 13:22:22,933] Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. + +DEBUG [2019-11-03 13:22:22,933] Links: +[1]: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/u.zbZWhagOyVs3mZPs7xDw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/56855c90-fd12-11e9-b7ff-ee5e78520884 + + +DEBUG [2019-11-03 13:22:22,966] Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa +DEBUG [2019-11-03 13:22:22,967] Date: Fri, 01 Nov 2019 15:07:03 -0400 +DEBUG [2019-11-03 13:22:22,967] Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html + +DEBUG [2019-11-03 13:22:22,968] Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? + +DEBUG [2019-11-03 13:22:22,968] Links: +[1]: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/dNE.Y5HcCE5oIQ4z_V58dA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/f185fd60-fcd3-11e9-bed9-65bc5d40927a + + +DEBUG [2019-11-03 13:22:34,788] Title: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL - ABC News +DEBUG [2019-11-03 13:22:34,788] Date: Sun, 03 Nov 2019 05:00:00 GMT +DEBUG [2019-11-03 13:22:34,788] Link: https://news.google.com/__i/rss/rd/articles/CBMiYGh0dHBzOi8vYWJjbmV3cy5nby5jb20vUG9saXRpY3MvYmlkZW4td2FycmVuLXNhbmRlcnMtc3RheS10b3AtaGVhbHRoLW5vdy1pc3N1ZS9zdG9yeT9pZD02NjY2NDc3MdIBAA?oc=5 + +DEBUG [2019-11-03 13:22:34,791] Description: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL  ABC News7 takeaways from Iowa Democrats' biggest night of the year  CNNDemocrats speak to Iowa voters  CBS This MorningAre There Any Cheerful Democrats Left? Yes, in Iowa.  BloombergAt critical Iowa dinner, Buttigieg and Harris deliver a punch  The Boston GlobeView full coverage on Google News + +DEBUG [2019-11-03 13:22:34,791] Links: +[1]: https://news.google.com/__i/rss/rd/articles/CBMiYGh0dHBzOi8vYWJjbmV3cy5nby5jb20vUG9saXRpY3MvYmlkZW4td2FycmVuLXNhbmRlcnMtc3RheS10b3AtaGVhbHRoLW5vdy1pc3N1ZS9zdG9yeT9pZD02NjY2NDc3MdIBAA?oc=5(link) +[2]: + +DEBUG [2019-11-03 13:22:34,802] Title: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm - USA TODAY +DEBUG [2019-11-03 13:22:34,802] Date: Sun, 03 Nov 2019 00:59:04 GMT +DEBUG [2019-11-03 13:22:34,802] Link: https://news.google.com/__i/rss/rd/articles/CBMigQFodHRwczovL3d3dy51c2F0b2RheS5jb20vc3RvcnkvbmV3cy93b3JsZC8yMDE5LzExLzAyL2lyb24tc2Nvdy1uaWFnYXJhLWZhbGxzLWRpc2xvZGdlZC1zZXZlcmUtd2VhdGhlci1hZnRlci0xMDEteWVhcnMvNDE0NDEwNDAwMi_SASdodHRwczovL2FtcC51c2F0b2RheS5jb20vYW1wLzQxNDQxMDQwMDI?oc=5 + +DEBUG [2019-11-03 13:22:34,805] Description: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm  USA TODAYWind and rain dislodge boat trapped on rocks above Niagara Falls for 101 years  CNNHistoric iron scow moves for first time in 101 years, closer to Niagara Falls ledge  Fox NewsNiagara Falls' iron scow moves for first time in 101 years, moving it closer to Niagara Falls ledge  Daily MailView full coverage on Google News + +DEBUG [2019-11-03 13:22:34,806] Links: +[1]: https://news.google.com/__i/rss/rd/articles/CBMigQFodHRwczovL3d3dy51c2F0b2RheS5jb20vc3RvcnkvbmV3cy93b3JsZC8yMDE5LzExLzAyL2lyb24tc2Nvdy1uaWFnYXJhLWZhbGxzLWRpc2xvZGdlZC1zZXZlcmUtd2VhdGhlci1hZnRlci0xMDEteWVhcnMvNDE0NDEwNDAwMi_SASdodHRwczovL2FtcC51c2F0b2RheS5jb20vYW1wLzQxNDQxMDQwMDI?oc=5(link) +[2]: + +DEBUG [2019-11-03 13:23:51,912] Title: Johnson ‘knew about Vote Leave’s illegal overspend’, says MP +DEBUG [2019-11-03 13:23:51,912] Date: Sat, 02 Nov 2019 20:31:08 GMT +DEBUG [2019-11-03 13:23:51,912] Link: https://www.theguardian.com/politics/2019/nov/02/boris-johnson-vote-leave-overspend-eu-referendum-labour-mp-ian-lucas + +DEBUG [2019-11-03 13:23:51,915] Description: Prime minister accused of ‘sitting on information’ after EU referendumBoris Johnson knew of Vote Leave’s overspend during the 2016 EU referendum, but appears to have failed to tell the authorities, according to explosive new claims from a senior MP. The payment was subsequently ruled to be illegal.Ian Lucas revealed that he has seen correspondence obtained during the parliamentary inquiry into disinformation and democracy which showed that Johnson’s most senior aide, Dominic Cummings, told the Electoral Commission that the prime minister, and his cabinet colleague Michael Gove, knew of the overspend by the pro-Brexit organisation. Continue reading... + +DEBUG [2019-11-03 13:23:51,915] Links: +[1]: https://www.theguardian.com/politics/2019/nov/02/boris-johnson-vote-leave-overspend-eu-referendum-labour-mp-ian-lucas(link) +[2]: https://i.guim.co.uk/img/media/d6585725c69aafb691d17be555ac42423a40f4f8/91_62_4280_2568/master/4280.jpg?width=140&quality=85&auto=format&fit=max&s=cd65351129bee6645ee5aeae3d8c2aa5 + + +DEBUG [2019-11-03 13:23:51,927] Title: Airbnb to ban party houses in wake of Halloween shooting in California +DEBUG [2019-11-03 13:23:51,927] Date: Sun, 03 Nov 2019 02:27:40 GMT +DEBUG [2019-11-03 13:23:51,927] Link: https://www.theguardian.com/us-news/2019/nov/03/airbnb-to-ban-party-houses-in-wake-of-halloween-shooting-in-california + +DEBUG [2019-11-03 13:23:51,930] Description: Airbnb’s CEO said the company ‘must do better’ after five people were killed at a party in San FranciscoAirbnb’s chief executive has said the company will ban “party houses” following a deadly shooting at a Halloween party held at an Airbnb rental home in California.In a series of tweets, Brian Chesky said on Saturday that the San Francisco-based company would expand manual screening of “high risk” reservations and remove guests who fail to comply with policies banning parties at Airbnb rental homes. Continue reading... + +DEBUG [2019-11-03 13:23:51,931] Links: +[1]: https://www.theguardian.com/us-news/2019/nov/03/airbnb-to-ban-party-houses-in-wake-of-halloween-shooting-in-california(link) +[2]: https://i.guim.co.uk/img/media/6ddf2b7af06737f9c0548167d66c6c05588fd0bb/0_115_2572_1544/master/2572.jpg?width=140&quality=85&auto=format&fit=max&s=7f4a1d920b2e9a3cca0ae2a5cd98eb99 + + diff --git a/rss/pdfs/19.pdf b/rss/pdfs/19.pdf deleted file mode 100644 index 940cabe1eb56538b14198ac1afd799598fd6efba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40989 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 0): cacheNews(item) @@ -33,37 +36,32 @@ def getEntries(url, json, verbose, limit, pdf_path): print(getJSON(item)) else: - print("Title: ", item.title) - print("Date: ", item.published) - print("Link: ", item.link, '\n') - print("Description: ", getDescription(item.description), '\n') - print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", - checkMediaContent(item), '\n') + printNews(item) else: break limit -= 1 - +# checks if there is media content def checkMediaContent(item): - media_content = '\n' - if(item.has_key('media_content')): - media_content = '\n' + item.media_content[0]['url'] + '\n' + media_content = ' ' + if('media_content' in item.keys()): + media_content = item.media_content[0]['url'] + '\n' return media_content +# news caching in 'cache_news.txt' def cacheNews(item): - today = date.today() - d1 = today.strftime("%Y-%m-%d") + today = date.today().strftime("%Y-%m-%d") with open('cache_news.txt', 'a', encoding="utf-8") as f: - f.write(d1 + '\n' + + f.write(today + '\n' + "Title: " + item.title + '\n' + "Link: " + item.link + '\n' + - "Description: " + getDescription(item.description) + - checkMediaContent(item) + '\n') + "Description: " + getDescription(item.description) + '\n') +# logs news in 'parser.log' def loggingItems(item): logging.debug("Title: " + str(item.title)) logging.debug("Date: " + str(item.published)) @@ -73,6 +71,7 @@ def loggingItems(item): "(link)\n[2]: " + str(checkMediaContent(item))+'\n') +# return news in json format def getJSON(item): return json.dumps({ 'Title: ': item.title, @@ -82,14 +81,23 @@ def getJSON(item): }) +# return description without html tags def getDescription(item): return BeautifulSoup(item, features="html.parser").getText() +# print news +def printNews(item): + print("Title: ", item.title) + print("Date: ", item.published) + print("Link: ", item.link, '\n') + print("Description: ", getDescription(item.description), '\n') + print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", + checkMediaContent(item), '\n') + + +# pring logs def printLogs(): with open('parser.log', 'r') as f: for line in f: print(line) - - - diff --git a/rss/test.css b/rss/style.css similarity index 100% rename from rss/test.css rename to rss/style.css From 659f036aa9048c70cb7384503ca9a125c6f3a88c Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Sun, 3 Nov 2019 21:48:03 +0300 Subject: [PATCH 06/15] Made refactoring of all code --- .gitignore | 1 - README.md | 72 ++++++++++--------- LICENSE.txt => __init__.py | 0 rss/__init__.py | 3 - rss/cache_news.txt | 44 ------------ rss/convert.py | 105 --------------------------- rss/get_cache.py | 31 -------- rss/main.py | 52 -------------- rss/parser.log | 122 ------------------------------- rss/rss_parser.py | 103 -------------------------- rss_reader/__init__.py | 0 rss_reader/converter.py | 26 +++++++ rss_reader/get_cache.py | 18 +++++ rss_reader/logger.py | 7 ++ rss_reader/parse_news.py | 28 ++++++++ rss_reader/parser.log | 0 rss_reader/requirements.txt | 5 ++ rss_reader/rss_reader.py | 132 ++++++++++++++++++++++++++++++++++ {rss => rss_reader}/style.css | 4 -- rss_reader/version.py | 1 + setup.py | 25 ++++--- 21 files changed, 271 insertions(+), 508 deletions(-) rename LICENSE.txt => __init__.py (100%) delete mode 100644 rss/__init__.py delete mode 100644 rss/cache_news.txt delete mode 100644 rss/convert.py delete mode 100644 rss/get_cache.py delete mode 100644 rss/main.py delete mode 100644 rss/parser.log delete mode 100644 rss/rss_parser.py create mode 100644 rss_reader/__init__.py create mode 100644 rss_reader/converter.py create mode 100644 rss_reader/get_cache.py create mode 100644 rss_reader/logger.py create mode 100644 rss_reader/parse_news.py create mode 100644 rss_reader/parser.log create mode 100644 rss_reader/requirements.txt create mode 100644 rss_reader/rss_reader.py rename {rss => rss_reader}/style.css (90%) create mode 100644 rss_reader/version.py diff --git a/.gitignore b/.gitignore index 2c09a51..5338b7b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ __pycache__/ test.py rss/__pycache__/ rss/.vscode/ - diff --git a/README.md b/README.md index f3171ab..d6f410c 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,38 @@ -# FinalTaskRssReader -For final task pull requests. - - -## How to create a pull request - -1. Create github account. *Preferrably using email you used when registerer on this course* -2. Fork this repository. ('Fork' button at the top right of this repository page) -3. Open the page of your *new repository* that was created when you forked this repo. -4. Press button clone or download at the middle right of the page and CTRL-C the url. -5. On your machine go to the directory you want. -6. Depending on the OS you are working with, open GitBash(Windows)/Command Line or Terminal(Linux) there -7. Use command `git clone ` - -Congrats! You have successfully forked our repository. - - -## Additional project structure requirements - -1. `setup.py` file for setuptools *must* be in the root of `final_task` folder. Use `setup.py` that is already there. (that means path to this file must end with `final_task/setup.py` ) -2. Entry point to your application, aka its main module *must* be named as `rss_reader.py` . Use `rss_reader.py` that is already in `rss_reader` folder. -3. You should describe how does your project work, how to launch it and etc in README.md in the `final_task/README.md` file. -4. If you used any non-standart libraries they must be listed in `rss_reader/requirements.txt` file. -5. All unit test files should be in separate folder called `tests`. - - -## Pull request requirements(!!!) - -1. When creating pull request make sure that `target branch` is `master` on OUR repo, not yours. -2. Pull request name *MUST* be in format: `YourFirstName_YourLastName_EmailYouUsedWhileRegisteringOnThisCourse` -3. Pull request which have any other name format, or invalid e-mail *will be ignored completely until you fix it*. So make sure you specified correct e-mail. -4. In pull request description specify your current iteration. You also can add there any other info you want us to know before we start code review. -5. *Pull request must NOT contain any .pyc files, any virtual environment files/folders, any IDE technical files*. - - +#RSS-READER + +###Command-line utility which receives RSS URL and prints results in human-readable format. + +**Example:** +python rss_reader.py https://news.yahoo.com/rss - -limit LIMIT 1 + +**Output**: +Feed: Yahoo News - Latest News & Headlines + +Title: Families come from across U.S. to grieve relatives slain in Mexico +Date: Thu, 07 Nov 2019 01:06:45 -0500 +Link: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html + +Description: An American man whose grandchildren were slain in a massacre in Mexico demanded justice on Thursday for other victims of the country's drug war, as relatives gathered from +across the United States for a funeral guarded by heavily armed military. Kenneth Miller lost his daughter-in-law and four grandchildren, all dual citizens, in an ambush on Monday in th +e northern border state of Sonora that killed three mothers and six children. The attack on members of breakaway Mormon communities who settled in Mexico decades ago prompted U.S. Pres +ident Donald Trump to urge Mexico and the United States to "wage war" together on drug cartels. + +Links: +``` +[1]: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html (link) +[2]: http://l.yimg.com/uu/api/res/1.2/rRx_J3xHKYzIQ4EsiCPRTw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/9cbb9d9c38b8bbe10243ebfde0f6db48 +``` + +**positional arguments:** + - -source RSS URL + +**optional arguments:** + - -help Show this help message and exit + - -version Print version info + - -json Print result as JSON in stdout + - -verbose Outputs verbose status messages + - -limit LIMIT Limit news topics if this parameter provided + - -date DATE Print news from the specified day + - -tohtml Convert news in html format + - -topdf Convert news in pdf format diff --git a/LICENSE.txt b/__init__.py similarity index 100% rename from LICENSE.txt rename to __init__.py diff --git a/rss/__init__.py b/rss/__init__.py deleted file mode 100644 index bb12d91..0000000 --- a/rss/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# import parser -# from iter1 import getEntries -# name = "piter" \ No newline at end of file diff --git a/rss/cache_news.txt b/rss/cache_news.txt deleted file mode 100644 index 645c181..0000000 --- a/rss/cache_news.txt +++ /dev/null @@ -1,44 +0,0 @@ -2019-11-02 -Title: Greta Thunberg says meeting with Trump 'would be a waste of time' -Link: https://news.yahoo.com/greta-thunberg-trump-meeting-waste-of-time-ellen-show-145843540.html -Description: The Swedish teenage climate activist says she wouldn’t want to meet with the president even if given the opportunity. -2019-11-02 -Title: Hedge fund billionaire fires back at Warren: 'Your vilification of the rich is misguided' -Link: https://news.yahoo.com/hedge-fund-billionaire-fires-back-at-warren-your-vilification-of-the-rich-is-misguided-223828623.html -Description: Hedge fund mogul Leon Cooperman takes issue with Sen. Warren's plans for a wealth tax. -2019-11-02 -Title: 2 bodies found at Texas beach are New Hampshire couple -Link: https://news.yahoo.com/2-bodies-found-texas-beach-123706918.html -Description: The two bodies found buried at a South Texas beach have been identified as a missing New Hampshire couple, investigators announced Friday. The deaths of James Butler, 48, and Michelle Butler, 46, are being investigated as homicides, the Kleberg County Sheriff's Office in Texas said in a release. A deputy on Sunday located a woman's remains in a shallow grave on Padre Island, near Corpus Christi, the sheriff's office said. -2019-11-03 -Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September -Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html -Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. -2019-11-03 -Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa -Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html -Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? -2019-11-03 -Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September -Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html -Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. -2019-11-03 -Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa -Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html -Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? -2019-11-03 -Title: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL - ABC News -Link: https://news.google.com/__i/rss/rd/articles/CBMiYGh0dHBzOi8vYWJjbmV3cy5nby5jb20vUG9saXRpY3MvYmlkZW4td2FycmVuLXNhbmRlcnMtc3RheS10b3AtaGVhbHRoLW5vdy1pc3N1ZS9zdG9yeT9pZD02NjY2NDc3MdIBAA?oc=5 -Description: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL  ABC News7 takeaways from Iowa Democrats' biggest night of the year  CNNDemocrats speak to Iowa voters  CBS This MorningAre There Any Cheerful Democrats Left? Yes, in Iowa.  BloombergAt critical Iowa dinner, Buttigieg and Harris deliver a punch  The Boston GlobeView full coverage on Google News -2019-11-03 -Title: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm - USA TODAY -Link: https://news.google.com/__i/rss/rd/articles/CBMigQFodHRwczovL3d3dy51c2F0b2RheS5jb20vc3RvcnkvbmV3cy93b3JsZC8yMDE5LzExLzAyL2lyb24tc2Nvdy1uaWFnYXJhLWZhbGxzLWRpc2xvZGdlZC1zZXZlcmUtd2VhdGhlci1hZnRlci0xMDEteWVhcnMvNDE0NDEwNDAwMi_SASdodHRwczovL2FtcC51c2F0b2RheS5jb20vYW1wLzQxNDQxMDQwMDI?oc=5 -Description: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm  USA TODAYWind and rain dislodge boat trapped on rocks above Niagara Falls for 101 years  CNNHistoric iron scow moves for first time in 101 years, closer to Niagara Falls ledge  Fox NewsNiagara Falls' iron scow moves for first time in 101 years, moving it closer to Niagara Falls ledge  Daily MailView full coverage on Google News -2019-11-03 -Title: Johnson ‘knew about Vote Leave’s illegal overspend’, says MP -Link: https://www.theguardian.com/politics/2019/nov/02/boris-johnson-vote-leave-overspend-eu-referendum-labour-mp-ian-lucas -Description: Prime minister accused of ‘sitting on information’ after EU referendumBoris Johnson knew of Vote Leave’s overspend during the 2016 EU referendum, but appears to have failed to tell the authorities, according to explosive new claims from a senior MP. The payment was subsequently ruled to be illegal.Ian Lucas revealed that he has seen correspondence obtained during the parliamentary inquiry into disinformation and democracy which showed that Johnson’s most senior aide, Dominic Cummings, told the Electoral Commission that the prime minister, and his cabinet colleague Michael Gove, knew of the overspend by the pro-Brexit organisation. Continue reading... -2019-11-03 -Title: Airbnb to ban party houses in wake of Halloween shooting in California -Link: https://www.theguardian.com/us-news/2019/nov/03/airbnb-to-ban-party-houses-in-wake-of-halloween-shooting-in-california -Description: Airbnb’s CEO said the company ‘must do better’ after five people were killed at a party in San FranciscoAirbnb’s chief executive has said the company will ban “party houses” following a deadly shooting at a Halloween party held at an Airbnb rental home in California.In a series of tweets, Brian Chesky said on Saturday that the San Francisco-based company would expand manual screening of “high risk” reservations and remove guests who fail to comply with policies banning parties at Airbnb rental homes. Continue reading... diff --git a/rss/convert.py b/rss/convert.py deleted file mode 100644 index a103f14..0000000 --- a/rss/convert.py +++ /dev/null @@ -1,105 +0,0 @@ -import urllib.request -import random -import feedparser -import os -from bs4 import BeautifulSoup -import dominate -from dominate.tags import * -import pdfkit -import webbrowser - - -# make convertion of news into pdf or html -def makeConvertion(url, html_path, pdf_path, limit): - ''' - 1. create object of feedparser library - 2. create object of dominate library for creating html files - 3. on each iteration - 3.1 check if there is media content - 3.2 write html tags - 3.3 convert format of file - ''' - channel = feedparser.parse(url) - doc = dominate.document(title="HTML document") - - for index, item in enumerate(channel.entries): - if(index == limit): - break - - random_name = random.randint(1, 120) - media_file_name = checkMediaContent(item, random_name) - - with doc: - with div(): - h2(item.title) - p("Date: " + item.published) - if (media_file_name): - img(src=os.path.abspath(media_file_name)) - p("Description: " + getDescription(item.description)) - - - if(html_path): - intoHTML(html_path, doc, random_name) - - elif(pdf_path): - intoPDF(pdf_path, doc, random_name) - - -# return description of news without html tags -def getDescription(item): - return BeautifulSoup(item, features="html.parser").getText() - - -def checkMediaContent(item, random_name): - ''' - 1. create folder with images and return name of image - if there is media content in news - 2. or return nothing - ''' - media_content = '' - if('media_content' in item.keys()): - media_content = item.media_content[0]['url'] - - if not os.path.exists('images/'): - os.makedirs('images/') - filename_ = 'images/' + str(random_name) + '.jpg' - urllib.request.urlretrieve(media, filename_) - return filename_ - - return media_content - - -def intoHTML(html_path, doc, random_name): - ''' - 1. read file with styles for html - 2. write styles in html - 3. create folder for html file if not exists - 4. write html file - 5. open browser with html - ''' - with open('style.css') as file: - css_file = file.read() - - with doc.head: - style(css_file) - - if not os.path.exists(html_path): - os.makedirs(html_path) - html_file = html_path + str(random_name) + '.html' - - with open(html_file, 'w') as f: - f.write(str(doc)) - - webbrowser.open(html_file, new=2) - - -def intoPDF(pdf_path, doc, random_name): - ''' - 1. create folder for pdf files if not exists - 2. write pdf file - ''' - if not os.path.exists(pdf_path): - os.makedirs(pdf_path) - pdf_file = pdf_path + str(random_name) + '.pdf' - - pdfkit.from_string(str(doc), pdf_file) \ No newline at end of file diff --git a/rss/get_cache.py b/rss/get_cache.py deleted file mode 100644 index 6d9ab5d..0000000 --- a/rss/get_cache.py +++ /dev/null @@ -1,31 +0,0 @@ -class NotFound(Exception): - pass - - -# print cached news -def getCache(arg_date): - ''' - 1. open and read file with news - 2. print news if their date is equal to the date in argument - 3. or raise error - ''' - arg_date = arg_date + '\n' - - f = open('cache_news.txt', encoding='utf-8') - lines = f.readlines() - - i = 0 - while (i < len(lines)): - count = 0 - line = lines[i].replace('-', '') - - if(line == arg_date): - count += 1 - print(lines[i+1], '\n', lines[i+2], '\n', lines[i+3]) - i = i + 4 - - if (count == 0): - print("Not news found on this date") - raise NotFound - - f.close() diff --git a/rss/main.py b/rss/main.py deleted file mode 100644 index afeedf6..0000000 --- a/rss/main.py +++ /dev/null @@ -1,52 +0,0 @@ -import argparse -import rss_parser -import get_cache -import convert - - -parser = argparse.ArgumentParser(description='Pure Python command-line RSS reader') - -parser.add_argument('source', action='store', help='RSS URL') -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('--limit', action='store', help='Limit news topics') -parser.add_argument('--date', action='store', help='Print news from the specified day') -parser.add_argument('--tohtml', action='store', help='Convert news in html format') -parser.add_argument('--topdf', action='store', help='Convert news in pdf format') - -args = parser.parse_args() - - -if (args.version): - version = 4.1 - print(version) - -elif(args.verbose): - rss_parser.printLogs() - -elif(args.date): - get_cache.getCache(args.date) - -elif(args.tohtml or args.topdf): - - if(args.tohtml): - args.topdf = False - elif(args.topdf): - args.tohtml = False - if not(args.limit): - args.limit = 0 - - convert.makeConvertion(args.source, args.tohtml, args.topdf, int(args.limit)) - -else: - if not(args.limit): - args.limit = 0 - - rss_parser.getEntries(args.source, args.json, args.verbose, int(args.limit)) - - -# fast paste in argument -# source = "https://news.yahoo.com/rss" -# source1 = "https://news.google.com/rss" -# source2 = "https://www.theguardian.com/world/rss" diff --git a/rss/parser.log b/rss/parser.log deleted file mode 100644 index 84c4580..0000000 --- a/rss/parser.log +++ /dev/null @@ -1,122 +0,0 @@ -DEBUG [2019-11-02 22:19:27,660] Title: Greta Thunberg says meeting with Trump 'would be a waste of time' -DEBUG [2019-11-02 22:19:27,660] Date: Fri, 01 Nov 2019 10:58:43 -0400 -DEBUG [2019-11-02 22:19:27,660] Link: https://news.yahoo.com/greta-thunberg-trump-meeting-waste-of-time-ellen-show-145843540.html - -DEBUG [2019-11-02 22:19:27,662] Description: The Swedish teenage climate activist says she wouldn’t want to meet with the president even if given the opportunity. - -DEBUG [2019-11-02 22:19:27,662] Links: -[1]: https://news.yahoo.com/greta-thunberg-trump-meeting-waste-of-time-ellen-show-145843540.html(link) -[2]: -http://l1.yimg.com/uu/api/res/1.2/wW.7oSM3qCipVmF.GW59yQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/c9530760-fcb6-11e9-9b86-71abb69fe041 - - -DEBUG [2019-11-02 22:19:27,808] Title: Hedge fund billionaire fires back at Warren: 'Your vilification of the rich is misguided' -DEBUG [2019-11-02 22:19:27,808] Date: Thu, 31 Oct 2019 18:38:28 -0400 -DEBUG [2019-11-02 22:19:27,809] Link: https://news.yahoo.com/hedge-fund-billionaire-fires-back-at-warren-your-vilification-of-the-rich-is-misguided-223828623.html - -DEBUG [2019-11-02 22:19:27,810] Description: Hedge fund mogul Leon Cooperman takes issue with Sen. Warren's plans for a wealth tax. - -DEBUG [2019-11-02 22:19:27,810] Links: -[1]: https://news.yahoo.com/hedge-fund-billionaire-fires-back-at-warren-your-vilification-of-the-rich-is-misguided-223828623.html(link) -[2]: -http://l2.yimg.com/uu/api/res/1.2/7AlswhC6cT41CFu0dIKPng--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-10/6b111e90-fc2e-11e9-9cfb-0aed11fd38d1 - - -DEBUG [2019-11-02 22:19:27,861] Title: 2 bodies found at Texas beach are New Hampshire couple -DEBUG [2019-11-02 22:19:27,862] Date: Sat, 02 Nov 2019 12:02:27 -0400 -DEBUG [2019-11-02 22:19:27,863] Link: https://news.yahoo.com/2-bodies-found-texas-beach-123706918.html - -DEBUG [2019-11-02 22:19:27,864] Description: The two bodies found buried at a South Texas beach have been identified as a missing New Hampshire couple, investigators announced Friday. The deaths of James Butler, 48, and Michelle Butler, 46, are being investigated as homicides, the Kleberg County Sheriff's Office in Texas said in a release. A deputy on Sunday located a woman's remains in a shallow grave on Padre Island, near Corpus Christi, the sheriff's office said. - -DEBUG [2019-11-02 22:19:27,864] Links: -[1]: https://news.yahoo.com/2-bodies-found-texas-beach-123706918.html(link) -[2]: - - - -DEBUG [2019-11-03 13:21:26,115] Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September -DEBUG [2019-11-03 13:21:26,115] Date: Fri, 01 Nov 2019 20:10:06 -0400 -DEBUG [2019-11-03 13:21:26,115] Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html - -DEBUG [2019-11-03 13:21:26,116] Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. - -DEBUG [2019-11-03 13:21:26,116] Links: -[1]: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/u.zbZWhagOyVs3mZPs7xDw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/56855c90-fd12-11e9-b7ff-ee5e78520884 - - -DEBUG [2019-11-03 13:21:26,125] Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa -DEBUG [2019-11-03 13:21:26,125] Date: Fri, 01 Nov 2019 15:07:03 -0400 -DEBUG [2019-11-03 13:21:26,125] Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html - -DEBUG [2019-11-03 13:21:26,126] Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? - -DEBUG [2019-11-03 13:21:26,127] Links: -[1]: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/dNE.Y5HcCE5oIQ4z_V58dA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/f185fd60-fcd3-11e9-bed9-65bc5d40927a - - -DEBUG [2019-11-03 13:22:22,932] Title: Thanks to Trump, the U.S. hasn't admitted a single refugee since September -DEBUG [2019-11-03 13:22:22,932] Date: Fri, 01 Nov 2019 20:10:06 -0400 -DEBUG [2019-11-03 13:22:22,932] Link: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html - -DEBUG [2019-11-03 13:22:22,933] Description: October was the first full month in at least 18 years in which the United States did not admit any refugees. - -DEBUG [2019-11-03 13:22:22,933] Links: -[1]: https://news.yahoo.com/thanks-to-trump-the-us-hasnt-admitted-a-single-refugee-since-september-001006863.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/u.zbZWhagOyVs3mZPs7xDw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/56855c90-fd12-11e9-b7ff-ee5e78520884 - - -DEBUG [2019-11-03 13:22:22,966] Title: 2020 Vision: If a single speech can shake up the Democratic race, it might happen in Iowa -DEBUG [2019-11-03 13:22:22,967] Date: Fri, 01 Nov 2019 15:07:03 -0400 -DEBUG [2019-11-03 13:22:22,967] Link: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html - -DEBUG [2019-11-03 13:22:22,968] Description: Will any of this year’s candidates pull an Obama at the newly named Liberty and Justice Celebration? - -DEBUG [2019-11-03 13:22:22,968] Links: -[1]: https://news.yahoo.com/2020-vision-iowa-poll-warren-biden-bernie-obama-190703996.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/dNE.Y5HcCE5oIQ4z_V58dA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/f185fd60-fcd3-11e9-bed9-65bc5d40927a - - -DEBUG [2019-11-03 13:22:34,788] Title: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL - ABC News -DEBUG [2019-11-03 13:22:34,788] Date: Sun, 03 Nov 2019 05:00:00 GMT -DEBUG [2019-11-03 13:22:34,788] Link: https://news.google.com/__i/rss/rd/articles/CBMiYGh0dHBzOi8vYWJjbmV3cy5nby5jb20vUG9saXRpY3MvYmlkZW4td2FycmVuLXNhbmRlcnMtc3RheS10b3AtaGVhbHRoLW5vdy1pc3N1ZS9zdG9yeT9pZD02NjY2NDc3MdIBAA?oc=5 - -DEBUG [2019-11-03 13:22:34,791] Description: Biden, Warren, Sanders stay on top; health is now an issue for Sanders: POLL  ABC News7 takeaways from Iowa Democrats' biggest night of the year  CNNDemocrats speak to Iowa voters  CBS This MorningAre There Any Cheerful Democrats Left? Yes, in Iowa.  BloombergAt critical Iowa dinner, Buttigieg and Harris deliver a punch  The Boston GlobeView full coverage on Google News - -DEBUG [2019-11-03 13:22:34,791] Links: -[1]: https://news.google.com/__i/rss/rd/articles/CBMiYGh0dHBzOi8vYWJjbmV3cy5nby5jb20vUG9saXRpY3MvYmlkZW4td2FycmVuLXNhbmRlcnMtc3RheS10b3AtaGVhbHRoLW5vdy1pc3N1ZS9zdG9yeT9pZD02NjY2NDc3MdIBAA?oc=5(link) -[2]: - -DEBUG [2019-11-03 13:22:34,802] Title: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm - USA TODAY -DEBUG [2019-11-03 13:22:34,802] Date: Sun, 03 Nov 2019 00:59:04 GMT -DEBUG [2019-11-03 13:22:34,802] Link: https://news.google.com/__i/rss/rd/articles/CBMigQFodHRwczovL3d3dy51c2F0b2RheS5jb20vc3RvcnkvbmV3cy93b3JsZC8yMDE5LzExLzAyL2lyb24tc2Nvdy1uaWFnYXJhLWZhbGxzLWRpc2xvZGdlZC1zZXZlcmUtd2VhdGhlci1hZnRlci0xMDEteWVhcnMvNDE0NDEwNDAwMi_SASdodHRwczovL2FtcC51c2F0b2RheS5jb20vYW1wLzQxNDQxMDQwMDI?oc=5 - -DEBUG [2019-11-03 13:22:34,805] Description: Boat trapped for 101 years near edge of Niagara Falls moves after Halloween night storm  USA TODAYWind and rain dislodge boat trapped on rocks above Niagara Falls for 101 years  CNNHistoric iron scow moves for first time in 101 years, closer to Niagara Falls ledge  Fox NewsNiagara Falls' iron scow moves for first time in 101 years, moving it closer to Niagara Falls ledge  Daily MailView full coverage on Google News - -DEBUG [2019-11-03 13:22:34,806] Links: -[1]: https://news.google.com/__i/rss/rd/articles/CBMigQFodHRwczovL3d3dy51c2F0b2RheS5jb20vc3RvcnkvbmV3cy93b3JsZC8yMDE5LzExLzAyL2lyb24tc2Nvdy1uaWFnYXJhLWZhbGxzLWRpc2xvZGdlZC1zZXZlcmUtd2VhdGhlci1hZnRlci0xMDEteWVhcnMvNDE0NDEwNDAwMi_SASdodHRwczovL2FtcC51c2F0b2RheS5jb20vYW1wLzQxNDQxMDQwMDI?oc=5(link) -[2]: - -DEBUG [2019-11-03 13:23:51,912] Title: Johnson ‘knew about Vote Leave’s illegal overspend’, says MP -DEBUG [2019-11-03 13:23:51,912] Date: Sat, 02 Nov 2019 20:31:08 GMT -DEBUG [2019-11-03 13:23:51,912] Link: https://www.theguardian.com/politics/2019/nov/02/boris-johnson-vote-leave-overspend-eu-referendum-labour-mp-ian-lucas - -DEBUG [2019-11-03 13:23:51,915] Description: Prime minister accused of ‘sitting on information’ after EU referendumBoris Johnson knew of Vote Leave’s overspend during the 2016 EU referendum, but appears to have failed to tell the authorities, according to explosive new claims from a senior MP. The payment was subsequently ruled to be illegal.Ian Lucas revealed that he has seen correspondence obtained during the parliamentary inquiry into disinformation and democracy which showed that Johnson’s most senior aide, Dominic Cummings, told the Electoral Commission that the prime minister, and his cabinet colleague Michael Gove, knew of the overspend by the pro-Brexit organisation. Continue reading... - -DEBUG [2019-11-03 13:23:51,915] Links: -[1]: https://www.theguardian.com/politics/2019/nov/02/boris-johnson-vote-leave-overspend-eu-referendum-labour-mp-ian-lucas(link) -[2]: https://i.guim.co.uk/img/media/d6585725c69aafb691d17be555ac42423a40f4f8/91_62_4280_2568/master/4280.jpg?width=140&quality=85&auto=format&fit=max&s=cd65351129bee6645ee5aeae3d8c2aa5 - - -DEBUG [2019-11-03 13:23:51,927] Title: Airbnb to ban party houses in wake of Halloween shooting in California -DEBUG [2019-11-03 13:23:51,927] Date: Sun, 03 Nov 2019 02:27:40 GMT -DEBUG [2019-11-03 13:23:51,927] Link: https://www.theguardian.com/us-news/2019/nov/03/airbnb-to-ban-party-houses-in-wake-of-halloween-shooting-in-california - -DEBUG [2019-11-03 13:23:51,930] Description: Airbnb’s CEO said the company ‘must do better’ after five people were killed at a party in San FranciscoAirbnb’s chief executive has said the company will ban “party houses” following a deadly shooting at a Halloween party held at an Airbnb rental home in California.In a series of tweets, Brian Chesky said on Saturday that the San Francisco-based company would expand manual screening of “high risk” reservations and remove guests who fail to comply with policies banning parties at Airbnb rental homes. Continue reading... - -DEBUG [2019-11-03 13:23:51,931] Links: -[1]: https://www.theguardian.com/us-news/2019/nov/03/airbnb-to-ban-party-houses-in-wake-of-halloween-shooting-in-california(link) -[2]: https://i.guim.co.uk/img/media/6ddf2b7af06737f9c0548167d66c6c05588fd0bb/0_115_2572_1544/master/2572.jpg?width=140&quality=85&auto=format&fit=max&s=7f4a1d920b2e9a3cca0ae2a5cd98eb99 - - diff --git a/rss/rss_parser.py b/rss/rss_parser.py deleted file mode 100644 index 901e348..0000000 --- a/rss/rss_parser.py +++ /dev/null @@ -1,103 +0,0 @@ -from bs4 import BeautifulSoup -import feedparser -import json -import logging -from datetime import date - - -# Set basic configs for logging -logging.basicConfig(format=u'%(levelname)-8s [%(asctime)s] %(message)s', - level=logging.DEBUG, - handlers=[logging.FileHandler("parser.log", "a", encoding="utf-8")]) - - -# function for parsing news -def getEntries(url, json, verbose, limit): - ''' - 1. set object of feedparser library - 2. set news limit - 3. on each iteration of loop - 3.1 cache news - 3.2 log news - 3.3 print news in sdtout or json format - ''' - channel = feedparser.parse(url) - print("Feed: ", channel.feed.title, '\n') - - limit = limit or len(channel.entries) - - for item in channel.entries: - - if (limit > 0): - cacheNews(item) - loggingItems(item) - - if (json): - print(getJSON(item)) - - else: - printNews(item) - else: - break - limit -= 1 - - -# checks if there is media content -def checkMediaContent(item): - media_content = ' ' - if('media_content' in item.keys()): - media_content = item.media_content[0]['url'] + '\n' - return media_content - - -# news caching in 'cache_news.txt' -def cacheNews(item): - today = date.today().strftime("%Y-%m-%d") - - with open('cache_news.txt', 'a', encoding="utf-8") as f: - f.write(today + '\n' + - "Title: " + item.title + '\n' + - "Link: " + item.link + '\n' + - "Description: " + getDescription(item.description) + '\n') - - -# logs news in 'parser.log' -def loggingItems(item): - logging.debug("Title: " + str(item.title)) - logging.debug("Date: " + str(item.published)) - logging.debug("Link: " + str(item.link) + '\n') - logging.debug("Description: " + getDescription(item.description) + '\n') - logging.debug("Links:"+"\n[1]: " + str(item.link) + - "(link)\n[2]: " + str(checkMediaContent(item))+'\n') - - -# return news in json format -def getJSON(item): - return json.dumps({ - 'Title: ': item.title, - 'Date: ': item.published, - 'Link: ': item.link, - 'Description: ': getDescription(item.description) - }) - - -# return description without html tags -def getDescription(item): - return BeautifulSoup(item, features="html.parser").getText() - - -# print news -def printNews(item): - print("Title: ", item.title) - print("Date: ", item.published) - print("Link: ", item.link, '\n') - print("Description: ", getDescription(item.description), '\n') - print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", - checkMediaContent(item), '\n') - - -# pring logs -def printLogs(): - with open('parser.log', 'r') as f: - for line in f: - print(line) diff --git a/rss_reader/__init__.py b/rss_reader/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rss_reader/converter.py b/rss_reader/converter.py new file mode 100644 index 0000000..a0c43b6 --- /dev/null +++ b/rss_reader/converter.py @@ -0,0 +1,26 @@ +import os +import pdfkit + + +def intoHTML(html_path, doc, random_name): + ''' + 1. create folder with html file + 2. write html structure + ''' + if not os.path.exists(html_path): + os.makedirs(html_path) + html_file = html_path + str(random_name) + '.html' + + with open(html_file, 'w') as f: + f.write(str(doc)) + + +def intoPDF(pdf_path, doc, random_name): + ''' + 1. create folder with pdf file + 2. convert html into pdf + ''' + if not os.path.exists(pdf_path): + os.makedirs(pdf_path) + pdf_file = pdf_path + str(random_name) + '.pdf' + pdfkit.from_string(str(doc), pdf_file) \ No newline at end of file diff --git a/rss_reader/get_cache.py b/rss_reader/get_cache.py new file mode 100644 index 0000000..9922ab4 --- /dev/null +++ b/rss_reader/get_cache.py @@ -0,0 +1,18 @@ +import os +import logger + + +# print cached news +def getCache(arg_date): + ''' + 1. create file with cache news and write it + 3. or raise error + ''' + try: + cache_on_date = 'cache/' + str(arg_date) + '.txt' + with open(cache_on_date, 'r', encoding='utf-8') as f: + return f.read() + + except FileNotFoundError as e: + logger.logging.error("FileNotFoundError: " + str(e)) + print("No such file. Please first parse news") diff --git a/rss_reader/logger.py b/rss_reader/logger.py new file mode 100644 index 0000000..01f8db3 --- /dev/null +++ b/rss_reader/logger.py @@ -0,0 +1,7 @@ +import logging + +# Set basic configs for logging +fileHandler = logging.FileHandler("parser.log", "a", encoding="utf-8") +logging.basicConfig(format=u'%(levelname)-8s [%(asctime)s] %(message)s', + level=logging.DEBUG, + handlers=[fileHandler]) diff --git a/rss_reader/parse_news.py b/rss_reader/parse_news.py new file mode 100644 index 0000000..e470a69 --- /dev/null +++ b/rss_reader/parse_news.py @@ -0,0 +1,28 @@ +from bs4 import BeautifulSoup +import feedparser +import json + +# check if there is media content +def checkMediaContent(item): + media_content = '' + if ('media_content' in item.keys()): + media_content = item.media_content[0]['url'] + elif ('media_thumbnail' in item.keys()): + media_content = item.media_thumbnail[0]['url'] + return media_content + + +# return description without html tags +def getDescription(item): + return BeautifulSoup(item, features="html.parser").getText() + + +# print news in json format +def intoJson(item): + return json.dumps({ + 'Title: ': item.title, + 'Date: ': item.published, + 'Link: ': item.link, + 'Description: ': getDescription(item.description), + 'Media_link': checkMediaContent(item) + }) diff --git a/rss_reader/parser.log b/rss_reader/parser.log new file mode 100644 index 0000000..e69de29 diff --git a/rss_reader/requirements.txt b/rss_reader/requirements.txt new file mode 100644 index 0000000..95da44f --- /dev/null +++ b/rss_reader/requirements.txt @@ -0,0 +1,5 @@ +beautifulsoup4==4.8.1 +dominate==2.4.0 +feedparser==5.2.1 +pdfkit==0.6.1 +twine==2.0.0 \ No newline at end of file diff --git a/rss_reader/rss_reader.py b/rss_reader/rss_reader.py new file mode 100644 index 0000000..a9082a5 --- /dev/null +++ b/rss_reader/rss_reader.py @@ -0,0 +1,132 @@ +import argparse +import feedparser +import os +import pdfkit +import random +import dominate +from dominate.tags import * +from datetime import date + +import parse_news +import get_cache +import logger +import version +import converter + + +arguments = argparse.ArgumentParser(description='Pure Python command-line RSS reader') + +arguments.add_argument('source', action='store', help='RSS URL') +arguments.add_argument('--version', action='store_true', help='Print version info') +arguments.add_argument('--json', action='store_true', help='Print result as JSON in stdout') +arguments.add_argument('--verbose', action='store_true', help='Outputs verbose') +arguments.add_argument('--limit', action='store', help='Limit news topics') +arguments.add_argument('--date', action='store', help='Print news from the specified day') +arguments.add_argument('--tohtml', action='store', help='Convert news in html format') +arguments.add_argument('--topdf', action='store', help='Convert news in pdf format') + +args = arguments.parse_args() + +try: + # set object of feedparser library + channel = feedparser.parse(args.source) + print("Feed: ", channel.feed.title, '\n') + + # if no limit - take all news in channel + if not (args.limit): + args.limit = len(channel.entries) + limit = int(args.limit) + + if (args.version): + print(version.VERSION) + + elif (args.verbose): + # Create file if not exist + with open('parser.log', 'r') as f: + for line in f: + print(line) + + elif (args.date): + + if (len(args.date)==8): + print(get_cache.getCache(int(args.date))) + else: + print("Length of date should be equals 8") + + elif (args.tohtml or args.topdf): + + # set object of html converter library + doc = dominate.document(title="HTML document") + random_name = random.randint(1, 120) + + for index, item in enumerate(channel.entries): + if (index == limit): + break + + media_content = parse_news.checkMediaContent(item) + + # create html structure + with doc: + with div(): + h2(item.title) + p("Date: " + item.published) + if (media_content): + img(src=media_content) + p("Description: " + parse_news.getDescription(item.description)) + + if (args.tohtml): + converter.intoHTML(args.tohtml, doc, random_name) + + elif (args.topdf): + converter.intoPDF(args.topdf, doc, random_name) + + else: + + for index, item in enumerate(channel.entries): + if (index == limit): + break + + # create folder for cache + today = date.today().strftime("%Y%m%d") + if not os.path.exists('cache/'): + os.makedirs('cache/') + cache_file = 'cache/' + today + '.txt' + + # cache news + with open(cache_file, 'a', encoding="utf-8") as f: + f.write("Title: " + item.title + '\n' + + "Link: " + item.link + '\n' + + "Description: " + parse_news.getDescription(item.description) + '\n') + + # log news + logger.logging.debug("Title: " + str(item.title)) + logger.logging.debug("Date: " + str(item.published)) + logger.logging.debug("Link: " + str(item.link)) + logger.logging.debug("Description: " + parse_news.getDescription(item.description)) + logger.logging.debug("Links:"+"\n[1]: " + str(item.link) + + "(link)\n[2]: " + str(parse_news.checkMediaContent(item))) + + if (args.json): + print(parse_news.intoJson(item)) + + # print news + print("Title: ", item.title) + print("Date: ", item.published) + print("Link: ", item.link, '\n') + print("Description: ", parse_news.getDescription(item.description), '\n') + print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", + parse_news.checkMediaContent(item), '\n') + +except ValueError as e: + print("--limit or --date argument should be number") + logger.logging.error("ValueError: " + str(e)) + +except AttributeError as e: + print("AttributeError: " + str(e)) + print("Maybe check your source") + logger.logging.error("AttributeError: " + str(e)) + +# fast paste in argument +# source = "https://news.yahoo.com/rss" +# source1 = "https://news.google.com/rss" +# source2 = "https://www.theguardian.com/world/rss" diff --git a/rss/style.css b/rss_reader/style.css similarity index 90% rename from rss/style.css rename to rss_reader/style.css index 6307dca..d5454dc 100644 --- a/rss/style.css +++ b/rss_reader/style.css @@ -23,7 +23,3 @@ div p { body { background-color: black; } -div img{ - width: 100px; - height: 300px; -} \ No newline at end of file diff --git a/rss_reader/version.py b/rss_reader/version.py new file mode 100644 index 0000000..7d5f0d8 --- /dev/null +++ b/rss_reader/version.py @@ -0,0 +1 @@ +VERSION = '0.4.3' \ No newline at end of file diff --git a/setup.py b/setup.py index d0d377c..ef68e63 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,27 @@ from setuptools import setup, find_packages +import os -with open("README.md", "r") as fh: + +this_directory = os.path.abspath(os.path.dirname(__file__)) +with open(path.join(this_directory, 'README.md'), encoding='utf-8') as fh: long_description = fh.read() - + +def get_version(): + basedir = os.path.dirname(__file__) + with open(os.path.join(basedir, 'rss_reader/version.py')) as f: + VERSION = None + exec(f.read()) + return VERSION + raise RuntimeError('No version info found.') + setup( name="rss-reader", - version="1.0", + version=get_version(), packages=find_packages(), - #scripts=[], - install_requires=['beautifulsoup4==4.8.1', 'feedparser==5.2.1'], - author="Ilya Khonenko", + install_requires=['beautifulsoup4','dominate','feedparser','pdfkit','twine','urllib3'], + author="ilya khonenko", author_email="honenkoi@gmail.com", + # url="https://github.com" description="This is rss-reader", long_description = long_description, long_description_content_type="text/markdown", @@ -18,7 +29,6 @@ "Programming Language :: Python :: 3", "Operating System :: OS Independent", ], - # url="https://github.com" keywords="rss reader", python_requires='>=3.8', ) @@ -30,4 +40,3 @@ #(for real) #python -m twine upload dist/* (https://pypi.org by default) #pip install [your-package] - From c769d8e08e3d6b6bbb365053f60ec391605ccf8c Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Tue, 5 Nov 2019 17:37:26 +0300 Subject: [PATCH 07/15] remove __init__.py --- __init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 From de2f66e1260b04ea40ea090ec4941be155e6fcca Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Sat, 9 Nov 2019 16:28:54 +0300 Subject: [PATCH 08/15] Fix all problems. Write code to caching news in database and convert into Html or Pdf format --- .gitignore | 4 +- final_task/README.md | 41 ++- final_task/rss_reader/arg_parser.py | 18 ++ final_task/rss_reader/news_parser.py | 112 ++++++++ final_task/rss_reader/parser.log | 399 +++++++++++++++++++++++++++ final_task/rss_reader/rss_reader.py | 92 ++++++ final_task/setup.py | 35 +++ rss_reader/converter.py | 51 +++- rss_reader/get_cache.py | 78 +++++- rss_reader/logger.py | 24 ++ rss_reader/style.css | 3 + 11 files changed, 833 insertions(+), 24 deletions(-) create mode 100644 final_task/rss_reader/arg_parser.py create mode 100644 final_task/rss_reader/news_parser.py create mode 100644 final_task/rss_reader/parser.log diff --git a/.gitignore b/.gitignore index 5338b7b..07722c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ __pycache__/ .vscode/ -test.py -rss/__pycache__/ -rss/.vscode/ +.idea/ diff --git a/final_task/README.md b/final_task/README.md index 7af281f..0f8e976 100644 --- a/final_task/README.md +++ b/final_task/README.md @@ -1,3 +1,38 @@ -# Your readme here -Some text. -Checkout how to write this file using *markdown*. +# RSS-READER + +### Command-line utility which receives RSS URL and prints results in human-readable format. + +**Example:** +python rss_reader.py https://news.yahoo.com/rss - -limit 1 + +**Output**: + +Feed: Yahoo News - Latest News & Headlines + +Title: Families come from across U.S. to grieve relatives slain in Mexico +Date: Thu, 07 Nov 2019 01:06:45 -0500 +Link: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html + +Description: An American man whose grandchildren were slain in a massacre in Mexico demanded justice on Thursday for other victims of the country's drug war, as relatives gathered from +across the United States for a funeral guarded by heavily armed military. Kenneth Miller lost his daughter-in-law and four grandchildren, all dual citizens, in an ambush on Monday in th +e northern border state of Sonora that killed three mothers and six children. The attack on members of breakaway Mormon communities who settled in Mexico decades ago prompted U.S. Pres +ident Donald Trump to urge Mexico and the United States to "wage war" together on drug cartels. + +Links: +``` +[1]: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html (link) +[2]: http://l.yimg.com/uu/api/res/1.2/rRx_J3xHKYzIQ4EsiCPRTw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/9cbb9d9c38b8bbe10243ebfde0f6db48 +``` + +``` +positional arguments: + source RSS URL + +optional arguments: + -h, --help show this help message and exit + --version Print version info + --json Print result as JSON in stdout + --verbose Outputs verbose status messages + --limit LIMIT Limit news topics if this parameter provided + +``` \ No newline at end of file diff --git a/final_task/rss_reader/arg_parser.py b/final_task/rss_reader/arg_parser.py new file mode 100644 index 0000000..80ad36a --- /dev/null +++ b/final_task/rss_reader/arg_parser.py @@ -0,0 +1,18 @@ +import argparse +import version + +def createArgparser(): + '''Add argument commands''' + arguments = argparse.ArgumentParser(description='Pure Python command-line RSS reader') + + arguments.add_argument('source', type=str, help='RSS URL') + arguments.add_argument('--version', action='version', version=f'{version.VERSION}', + help='Print version info') + arguments.add_argument('--json', action='store_true', help='Print result as JSON in stdout') + arguments.add_argument('--verbose', action='store_true', help='Outputs verbose') + arguments.add_argument('--limit', action='store', type=int, help='Limit news topics') + arguments.add_argument('--date', action='store', help='Print news from the specified day') + arguments.add_argument('--tohtml', action='store', help='Convert news in html format') + arguments.add_argument('--topdf', action='store', help='Convert news in pdf format') + + return arguments.parse_args() \ No newline at end of file diff --git a/final_task/rss_reader/news_parser.py b/final_task/rss_reader/news_parser.py new file mode 100644 index 0000000..8365057 --- /dev/null +++ b/final_task/rss_reader/news_parser.py @@ -0,0 +1,112 @@ +from bs4 import BeautifulSoup +import json +import os +import urllib.request +from datetime import datetime +import time +import sqlite3 +import html + + +def checkMediaContent(item): + '''check if there is media content''' + media_content = '' + if ('media_content' in item.keys()): + media_content = item.media_content[0]['url'] + elif ('media_thumbnail' in item.keys()): + media_content = item.media_thumbnail[0]['url'] + return media_content + + +def getDescription(item): + '''return description without html tags''' + return BeautifulSoup(item, features="html.parser").getText() + + +def intoJson(item): + '''print news in json format''' + return json.dumps({ + 'Title: ': html.unescape(item.title), + 'Date: ': item.published, + 'Link: ': item.link, + 'Description: ': getDescription(item.description), + 'Media_link': checkMediaContent(item) + }) + + +def cacheNews(url, channel): + ''' + 1. connect to database + 2. create table in database + 3. insert news into table + ''' + conn = sqlite3.connect("newsdatabase.db") + cursor = conn.cursor() + + try: + cursor.execute("""CREATE TABLE news + (title text, link text, image text, + description text, pub_date_stamp real, + UNIQUE (title, link, pub_date_stamp)) + """) + except: + print("Table already exists") + + insertNewsIntoTable(channel, cursor) + conn.commit() + cursor.close() + conn.close() + + +def insertNewsIntoTable(channel, cursor): + ''' + 1. fill table with news + 2. convert date into timestamp + 3. create folder with cache images in loop + ''' + for index, item in enumerate(channel.entries): + + descr = getDescription(item.description) + + pub_date = getPublishedDate(item.published) + pub_date_stamp = time.mktime(datetime.strptime(pub_date, '%d %m %Y %H:%M:%S').timetuple()) + + if not os.path.exists('cache_images/'): + os.makedirs('cache_images/') + time_name = datetime.strftime(datetime.now(), "%H%M%S") + str(index) + image_file_name = 'cache_images/' + time_name + '.jpg' + if (checkMediaContent(item)): + urllib.request.urlretrieve(checkMediaContent(item), image_file_name) + + row = (html.unescape(item.title), item.link, descr, os.path.abspath(image_file_name), pub_date_stamp) + try: + cursor.execute("INSERT INTO news VALUES (?,?,?,?,?)", row) + except sqlite3.IntegrityError: + pass + + +def isEmpty(cursor): + ''' + 1. check if table is empty + ''' + cursor.execute("SELECT COUNT(*) FROM news") + exist = cursor.fetchone() + if not(exist[0]): + return True + else: + return False + + +def getPublishedDate(pub_date): + ''' + 1. convert published date into --date argument format + ''' + pub_date = ((pub_date).split(' ')[1:5]) + + month = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04','May':'05','Jun':'06', + 'Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'} + + pub_date[1] = month[pub_date[1]] + pub_date = ' '.join(pub_date) + + return pub_date diff --git a/final_task/rss_reader/parser.log b/final_task/rss_reader/parser.log new file mode 100644 index 0000000..7d6ae67 --- /dev/null +++ b/final_task/rss_reader/parser.log @@ -0,0 +1,399 @@ +ERROR [2019-11-09 00:03:49,421] no such table: news +INFO [2019-11-09 00:04:08,350] Website is working +DEBUG [2019-11-09 00:04:13,168] Title: Impeachment transcript details intrusion by GOP Rep. Gaetz +DEBUG [2019-11-09 00:04:13,168] Date: Fri, 08 Nov 2019 14:24:27 -0500 +DEBUG [2019-11-09 00:04:13,169] Link: https://news.yahoo.com/impeachment-transcript-details-intrusion-by-gop-rep-gaetz-192427892.html +DEBUG [2019-11-09 00:04:13,170] Description: House Intelligence Committee Chairman Adam Schiff did not take kindly to a protest waged by Republican Rep. Matt Gaetz. +DEBUG [2019-11-09 00:04:13,170] Links: +[1]: https://news.yahoo.com/impeachment-transcript-details-intrusion-by-gop-rep-gaetz-192427892.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/p0LFQDFOarpxYzakr0szxQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/dc1729d0-0256-11ea-bdff-89108ee6f919 + +DEBUG [2019-11-09 00:04:13,170] Title: South Korea deports North Koreans who fled after killing 16 +DEBUG [2019-11-09 00:04:13,171] Date: Thu, 07 Nov 2019 20:32:32 -0500 +DEBUG [2019-11-09 00:04:13,171] Link: https://news.yahoo.com/south-korea-deports-north-koreans-070243995.html +DEBUG [2019-11-09 00:04:13,172] Description: In an extremely unusual case, South Korea deported two North Korean fishermen on Thursday after determining they had killed 16 other crew members on their boat and then fled to South Korean waters, Seoul officials said. South Korea has a policy of accepting North Koreans who want to resettle in the South to avoid political oppression and poverty at home. This week's deportations were the first South Korea has carried out of any North Korean who came to the South since the end of the 1950-53 Korean War, according to Seoul's Unification Ministry, which deals with North Korean affairs. +DEBUG [2019-11-09 00:04:13,172] Links: +[1]: https://news.yahoo.com/south-korea-deports-north-koreans-070243995.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/sofRs2pkM3o6YIHAQ1lmgQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/9737049a3c4448d9d28f347f50dadf0f + +DEBUG [2019-11-09 00:04:13,172] Title: Iranian beauty queen wins asylum in Philippines +DEBUG [2019-11-09 00:04:13,172] Date: Fri, 08 Nov 2019 09:19:56 -0500 +DEBUG [2019-11-09 00:04:13,173] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html +DEBUG [2019-11-09 00:04:13,174] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. +DEBUG [2019-11-09 00:04:13,174] Links: +[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg + +DEBUG [2019-11-09 00:04:13,174] Title: Man serving life sentence says it ended once he died, was revived in medical emergency +DEBUG [2019-11-09 00:04:13,174] Date: Thu, 07 Nov 2019 17:43:49 -0500 +DEBUG [2019-11-09 00:04:13,175] Link: https://news.yahoo.com/man-serving-life-sentence-says-194608585.html +DEBUG [2019-11-09 00:04:13,175] Description: An Iowa man convicted of murder was rushed from prison to a hospital where his heart was restarted five times. He claims he should be freed. +DEBUG [2019-11-09 00:04:13,176] Links: +[1]: https://news.yahoo.com/man-serving-life-sentence-says-194608585.html(link) +[2]: http://l2.yimg.com/uu/api/res/1.2/6an.31kqozxWL0y05iSU7Q--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2019-11/54ba34b0-0250-11ea-bdbb-18b75000726e + +DEBUG [2019-11-09 00:04:13,176] Title: Bill Gates addressed his multiple meetings with Jeffrey Epstein: 'I made a mistake in judgment' +DEBUG [2019-11-09 00:04:13,176] Date: Wed, 06 Nov 2019 17:22:49 -0500 +DEBUG [2019-11-09 00:04:13,176] Link: https://news.yahoo.com/bill-gates-addressed-multiple-meetings-222249632.html +DEBUG [2019-11-09 00:04:13,177] Description: Bill Gates addressed his ties to Jeffrey Epstein Wednesday, saying that he "made a mistake in judgement" by associating with the convicted sex offender. +DEBUG [2019-11-09 00:04:13,177] Links: +[1]: https://news.yahoo.com/bill-gates-addressed-multiple-meetings-222249632.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/7RadwILn16dXQ_MIl0z7sw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/business_insider_articles_888/50e2156b9598bbedc0e8df96e877f8a3 + +INFO [2019-11-09 00:07:18,972] Successfull parsing +INFO [2019-11-09 00:08:09,057] Successfull parsing +ERROR [2019-11-09 00:29:22,423] URLError: +INFO [2019-11-09 00:29:37,818] Successfull converting into html +INFO [2019-11-09 00:29:58,662] Successfull converting into pdf +INFO [2019-11-09 00:55:01,092] Website is working +DEBUG [2019-11-09 00:55:06,802] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 00:55:06,802] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 00:55:06,802] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 00:55:06,804] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 00:55:06,804] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +DEBUG [2019-11-09 00:55:06,810] Title: Greta Thunberg shuts down heckler at climate rally +DEBUG [2019-11-09 00:55:06,811] Date: Fri, 08 Nov 2019 15:40:15 -0500 +DEBUG [2019-11-09 00:55:06,811] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html +DEBUG [2019-11-09 00:55:06,812] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. +DEBUG [2019-11-09 00:55:06,823] Links: +[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 + +DEBUG [2019-11-09 00:55:06,841] Title: Families come from across U.S. to grieve relatives slain in Mexico +DEBUG [2019-11-09 00:55:06,842] Date: Thu, 07 Nov 2019 01:06:45 -0500 +DEBUG [2019-11-09 00:55:06,842] Link: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html +DEBUG [2019-11-09 00:55:06,843] Description: An American man whose grandchildren were slain in a massacre in Mexico demanded justice on Thursday for other victims of the country's drug war, as relatives gathered from across the United States for a funeral guarded by heavily armed military. Kenneth Miller lost his daughter-in-law and four grandchildren, all dual citizens, in an ambush on Monday in the northern border state of Sonora that killed three mothers and six children. The attack on members of breakaway Mormon communities who settled in Mexico decades ago prompted U.S. President Donald Trump to urge Mexico and the United States to "wage war" together on drug cartels. +DEBUG [2019-11-09 00:55:06,844] Links: +[1]: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/rRx_J3xHKYzIQ4EsiCPRTw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/9cbb9d9c38b8bbe10243ebfde0f6db48 + +INFO [2019-11-09 01:17:55,483] Website is working +DEBUG [2019-11-09 01:18:00,501] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 01:18:00,502] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 01:18:00,502] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 01:18:00,503] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 01:18:00,503] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +INFO [2019-11-09 01:18:50,287] Website is working +DEBUG [2019-11-09 01:18:54,766] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 01:18:54,767] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 01:18:54,767] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 01:18:54,768] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 01:18:54,768] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +INFO [2019-11-09 01:19:16,963] Website is working +DEBUG [2019-11-09 01:19:21,463] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 01:19:21,463] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 01:19:21,463] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 01:19:21,464] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 01:19:21,464] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +INFO [2019-11-09 01:20:24,404] Website is working +DEBUG [2019-11-09 01:20:28,607] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 01:20:28,607] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 01:20:28,608] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 01:20:28,609] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 01:20:28,609] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +ERROR [2019-11-09 01:25:45,080] no such table: news +INFO [2019-11-09 01:25:45,130] Successfull converting into html +INFO [2019-11-09 01:25:54,605] Website is working +DEBUG [2019-11-09 01:25:59,750] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 01:25:59,750] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 01:25:59,750] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 01:25:59,751] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 01:25:59,752] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +DEBUG [2019-11-09 01:25:59,755] Title: Greta Thunberg shuts down heckler at climate rally +DEBUG [2019-11-09 01:25:59,756] Date: Fri, 08 Nov 2019 15:40:15 -0500 +DEBUG [2019-11-09 01:25:59,756] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html +DEBUG [2019-11-09 01:25:59,757] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. +DEBUG [2019-11-09 01:25:59,757] Links: +[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 + +DEBUG [2019-11-09 01:25:59,762] Title: Suspects linked to New Hampshire couple's deaths found in Mexico +DEBUG [2019-11-09 01:25:59,763] Date: Thu, 07 Nov 2019 18:42:41 -0500 +DEBUG [2019-11-09 01:25:59,763] Link: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html +DEBUG [2019-11-09 01:25:59,765] Description: A man and a woman wanted in connection with the missing New Hampshire couple found buried at a Texas beach will be turned over to the Texas Rangers. +DEBUG [2019-11-09 01:25:59,766] Links: +[1]: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/xwz8.ULZexSl6yl6pIr7Sg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-us/usa_today_news_641/2b1782f8c4fb14398798220447951841 + +DEBUG [2019-11-09 01:25:59,775] Title: Iranian beauty queen wins asylum in Philippines +DEBUG [2019-11-09 01:25:59,776] Date: Fri, 08 Nov 2019 09:19:56 -0500 +DEBUG [2019-11-09 01:25:59,776] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html +DEBUG [2019-11-09 01:25:59,777] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. +DEBUG [2019-11-09 01:25:59,777] Links: +[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg + +DEBUG [2019-11-09 01:25:59,785] Title: Spain court agrees to extradite Venezuelan ex-intelligence chief to USA: EFE +DEBUG [2019-11-09 01:25:59,786] Date: Fri, 08 Nov 2019 14:16:29 -0500 +DEBUG [2019-11-09 01:25:59,786] Link: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html +DEBUG [2019-11-09 01:25:59,787] Description: Spain's High Court has agreed to extradite former Venezuelan intelligence chief Hugo Carvajal to the United States, reversing a previous ruling to deny the extradition request, EFE news agency reported on Friday, citing judicial sources. Carvajal's lawyer told Reuters she had not been notified of any court decision. The former general was arrested by Spanish police in April at the request of U.S. authorities, but Spain's High Court then ruled in September that he should be released and his extradition request denied. +DEBUG [2019-11-09 01:25:59,787] Links: +[1]: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/_TymIShCAQtjIx56GTvg1A--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/33f2a5c1e4a02bed717f72be3dd95826 + +INFO [2019-11-09 01:26:07,686] Website is working +DEBUG [2019-11-09 01:26:12,104] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 01:26:12,104] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 01:26:12,104] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 01:26:12,106] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 01:26:12,106] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +INFO [2019-11-09 01:26:19,464] Successfull converting into html +INFO [2019-11-09 01:28:02,226] Website is working +INFO [2019-11-09 01:28:09,781] Successfull converting into html +INFO [2019-11-09 01:28:59,137] Website is working +DEBUG [2019-11-09 01:29:03,694] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden +DEBUG [2019-11-09 01:29:03,694] Date: Wed, 06 Nov 2019 19:41:52 -0500 +DEBUG [2019-11-09 01:29:03,694] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html +DEBUG [2019-11-09 01:29:03,695] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." +DEBUG [2019-11-09 01:29:03,696] Links: +[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba + +DEBUG [2019-11-09 01:29:03,699] Title: Greta Thunberg shuts down heckler at climate rally +DEBUG [2019-11-09 01:29:03,699] Date: Fri, 08 Nov 2019 15:40:15 -0500 +DEBUG [2019-11-09 01:29:03,700] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html +DEBUG [2019-11-09 01:29:03,700] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. +DEBUG [2019-11-09 01:29:03,701] Links: +[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 + +DEBUG [2019-11-09 01:29:03,708] Title: Suspects linked to New Hampshire couple's deaths found in Mexico +DEBUG [2019-11-09 01:29:03,708] Date: Thu, 07 Nov 2019 18:42:41 -0500 +DEBUG [2019-11-09 01:29:03,708] Link: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html +DEBUG [2019-11-09 01:29:03,709] Description: A man and a woman wanted in connection with the missing New Hampshire couple found buried at a Texas beach will be turned over to the Texas Rangers. +DEBUG [2019-11-09 01:29:03,709] Links: +[1]: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/xwz8.ULZexSl6yl6pIr7Sg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-us/usa_today_news_641/2b1782f8c4fb14398798220447951841 + +DEBUG [2019-11-09 01:29:03,715] Title: Iranian beauty queen wins asylum in Philippines +DEBUG [2019-11-09 01:29:03,715] Date: Fri, 08 Nov 2019 09:19:56 -0500 +DEBUG [2019-11-09 01:29:03,715] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html +DEBUG [2019-11-09 01:29:03,716] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. +DEBUG [2019-11-09 01:29:03,716] Links: +[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg + +DEBUG [2019-11-09 01:29:03,723] Title: Spain court agrees to extradite Venezuelan ex-intelligence chief to USA: EFE +DEBUG [2019-11-09 01:29:03,724] Date: Fri, 08 Nov 2019 14:16:29 -0500 +DEBUG [2019-11-09 01:29:03,724] Link: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html +DEBUG [2019-11-09 01:29:03,725] Description: Spain's High Court has agreed to extradite former Venezuelan intelligence chief Hugo Carvajal to the United States, reversing a previous ruling to deny the extradition request, EFE news agency reported on Friday, citing judicial sources. Carvajal's lawyer told Reuters she had not been notified of any court decision. The former general was arrested by Spanish police in April at the request of U.S. authorities, but Spain's High Court then ruled in September that he should be released and his extradition request denied. +DEBUG [2019-11-09 01:29:03,725] Links: +[1]: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/_TymIShCAQtjIx56GTvg1A--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/33f2a5c1e4a02bed717f72be3dd95826 + +INFO [2019-11-09 01:32:08,975] Website is working +DEBUG [2019-11-09 01:32:13,681] Title: Greta Thunberg shuts down heckler at climate rally +DEBUG [2019-11-09 01:32:13,681] Date: Fri, 08 Nov 2019 15:40:15 -0500 +DEBUG [2019-11-09 01:32:13,681] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html +DEBUG [2019-11-09 01:32:13,682] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. +DEBUG [2019-11-09 01:32:13,682] Links: +[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 + +DEBUG [2019-11-09 01:32:13,688] Title: Rockets hit Iraq base with US troops; no word on casualties +DEBUG [2019-11-09 01:32:13,688] Date: Fri, 08 Nov 2019 14:53:04 -0500 +DEBUG [2019-11-09 01:32:13,688] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html +DEBUG [2019-11-09 01:32:13,689] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. +DEBUG [2019-11-09 01:32:13,689] Links: +[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 + +DEBUG [2019-11-09 01:32:13,697] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives +DEBUG [2019-11-09 01:32:13,698] Date: Wed, 06 Nov 2019 21:45:35 -0500 +DEBUG [2019-11-09 01:32:13,698] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html +DEBUG [2019-11-09 01:32:13,699] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. +DEBUG [2019-11-09 01:32:13,699] Links: +[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) +[2]: + +DEBUG [2019-11-09 01:32:13,707] Title: Iranian beauty queen wins asylum in Philippines +DEBUG [2019-11-09 01:32:13,708] Date: Fri, 08 Nov 2019 09:19:56 -0500 +DEBUG [2019-11-09 01:32:13,708] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html +DEBUG [2019-11-09 01:32:13,709] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. +DEBUG [2019-11-09 01:32:13,709] Links: +[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg + +DEBUG [2019-11-09 01:32:13,807] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars +DEBUG [2019-11-09 01:32:13,807] Date: Thu, 07 Nov 2019 16:11:32 -0500 +DEBUG [2019-11-09 01:32:13,807] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html +DEBUG [2019-11-09 01:32:13,808] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. +DEBUG [2019-11-09 01:32:13,809] Links: +[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 + +INFO [2019-11-09 01:35:58,915] Website is working +DEBUG [2019-11-09 01:36:04,288] Title: Greta Thunberg shuts down heckler at climate rally +DEBUG [2019-11-09 01:36:04,289] Date: Fri, 08 Nov 2019 15:40:15 -0500 +DEBUG [2019-11-09 01:36:04,289] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html +DEBUG [2019-11-09 01:36:04,360] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. +DEBUG [2019-11-09 01:36:04,361] Links: +[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 + +DEBUG [2019-11-09 01:36:04,380] Title: Rockets hit Iraq base with US troops; no word on casualties +DEBUG [2019-11-09 01:36:04,380] Date: Fri, 08 Nov 2019 14:53:04 -0500 +DEBUG [2019-11-09 01:36:04,381] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html +DEBUG [2019-11-09 01:36:04,382] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. +DEBUG [2019-11-09 01:36:04,382] Links: +[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 + +DEBUG [2019-11-09 01:36:04,389] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives +DEBUG [2019-11-09 01:36:04,389] Date: Wed, 06 Nov 2019 21:45:35 -0500 +DEBUG [2019-11-09 01:36:04,389] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html +DEBUG [2019-11-09 01:36:04,390] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. +DEBUG [2019-11-09 01:36:04,391] Links: +[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) +[2]: + +DEBUG [2019-11-09 01:36:04,424] Title: Iranian beauty queen wins asylum in Philippines +DEBUG [2019-11-09 01:36:04,424] Date: Fri, 08 Nov 2019 09:19:56 -0500 +DEBUG [2019-11-09 01:36:04,425] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html +DEBUG [2019-11-09 01:36:04,426] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. +DEBUG [2019-11-09 01:36:04,426] Links: +[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg + +DEBUG [2019-11-09 01:36:04,434] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars +DEBUG [2019-11-09 01:36:04,434] Date: Thu, 07 Nov 2019 16:11:32 -0500 +DEBUG [2019-11-09 01:36:04,434] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html +DEBUG [2019-11-09 01:36:04,435] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. +DEBUG [2019-11-09 01:36:04,436] Links: +[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 + +INFO [2019-11-09 01:38:02,188] Website is working +DEBUG [2019-11-09 01:38:06,738] Title: Greta Thunberg shuts down heckler at climate rally +DEBUG [2019-11-09 01:38:06,738] Date: Fri, 08 Nov 2019 15:40:15 -0500 +DEBUG [2019-11-09 01:38:06,738] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html +DEBUG [2019-11-09 01:38:06,739] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. +DEBUG [2019-11-09 01:38:06,740] Links: +[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 + +DEBUG [2019-11-09 01:38:06,745] Title: Rockets hit Iraq base with US troops; no word on casualties +DEBUG [2019-11-09 01:38:06,746] Date: Fri, 08 Nov 2019 14:53:04 -0500 +DEBUG [2019-11-09 01:38:06,746] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html +DEBUG [2019-11-09 01:38:06,747] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. +DEBUG [2019-11-09 01:38:06,748] Links: +[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 + +DEBUG [2019-11-09 01:38:06,754] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives +DEBUG [2019-11-09 01:38:06,754] Date: Wed, 06 Nov 2019 21:45:35 -0500 +DEBUG [2019-11-09 01:38:06,755] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html +DEBUG [2019-11-09 01:38:06,756] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. +DEBUG [2019-11-09 01:38:06,756] Links: +[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) +[2]: + +DEBUG [2019-11-09 01:38:06,761] Title: Iranian beauty queen wins asylum in Philippines +DEBUG [2019-11-09 01:38:06,762] Date: Fri, 08 Nov 2019 09:19:56 -0500 +DEBUG [2019-11-09 01:38:06,762] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html +DEBUG [2019-11-09 01:38:06,763] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. +DEBUG [2019-11-09 01:38:06,763] Links: +[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg + +DEBUG [2019-11-09 01:38:06,787] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars +DEBUG [2019-11-09 01:38:06,787] Date: Thu, 07 Nov 2019 16:11:32 -0500 +DEBUG [2019-11-09 01:38:06,787] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html +DEBUG [2019-11-09 01:38:06,788] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. +DEBUG [2019-11-09 01:38:06,789] Links: +[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 + +INFO [2019-11-09 01:38:35,554] Website is working +DEBUG [2019-11-09 01:38:40,072] Title: Greta Thunberg shuts down heckler at climate rally +DEBUG [2019-11-09 01:38:40,072] Date: Fri, 08 Nov 2019 15:40:15 -0500 +DEBUG [2019-11-09 01:38:40,073] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html +DEBUG [2019-11-09 01:38:40,074] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. +DEBUG [2019-11-09 01:38:40,074] Links: +[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 + +DEBUG [2019-11-09 01:38:40,082] Title: Rockets hit Iraq base with US troops; no word on casualties +DEBUG [2019-11-09 01:38:40,082] Date: Fri, 08 Nov 2019 14:53:04 -0500 +DEBUG [2019-11-09 01:38:40,082] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html +DEBUG [2019-11-09 01:38:40,083] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. +DEBUG [2019-11-09 01:38:40,084] Links: +[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 + +DEBUG [2019-11-09 01:38:40,090] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives +DEBUG [2019-11-09 01:38:40,091] Date: Wed, 06 Nov 2019 21:45:35 -0500 +DEBUG [2019-11-09 01:38:40,091] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html +DEBUG [2019-11-09 01:38:40,092] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. +DEBUG [2019-11-09 01:38:40,092] Links: +[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) +[2]: + +DEBUG [2019-11-09 01:38:40,102] Title: Iranian beauty queen wins asylum in Philippines +DEBUG [2019-11-09 01:38:40,103] Date: Fri, 08 Nov 2019 09:19:56 -0500 +DEBUG [2019-11-09 01:38:40,103] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html +DEBUG [2019-11-09 01:38:40,104] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. +DEBUG [2019-11-09 01:38:40,105] Links: +[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg + +DEBUG [2019-11-09 01:38:40,118] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars +DEBUG [2019-11-09 01:38:40,118] Date: Thu, 07 Nov 2019 16:11:32 -0500 +DEBUG [2019-11-09 01:38:40,119] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html +DEBUG [2019-11-09 01:38:40,120] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. +DEBUG [2019-11-09 01:38:40,120] Links: +[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 + +DEBUG [2019-11-09 01:38:40,133] Title: Jim Jordan to be moved to House Intelligence Committee +DEBUG [2019-11-09 01:38:40,133] Date: Fri, 08 Nov 2019 06:43:37 -0500 +DEBUG [2019-11-09 01:38:40,134] Link: https://news.yahoo.com/jim-jordan-moved-house-intelligence-090813170.html +DEBUG [2019-11-09 01:38:40,135] Description: Jordan will replace Congressman Rick Crawford on the House Intelligence Committee +DEBUG [2019-11-09 01:38:40,135] Links: +[1]: https://news.yahoo.com/jim-jordan-moved-house-intelligence-090813170.html(link) +[2]: http://l2.yimg.com/uu/api/res/1.2/P1oiZSR9PCdnaNHZwQ4k1g--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/cbs_news_897/b702b30415bfa6f67827761aa3e6a22c + +DEBUG [2019-11-09 01:38:40,164] Title: Researchers didn't think humans attacked woolly mammoths – until they uncovered a trap in Mexico +DEBUG [2019-11-09 01:38:40,164] Date: Fri, 08 Nov 2019 12:57:37 -0500 +DEBUG [2019-11-09 01:38:40,164] Link: https://news.yahoo.com/researchers-didnt-think-humans-attacked-175224059.html +DEBUG [2019-11-09 01:38:40,165] Description: Woolly mammoth bones found in Mexico prove that hunters actually attacked the mammal, instead of waiting for them to die +DEBUG [2019-11-09 01:38:40,165] Links: +[1]: https://news.yahoo.com/researchers-didnt-think-humans-attacked-175224059.html(link) +[2]: http://l.yimg.com/uu/api/res/1.2/QudPZtOUjqY6CTO76EmojQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-us/usa_today_news_641/7b2a450e11e613f76319fe6a3451ce25 + +INFO [2019-11-09 01:45:28,134] Website is working +INFO [2019-11-09 01:45:43,131] Successfull converting into html +INFO [2019-11-09 01:47:14,912] Website is working +INFO [2019-11-09 01:47:21,471] Successfull converting into html +INFO [2019-11-09 16:24:13,687] Website is working +DEBUG [2019-11-09 16:24:14,825] Title: Mulvaney asks to join lawsuit over conflicting demands for impeachment testimony - The Washington Post +DEBUG [2019-11-09 16:24:14,826] Date: Sat, 09 Nov 2019 10:36:00 GMT +DEBUG [2019-11-09 16:24:14,826] Link: https://news.google.com/__i/rss/rd/articles/CBMifWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkv0gGMAWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkvP291dHB1dFR5cGU9YW1w?oc=5 +DEBUG [2019-11-09 16:24:14,829] Description: Mulvaney asks to join lawsuit over conflicting demands for impeachment testimony  The Washington PostTrump says he had a second Ukraine call  CBS NewsIvanka Trump: Whistleblower's ID irrelevant to impeachment  POLITICOTrump’s whistleblower attacks set a dangerous precedent  The Washington PostGOP can make a Senate impeachment trial just as painful for Democrats as it will be for Trump  Washington ExaminerView full coverage on Google News +DEBUG [2019-11-09 16:24:14,829] Links: +[1]: https://news.google.com/__i/rss/rd/articles/CBMifWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkv0gGMAWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkvP291dHB1dFR5cGU9YW1w?oc=5(link) +[2]: + +INFO [2019-11-09 16:24:42,961] Website is working +INFO [2019-11-09 16:24:43,917] Successfull converting into html diff --git a/final_task/rss_reader/rss_reader.py b/final_task/rss_reader/rss_reader.py index e69de29..0631cd3 100644 --- a/final_task/rss_reader/rss_reader.py +++ b/final_task/rss_reader/rss_reader.py @@ -0,0 +1,92 @@ +import arg_parser +import feedparser +import sys +from urllib.request import Request, urlopen +from urllib.error import URLError, HTTPError +import dominate +import html + +import news_parser +import logger +import get_cache +import converter + + +def main(channel, limit): + ''' + 1. cache news + 2. create html or pdf document + 3. or print in stdout news in json or normal format + ''' + news_parser.cacheNews(args.source, channel) + + if (args.tohtml or args.topdf): + html_document = dominate.document(title="HTML document") + converter.createHtmlStructure(channel, limit, html_document, args.tohtml, args.topdf) + else: + for index, item in enumerate(channel.entries): + if (index == limit): + break + logger.createLogs(item) + + if (args.json): + print(news_parser.intoJson(item)) + else: + print("\nTitle: ", html.unescape(item.title)) + print("Date: ", item.published) + print("Link: ", item.link, '\n') + print("Description: ", news_parser.getDescription(item.description), '\n') + print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", + news_parser.checkMediaContent(item), '\n') + + +def checkArguments(): + '''Check if in argument are --version or --help''' + if ('--version' in sys.argv or '--help' in sys.argv or '-h' in sys.argv): + return False + else: + return True + + +def checkConnection(source): + '''Check connection to server''' + source = Request(source) + try: + response = urlopen(source) + except HTTPError as e: + print('The server couldn\'t fulfill the request.') + print('Error code: ', e.code) + logger.logging.error("HTTPError: " + str(e)) + sys.exit() + except URLError as e: + print('Failed to reach a server.') + print('Reason: ', e.reason) + logger.logging.error("URLError: " + str(e)) + sys.exit() + else: + logger.logging.info('Website is working') + + +if __name__ == "__main__": + + args = arg_parser.createArgparser() + + if (args.verbose): + logger.makeVerbose() + + if (args.date): + get_cache.convertCache(args.source, args.date, args.limit, args.tohtml, args.topdf) + else: + if (checkArguments()): + checkConnection(args.source) + # set object of feedparser library + channel = feedparser.parse(args.source) + print("Feed: ", channel.feed.title, '\n') + limit = args.limit or len(channel.entries) + + main(channel, limit) + +# for faste paste +# python rss_reader.py https://news.yahoo.com/rss +# python rss_reader.py https://news.google.com/rss +# python rss_reader.py https://www.theguardian.com/world/rss diff --git a/final_task/setup.py b/final_task/setup.py index e69de29..250a1f5 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -0,0 +1,35 @@ +from setuptools import setup, find_packages +import os +from rss_reader import version + +this_directory = os.path.abspath(os.path.dirname(__file__)) +with open(path.join(this_directory, 'README.md'), encoding='utf-8') as fh: + long_description = fh.read() + + +setup( + name="rss-reader", + version=version.VERSION, + packages=find_packages(), + install_requires=['beautifulsoup4','dominate','feedparser','pdfkit','twine','urllib3'], + author="ilya khonenko", + author_email="honenkoi@gmail.com", + # url="https://github.com" + description="This is rss-reader", + long_description = long_description, + long_description_content_type="text/markdown", + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + keywords="rss reader", + python_requires='>=3.8', +) + +#python setup.py sdist bdist_wheel +#(for testing) +#python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* +#python -m pip install --index-url https://test.pypi.org/simple/ --no-deps rss-reader (in venv) +#(for real) +#python -m twine upload dist/* (https://pypi.org by default) +#pip install [your-package] \ No newline at end of file diff --git a/rss_reader/converter.py b/rss_reader/converter.py index a0c43b6..f1877be 100644 --- a/rss_reader/converter.py +++ b/rss_reader/converter.py @@ -1,26 +1,63 @@ import os import pdfkit +from dominate.tags import div, h2, img, p, link +from datetime import datetime +import news_parser +import logger -def intoHTML(html_path, doc, random_name): +def createHtmlStructure(channel, limit, html_document, html_path, pdf_path): + ''' + 1. in loop create html structure + 2. create html document or convert html structure into pdf + ''' + with html_document.head: + styles = os.path.abspath('style.css') + link(rel='stylesheet', href=styles) + for index, item in enumerate(channel.entries): + if (index == limit): + break + with html_document: + with div(): + h2("Title: " + item.title) + p("Link: " + item.link) + media_content = news_parser.checkMediaContent(item) + if (media_content): + img(src=media_content) + p("Description: " + news_parser.getDescription(item.description)) + + if (html_path): + intoHTML(html_document, html_path) + else: + intoPDF(html_document, pdf_path) + + + +def intoHTML(html_document, html_path): ''' 1. create folder with html file - 2. write html structure + 2. write html structure in file ''' if not os.path.exists(html_path): os.makedirs(html_path) - html_file = html_path + str(random_name) + '.html' + time_name = datetime.strftime(datetime.now(), "%H%M%S") + html_file = html_path + '/' + time_name + '.html' with open(html_file, 'w') as f: - f.write(str(doc)) + f.write(str(html_document)) + logger.logging.info("Successfull converting into html") -def intoPDF(pdf_path, doc, random_name): + +def intoPDF(doc, pdf_path): ''' 1. create folder with pdf file 2. convert html into pdf ''' if not os.path.exists(pdf_path): os.makedirs(pdf_path) - pdf_file = pdf_path + str(random_name) + '.pdf' - pdfkit.from_string(str(doc), pdf_file) \ No newline at end of file + time_name = datetime.strftime(datetime.now(), "%H%M%S") + pdf_file = pdf_path + '/' + time_name + '.pdf' + + pdfkit.from_string(str(doc), pdf_file) + logger.logging.info("Successfull converting into pdf") diff --git a/rss_reader/get_cache.py b/rss_reader/get_cache.py index 9922ab4..9975bb3 100644 --- a/rss_reader/get_cache.py +++ b/rss_reader/get_cache.py @@ -1,18 +1,74 @@ -import os +import dominate +from dominate.tags import div, h2, img, p, link +import sqlite3 +from datetime import datetime +import time + import logger +import converter + + +def dateToStamp(arg_date): + ''' + 1. convert --date into timestamp + ''' + arg_date = str(arg_date) + arg_date = time.mktime(datetime.strptime(arg_date, '%Y%m%d').timetuple()) + return arg_date -# print cached news -def getCache(arg_date): +def convertCache(url, arg_date, limit, html_path, pdf_path): ''' - 1. create file with cache news and write it - 3. or raise error + 1. connect to database + 2. select from table news with published date equals --date + 3. convert news into html or pdf if there are --tohtml or --topdf arguments + 3. or print news in stdout ''' + conn = sqlite3.connect("newsdatabase.db") + cursor = conn.cursor() + try: - cache_on_date = 'cache/' + str(arg_date) + '.txt' - with open(cache_on_date, 'r', encoding='utf-8') as f: - return f.read() + cursor.execute("SELECT title, link, description, image FROM news WHERE pub_date_stamp >= ? and pub_date_stamp < ? ", + (dateToStamp(arg_date), dateToStamp(int(arg_date) + 1))) + except sqlite3.OperationalError as e: + print("No such table") + logger.logging.error(str(e)) - except FileNotFoundError as e: - logger.logging.error("FileNotFoundError: " + str(e)) - print("No such file. Please first parse news") + records = cursor.fetchall() + + if (html_path is None and pdf_path is None): + for index, row in enumerate(records): + if(limit and index == limit): + break + print("\nTitle: ", row[0]) + print("Link: ", row[1], '\n') + print("Description: ", row[2], '\n') + else: + createHtmlStructure(records, limit, html_path, pdf_path) + + cursor.close() + conn.close() + + +def createHtmlStructure(records, limit, html_path, pdf_path): + ''' + 1. in loop create html structure + 2. create html document or convert html structure into pdf + ''' + html_document = dominate.document(title="HTML document") + with html_document.head: + link(rel='stylesheet', href='style.css') + for index, row in enumerate(records): + if(limit and index==limit): + break + with html_document: + with div(): + h2("Title: " + row[0]) + p("Link: " + row[1]) + if (row[2]): + img(src=row[2]) + p("Description: " + row[3]) + if (html_path): + converter.intoHTML(html_document, html_path) + elif (pdf_path): + converter.intoPDF(html_document, pdf_path) \ No newline at end of file diff --git a/rss_reader/logger.py b/rss_reader/logger.py index 01f8db3..8d1b012 100644 --- a/rss_reader/logger.py +++ b/rss_reader/logger.py @@ -1,7 +1,31 @@ import logging +import news_parser +import sys # Set basic configs for logging +stdoutHandler = logging.StreamHandler(sys.stdout) fileHandler = logging.FileHandler("parser.log", "a", encoding="utf-8") logging.basicConfig(format=u'%(levelname)-8s [%(asctime)s] %(message)s', level=logging.DEBUG, handlers=[fileHandler]) + + +def makeVerbose(): + ''' + 1. print logs in stdout if there is --verbose argument + ''' + stderrLogger=logging.StreamHandler() + stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) + logging.getLogger().addHandler(stderrLogger) + + +def createLogs(item): + ''' + 1. log news in log file + ''' + logging.debug("Title: " + str(item.title)) + logging.debug("Date: " + str(item.published)) + logging.debug("Link: " + str(item.link)) + logging.debug("Description: " + news_parser.getDescription(item.description)) + logging.debug("Links:"+"\n[1]: " + str(item.link) + + "(link)\n[2]: " + str(news_parser.checkMediaContent(item)) + '\n') \ No newline at end of file diff --git a/rss_reader/style.css b/rss_reader/style.css index d5454dc..13bfecd 100644 --- a/rss_reader/style.css +++ b/rss_reader/style.css @@ -23,3 +23,6 @@ div p { body { background-color: black; } +div img { + width: 150px; +} \ No newline at end of file From 9cfc3eaf127ae7e58066e85acf3be6eb4869db57 Mon Sep 17 00:00:00 2001 From: kingofmidas <38670030+kingofmidas@users.noreply.github.com> Date: Tue, 5 Nov 2019 18:36:27 +0300 Subject: [PATCH 09/15] Update README.md --- final_task/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/final_task/README.md b/final_task/README.md index 0f8e976..4994e71 100644 --- a/final_task/README.md +++ b/final_task/README.md @@ -34,5 +34,4 @@ optional arguments: --json Print result as JSON in stdout --verbose Outputs verbose status messages --limit LIMIT Limit news topics if this parameter provided - ``` \ No newline at end of file From e3a6bfe8ff84d5039f1bd4cea3607850647f580a Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Sat, 9 Nov 2019 18:01:06 +0300 Subject: [PATCH 10/15] Delete unnecessary files --- final_task/rss_reader/parser.log | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/final_task/rss_reader/parser.log b/final_task/rss_reader/parser.log index 7d6ae67..33bdcd3 100644 --- a/final_task/rss_reader/parser.log +++ b/final_task/rss_reader/parser.log @@ -397,3 +397,20 @@ DEBUG [2019-11-09 16:24:14,829] Links: INFO [2019-11-09 16:24:42,961] Website is working INFO [2019-11-09 16:24:43,917] Successfull converting into html +INFO [2019-11-09 17:47:44,450] Website is working +DEBUG [2019-11-09 17:47:52,644] Title: Iran says case open on ex-FBI agent missing there on CIA job +DEBUG [2019-11-09 17:47:52,644] Date: Sat, 09 Nov 2019 03:47:16 -0500 +DEBUG [2019-11-09 17:47:52,644] Link: https://news.yahoo.com/iran-says-case-open-ex-070518919.html +DEBUG [2019-11-09 17:47:52,645] Description: Iran is acknowledging for the first time it has an open case before its Revolutionary Court over the 2007 disappearance of a former FBI agent on an unauthorized CIA mission to the country, renewing questions over what happened to him. In a filing to the United Nations, Iran said the case over Robert Levinson was "on going," without elaborating. +DEBUG [2019-11-09 17:47:52,645] Links: +[1]: https://news.yahoo.com/iran-says-case-open-ex-070518919.html(link) +[2]: http://l2.yimg.com/uu/api/res/1.2/pRZD03lq5AKphL6noxcB1g--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d83a715f4d3ea8efbb2129040f542c4a + +DEBUG [2019-11-09 17:47:52,651] Title: Huge Hong Kong rally after student dies and lawmakers arrested +DEBUG [2019-11-09 17:47:52,651] Date: Fri, 08 Nov 2019 19:01:45 -0500 +DEBUG [2019-11-09 17:47:52,651] Link: https://news.yahoo.com/hong-kong-pro-democracy-lawmakers-arrested-tensions-soar-060439204.html +DEBUG [2019-11-09 17:47:52,653] Description: Tens of thousands of Hong Kongers packed into a park Saturday night to mourn a student who died during recent clashes as police arrested a group of pro-democracy lawmakers, deepening the city's political crisis. The international finance hub has been upended by five months of huge and increasingly violent pro-democracy protests, but Beijing has refused to give in to most of the movement's demands. Tensions have soared since the death on Friday of Alex Chow, 22, who succumbed to head injuries sustained during a fall as police skirmished with demonstrators inside a car park last weekend. +DEBUG [2019-11-09 17:47:52,653] Links: +[1]: https://news.yahoo.com/hong-kong-pro-democracy-lawmakers-arrested-tensions-soar-060439204.html(link) +[2]: http://l1.yimg.com/uu/api/res/1.2/Cigo.wzpuf0tmOWdzOr1VA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/574f0ffc2ba7be5934fcc6cdef859af0ee007aa3.jpg + From ac18443ff7ba4f8358ce9c3fba8600553bf94fa5 Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Sat, 9 Nov 2019 18:53:06 +0300 Subject: [PATCH 11/15] Redesigned git and rebase commits --- README.md | 53 ++++--- .../rss_reader}/__init__.py | 0 .../rss_reader}/converter.py | 3 +- .../rss_reader}/get_cache.py | 0 .../rss_reader}/logger.py | 1 + final_task/rss_reader/requirements.txt | 5 + .../rss_reader}/style.css | 0 final_task/rss_reader/version.py | 1 + final_task/setup.py | 9 +- rss_reader/parse_news.py | 28 ---- rss_reader/parser.log | 0 rss_reader/requirements.txt | 5 - rss_reader/rss_reader.py | 132 ------------------ rss_reader/version.py | 1 - setup.py | 42 ------ 15 files changed, 37 insertions(+), 243 deletions(-) rename {rss_reader => final_task/rss_reader}/__init__.py (100%) rename {rss_reader => final_task/rss_reader}/converter.py (99%) rename {rss_reader => final_task/rss_reader}/get_cache.py (100%) rename {rss_reader => final_task/rss_reader}/logger.py (99%) rename {rss_reader => final_task/rss_reader}/style.css (100%) create mode 100644 final_task/rss_reader/version.py delete mode 100644 rss_reader/parse_news.py delete mode 100644 rss_reader/parser.log delete mode 100644 rss_reader/requirements.txt delete mode 100644 rss_reader/rss_reader.py delete mode 100644 rss_reader/version.py delete mode 100644 setup.py diff --git a/README.md b/README.md index d6f410c..714e84c 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,33 @@ -#RSS-READER +# FinalTaskRssReader +For final task pull requests. -###Command-line utility which receives RSS URL and prints results in human-readable format. -**Example:** -python rss_reader.py https://news.yahoo.com/rss - -limit LIMIT 1 +## How to create a pull request -**Output**: -Feed: Yahoo News - Latest News & Headlines +1. Create github account. *Preferrably using email you used when registerer on this course* +2. Fork this repository. ('Fork' button at the top right of this repository page) +3. Open the page of your *new repository* that was created when you forked this repo. +4. Press button clone or download at the middle right of the page and CTRL-C the url. +5. On your machine go to the directory you want. +6. Depending on the OS you are working with, open GitBash(Windows)/Command Line or Terminal(Linux) there +7. Use command `git clone ` + +Congrats! You have successfully forked our repository. -Title: Families come from across U.S. to grieve relatives slain in Mexico -Date: Thu, 07 Nov 2019 01:06:45 -0500 -Link: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html -Description: An American man whose grandchildren were slain in a massacre in Mexico demanded justice on Thursday for other victims of the country's drug war, as relatives gathered from -across the United States for a funeral guarded by heavily armed military. Kenneth Miller lost his daughter-in-law and four grandchildren, all dual citizens, in an ambush on Monday in th -e northern border state of Sonora that killed three mothers and six children. The attack on members of breakaway Mormon communities who settled in Mexico decades ago prompted U.S. Pres -ident Donald Trump to urge Mexico and the United States to "wage war" together on drug cartels. +## Additional project structure requirements -Links: -``` -[1]: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html (link) -[2]: http://l.yimg.com/uu/api/res/1.2/rRx_J3xHKYzIQ4EsiCPRTw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/9cbb9d9c38b8bbe10243ebfde0f6db48 -``` +1. `setup.py` file for setuptools *must* be in the root of `final_task` folder. Use `setup.py` that is already there. (that means path to this file must end with `final_task/setup.py` ) +2. Entry point to your application, aka its main module *must* be named as `rss_reader.py` . Use `rss_reader.py` that is already in `rss_reader` folder. +3. You should describe how does your project work, how to launch it and etc in README.md in the `final_task/README.md` file. +4. If you used any non-standart libraries they must be listed in `rss_reader/requirements.txt` file. +5. All unit test files should be in separate folder called `tests`. -**positional arguments:** - - -source RSS URL -**optional arguments:** - - -help Show this help message and exit - - -version Print version info - - -json Print result as JSON in stdout - - -verbose Outputs verbose status messages - - -limit LIMIT Limit news topics if this parameter provided - - -date DATE Print news from the specified day - - -tohtml Convert news in html format - - -topdf Convert news in pdf format +## Pull request requirements(!!!) +1. When creating pull request make sure that `target branch` is `master` on OUR repo, not yours. +2. Pull request name *MUST* be in format: `YourFirstName_YourLastName_EmailYouUsedWhileRegisteringOnThisCourse` +3. Pull request which have any other name format, or invalid e-mail *will be ignored completely until you fix it*. So make sure you specified correct e-mail. +4. In pull request description specify your current iteration. You also can add there any other info you want us to know before we start code review. +5. *Pull request must NOT contain any .pyc files, any virtual environment files/folders, any IDE technical files*. \ No newline at end of file diff --git a/rss_reader/__init__.py b/final_task/rss_reader/__init__.py similarity index 100% rename from rss_reader/__init__.py rename to final_task/rss_reader/__init__.py diff --git a/rss_reader/converter.py b/final_task/rss_reader/converter.py similarity index 99% rename from rss_reader/converter.py rename to final_task/rss_reader/converter.py index f1877be..a441d58 100644 --- a/rss_reader/converter.py +++ b/final_task/rss_reader/converter.py @@ -6,6 +6,7 @@ import news_parser import logger + def createHtmlStructure(channel, limit, html_document, html_path, pdf_path): ''' 1. in loop create html structure @@ -32,7 +33,6 @@ def createHtmlStructure(channel, limit, html_document, html_path, pdf_path): intoPDF(html_document, pdf_path) - def intoHTML(html_document, html_path): ''' 1. create folder with html file @@ -48,7 +48,6 @@ def intoHTML(html_document, html_path): logger.logging.info("Successfull converting into html") - def intoPDF(doc, pdf_path): ''' 1. create folder with pdf file diff --git a/rss_reader/get_cache.py b/final_task/rss_reader/get_cache.py similarity index 100% rename from rss_reader/get_cache.py rename to final_task/rss_reader/get_cache.py diff --git a/rss_reader/logger.py b/final_task/rss_reader/logger.py similarity index 99% rename from rss_reader/logger.py rename to final_task/rss_reader/logger.py index 8d1b012..2180ec8 100644 --- a/rss_reader/logger.py +++ b/final_task/rss_reader/logger.py @@ -2,6 +2,7 @@ import news_parser import sys + # Set basic configs for logging stdoutHandler = logging.StreamHandler(sys.stdout) fileHandler = logging.FileHandler("parser.log", "a", encoding="utf-8") diff --git a/final_task/rss_reader/requirements.txt b/final_task/rss_reader/requirements.txt index e69de29..95da44f 100644 --- a/final_task/rss_reader/requirements.txt +++ b/final_task/rss_reader/requirements.txt @@ -0,0 +1,5 @@ +beautifulsoup4==4.8.1 +dominate==2.4.0 +feedparser==5.2.1 +pdfkit==0.6.1 +twine==2.0.0 \ No newline at end of file diff --git a/rss_reader/style.css b/final_task/rss_reader/style.css similarity index 100% rename from rss_reader/style.css rename to final_task/rss_reader/style.css diff --git a/final_task/rss_reader/version.py b/final_task/rss_reader/version.py new file mode 100644 index 0000000..b553b53 --- /dev/null +++ b/final_task/rss_reader/version.py @@ -0,0 +1 @@ +VERSION = '0.4' \ No newline at end of file diff --git a/final_task/setup.py b/final_task/setup.py index 250a1f5..86a2e13 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -2,14 +2,15 @@ import os from rss_reader import version + this_directory = os.path.abspath(os.path.dirname(__file__)) -with open(path.join(this_directory, 'README.md'), encoding='utf-8') as fh: +with open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as fh: long_description = fh.read() setup( name="rss-reader", - version=version.VERSION, + version=get_version(), packages=find_packages(), install_requires=['beautifulsoup4','dominate','feedparser','pdfkit','twine','urllib3'], author="ilya khonenko", @@ -29,7 +30,7 @@ #python setup.py sdist bdist_wheel #(for testing) #python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* -#python -m pip install --index-url https://test.pypi.org/simple/ --no-deps rss-reader (in venv) +#python -m pip install --index-url https://test.pypi.org/simple/ --no-deps ilyakhonenko (in venv) #(for real) #python -m twine upload dist/* (https://pypi.org by default) -#pip install [your-package] \ No newline at end of file +#pip install [your-package] diff --git a/rss_reader/parse_news.py b/rss_reader/parse_news.py deleted file mode 100644 index e470a69..0000000 --- a/rss_reader/parse_news.py +++ /dev/null @@ -1,28 +0,0 @@ -from bs4 import BeautifulSoup -import feedparser -import json - -# check if there is media content -def checkMediaContent(item): - media_content = '' - if ('media_content' in item.keys()): - media_content = item.media_content[0]['url'] - elif ('media_thumbnail' in item.keys()): - media_content = item.media_thumbnail[0]['url'] - return media_content - - -# return description without html tags -def getDescription(item): - return BeautifulSoup(item, features="html.parser").getText() - - -# print news in json format -def intoJson(item): - return json.dumps({ - 'Title: ': item.title, - 'Date: ': item.published, - 'Link: ': item.link, - 'Description: ': getDescription(item.description), - 'Media_link': checkMediaContent(item) - }) diff --git a/rss_reader/parser.log b/rss_reader/parser.log deleted file mode 100644 index e69de29..0000000 diff --git a/rss_reader/requirements.txt b/rss_reader/requirements.txt deleted file mode 100644 index 95da44f..0000000 --- a/rss_reader/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -beautifulsoup4==4.8.1 -dominate==2.4.0 -feedparser==5.2.1 -pdfkit==0.6.1 -twine==2.0.0 \ No newline at end of file diff --git a/rss_reader/rss_reader.py b/rss_reader/rss_reader.py deleted file mode 100644 index a9082a5..0000000 --- a/rss_reader/rss_reader.py +++ /dev/null @@ -1,132 +0,0 @@ -import argparse -import feedparser -import os -import pdfkit -import random -import dominate -from dominate.tags import * -from datetime import date - -import parse_news -import get_cache -import logger -import version -import converter - - -arguments = argparse.ArgumentParser(description='Pure Python command-line RSS reader') - -arguments.add_argument('source', action='store', help='RSS URL') -arguments.add_argument('--version', action='store_true', help='Print version info') -arguments.add_argument('--json', action='store_true', help='Print result as JSON in stdout') -arguments.add_argument('--verbose', action='store_true', help='Outputs verbose') -arguments.add_argument('--limit', action='store', help='Limit news topics') -arguments.add_argument('--date', action='store', help='Print news from the specified day') -arguments.add_argument('--tohtml', action='store', help='Convert news in html format') -arguments.add_argument('--topdf', action='store', help='Convert news in pdf format') - -args = arguments.parse_args() - -try: - # set object of feedparser library - channel = feedparser.parse(args.source) - print("Feed: ", channel.feed.title, '\n') - - # if no limit - take all news in channel - if not (args.limit): - args.limit = len(channel.entries) - limit = int(args.limit) - - if (args.version): - print(version.VERSION) - - elif (args.verbose): - # Create file if not exist - with open('parser.log', 'r') as f: - for line in f: - print(line) - - elif (args.date): - - if (len(args.date)==8): - print(get_cache.getCache(int(args.date))) - else: - print("Length of date should be equals 8") - - elif (args.tohtml or args.topdf): - - # set object of html converter library - doc = dominate.document(title="HTML document") - random_name = random.randint(1, 120) - - for index, item in enumerate(channel.entries): - if (index == limit): - break - - media_content = parse_news.checkMediaContent(item) - - # create html structure - with doc: - with div(): - h2(item.title) - p("Date: " + item.published) - if (media_content): - img(src=media_content) - p("Description: " + parse_news.getDescription(item.description)) - - if (args.tohtml): - converter.intoHTML(args.tohtml, doc, random_name) - - elif (args.topdf): - converter.intoPDF(args.topdf, doc, random_name) - - else: - - for index, item in enumerate(channel.entries): - if (index == limit): - break - - # create folder for cache - today = date.today().strftime("%Y%m%d") - if not os.path.exists('cache/'): - os.makedirs('cache/') - cache_file = 'cache/' + today + '.txt' - - # cache news - with open(cache_file, 'a', encoding="utf-8") as f: - f.write("Title: " + item.title + '\n' + - "Link: " + item.link + '\n' + - "Description: " + parse_news.getDescription(item.description) + '\n') - - # log news - logger.logging.debug("Title: " + str(item.title)) - logger.logging.debug("Date: " + str(item.published)) - logger.logging.debug("Link: " + str(item.link)) - logger.logging.debug("Description: " + parse_news.getDescription(item.description)) - logger.logging.debug("Links:"+"\n[1]: " + str(item.link) + - "(link)\n[2]: " + str(parse_news.checkMediaContent(item))) - - if (args.json): - print(parse_news.intoJson(item)) - - # print news - print("Title: ", item.title) - print("Date: ", item.published) - print("Link: ", item.link, '\n') - print("Description: ", parse_news.getDescription(item.description), '\n') - print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", - parse_news.checkMediaContent(item), '\n') - -except ValueError as e: - print("--limit or --date argument should be number") - logger.logging.error("ValueError: " + str(e)) - -except AttributeError as e: - print("AttributeError: " + str(e)) - print("Maybe check your source") - logger.logging.error("AttributeError: " + str(e)) - -# fast paste in argument -# source = "https://news.yahoo.com/rss" -# source1 = "https://news.google.com/rss" -# source2 = "https://www.theguardian.com/world/rss" diff --git a/rss_reader/version.py b/rss_reader/version.py deleted file mode 100644 index 7d5f0d8..0000000 --- a/rss_reader/version.py +++ /dev/null @@ -1 +0,0 @@ -VERSION = '0.4.3' \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index ef68e63..0000000 --- a/setup.py +++ /dev/null @@ -1,42 +0,0 @@ -from setuptools import setup, find_packages -import os - - -this_directory = os.path.abspath(os.path.dirname(__file__)) -with open(path.join(this_directory, 'README.md'), encoding='utf-8') as fh: - long_description = fh.read() - -def get_version(): - basedir = os.path.dirname(__file__) - with open(os.path.join(basedir, 'rss_reader/version.py')) as f: - VERSION = None - exec(f.read()) - return VERSION - raise RuntimeError('No version info found.') - -setup( - name="rss-reader", - version=get_version(), - packages=find_packages(), - install_requires=['beautifulsoup4','dominate','feedparser','pdfkit','twine','urllib3'], - author="ilya khonenko", - author_email="honenkoi@gmail.com", - # url="https://github.com" - description="This is rss-reader", - long_description = long_description, - long_description_content_type="text/markdown", - classifiers=[ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", - ], - keywords="rss reader", - python_requires='>=3.8', -) - -#python setup.py sdist bdist_wheel -#(for testing) -#python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* -#python -m pip install --index-url https://test.pypi.org/simple/ --no-deps ilyakhonenko (in venv) -#(for real) -#python -m twine upload dist/* (https://pypi.org by default) -#pip install [your-package] From 27332b6044efddb7c47c42b7d5172e19a32f26a8 Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Sun, 10 Nov 2019 21:17:58 +0300 Subject: [PATCH 12/15] Fix encoding problems. Cache images in database. Create function for pdf converting. Describe method of storing cached data, converting to html or pdf and installing in the README.md --- .gitignore | 2 + final_task/README.md | 54 +++- final_task/rss_reader/converter.py | 42 ++- final_task/rss_reader/get_cache.py | 29 +- final_task/rss_reader/news_parser.py | 33 +- final_task/rss_reader/parser.log | 416 ------------------------- final_task/rss_reader/requirements.txt | 5 - final_task/rss_reader/rss_reader.py | 52 ++-- final_task/rss_reader/topdf.py | 40 +++ final_task/setup.py | 12 +- 10 files changed, 176 insertions(+), 509 deletions(-) delete mode 100644 final_task/rss_reader/parser.log delete mode 100644 final_task/rss_reader/requirements.txt create mode 100644 final_task/rss_reader/topdf.py diff --git a/.gitignore b/.gitignore index 07722c8..b18621a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ __pycache__/ .vscode/ .idea/ +final_task/rss_reader/parser.log +final_task/rss_reader/newsdatabase.db \ No newline at end of file diff --git a/final_task/README.md b/final_task/README.md index 4994e71..5a4d630 100644 --- a/final_task/README.md +++ b/final_task/README.md @@ -1,27 +1,30 @@ # RSS-READER -### Command-line utility which receives RSS URL and prints results in human-readable format. +## Command-line utility which receives RSS URL and prints results in human-readable format. -**Example:** +### **Example:** python rss_reader.py https://news.yahoo.com/rss - -limit 1 -**Output**: +### **Output**: -Feed: Yahoo News - Latest News & Headlines +**Feed: Yahoo News - Latest News & Headlines** -Title: Families come from across U.S. to grieve relatives slain in Mexico -Date: Thu, 07 Nov 2019 01:06:45 -0500 -Link: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html +**Title**: Families come from across U.S. to grieve relatives slain in Mexico -Description: An American man whose grandchildren were slain in a massacre in Mexico demanded justice on Thursday for other victims of the country's drug war, as relatives gathered from +**Date**: Thu, 07 Nov 2019 01:06:45 -0500 + +**Link**: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html + +**Description**: An American man whose grandchildren were slain in a massacre in Mexico demanded justice on Thursday for other victims of the country's drug war, as relatives gathered from across the United States for a funeral guarded by heavily armed military. Kenneth Miller lost his daughter-in-law and four grandchildren, all dual citizens, in an ambush on Monday in th e northern border state of Sonora that killed three mothers and six children. The attack on members of breakaway Mormon communities who settled in Mexico decades ago prompted U.S. Pres ident Donald Trump to urge Mexico and the United States to "wage war" together on drug cartels. -Links: +**Links**: + ``` -[1]: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html (link) -[2]: http://l.yimg.com/uu/api/res/1.2/rRx_J3xHKYzIQ4EsiCPRTw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/9cbb9d9c38b8bbe10243ebfde0f6db48 +[1]: https://news.yahoo.com/under-armed-escort-mourner-conv... (link) +[2]: http://l.yimg.com/uu/api/res/1.2/rRx_J3xHKYzIQ4EsiCPRT... ``` ``` @@ -34,4 +37,33 @@ optional arguments: --json Print result as JSON in stdout --verbose Outputs verbose status messages --limit LIMIT Limit news topics if this parameter provided +``` + +## Installation + +The recommended way to install rss-reader is with pip: + + +``` +pip install rss-reader +``` + + +## Data caching + +I wrote a program that creates a database for convenient storage of news. The **sqlite3** database (built into python) is perfectly suited for this. Pictures are also stored in the database in binary format. + +## Converting + +To convert data to html format, I used the **dominate** library. + +Example: +``` +html_document = dominate.document(title="HTML document") + +with html_document: + with div(): + h2("Title: " + news_title) + p("Link: " + news_link) + p("Description: " + news_description) ``` \ No newline at end of file diff --git a/final_task/rss_reader/converter.py b/final_task/rss_reader/converter.py index a441d58..ddd72a5 100644 --- a/final_task/rss_reader/converter.py +++ b/final_task/rss_reader/converter.py @@ -1,39 +1,45 @@ import os -import pdfkit +import re from dominate.tags import div, h2, img, p, link from datetime import datetime +import html import news_parser import logger +import topdf -def createHtmlStructure(channel, limit, html_document, html_path, pdf_path): +def createHtmlStructure(url, channel, limit, html_document, html_path, pdf_path): ''' 1. in loop create html structure 2. create html document or convert html structure into pdf ''' - with html_document.head: - styles = os.path.abspath('style.css') - link(rel='stylesheet', href=styles) + #uncomment if u want html with styles + # with html_document.head: + # styles = os.path.abspath('style.css') + # link(rel='stylesheet', href=styles) + for index, item in enumerate(channel.entries): if (index == limit): break with html_document: with div(): - h2("Title: " + item.title) + h2("Title: " + html.unescape(item.title)) p("Link: " + item.link) media_content = news_parser.checkMediaContent(item) if (media_content): img(src=media_content) - p("Description: " + news_parser.getDescription(item.description)) + description = news_parser.getDescription(item.description) + if (description): + p("Description: " + description) if (html_path): - intoHTML(html_document, html_path) + intoHTML(html_document, html_path, url) else: - intoPDF(html_document, pdf_path) + intoPDF(html_document, pdf_path, url) -def intoHTML(html_document, html_path): +def intoHTML(html_document, html_path, url): ''' 1. create folder with html file 2. write html structure in file @@ -41,14 +47,20 @@ def intoHTML(html_document, html_path): if not os.path.exists(html_path): os.makedirs(html_path) time_name = datetime.strftime(datetime.now(), "%H%M%S") - html_file = html_path + '/' + time_name + '.html' + html_file = html_path + '/' + getSiteName(url) + '-' + time_name + '.html' - with open(html_file, 'w') as f: + with open(html_file, 'w', encoding='utf-8') as f: f.write(str(html_document)) logger.logging.info("Successfull converting into html") -def intoPDF(doc, pdf_path): +def getSiteName(url): + regex = re.search(r"^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?([a-z0-9]+[\-\.]{1}[a-z0-9]+)*(\.[a-z]{2,5})(\/.*)?$", url) + + return ''.join(filter(None,regex.group(2,3))) + + +def intoPDF(doc, pdf_path, url): ''' 1. create folder with pdf file 2. convert html into pdf @@ -56,7 +68,7 @@ def intoPDF(doc, pdf_path): if not os.path.exists(pdf_path): os.makedirs(pdf_path) time_name = datetime.strftime(datetime.now(), "%H%M%S") - pdf_file = pdf_path + '/' + time_name + '.pdf' + pdf_file = pdf_path + '/' + getSiteName(url) + '-' + time_name + '.pdf' - pdfkit.from_string(str(doc), pdf_file) + topdf.convertHtmlToPdf(str(doc), pdf_file) logger.logging.info("Successfull converting into pdf") diff --git a/final_task/rss_reader/get_cache.py b/final_task/rss_reader/get_cache.py index 9975bb3..24d51c4 100644 --- a/final_task/rss_reader/get_cache.py +++ b/final_task/rss_reader/get_cache.py @@ -3,9 +3,12 @@ import sqlite3 from datetime import datetime import time +import os +import base64 import logger import converter +import rss_reader def dateToStamp(arg_date): @@ -28,7 +31,7 @@ def convertCache(url, arg_date, limit, html_path, pdf_path): cursor = conn.cursor() try: - cursor.execute("SELECT title, link, description, image FROM news WHERE pub_date_stamp >= ? and pub_date_stamp < ? ", + cursor.execute("SELECT title, link, image, description FROM news WHERE pub_date_stamp >= ? and pub_date_stamp < ? ", (dateToStamp(arg_date), dateToStamp(int(arg_date) + 1))) except sqlite3.OperationalError as e: print("No such table") @@ -42,22 +45,26 @@ def convertCache(url, arg_date, limit, html_path, pdf_path): break print("\nTitle: ", row[0]) print("Link: ", row[1], '\n') - print("Description: ", row[2], '\n') + if (row[3]): + print("Description: ", row[3], '\n') else: - createHtmlStructure(records, limit, html_path, pdf_path) + createHtmlStructure(records, limit, html_path, pdf_path, url) cursor.close() conn.close() -def createHtmlStructure(records, limit, html_path, pdf_path): +def createHtmlStructure(records, limit, html_path, pdf_path, url): ''' 1. in loop create html structure 2. create html document or convert html structure into pdf ''' html_document = dominate.document(title="HTML document") - with html_document.head: - link(rel='stylesheet', href='style.css') + #uncomment if u want html with styles + # with html_document.head: + # styles = os.path.abspath('style.css') + # link(rel='stylesheet', href=styles) + for index, row in enumerate(records): if(limit and index==limit): break @@ -66,9 +73,11 @@ def createHtmlStructure(records, limit, html_path, pdf_path): h2("Title: " + row[0]) p("Link: " + row[1]) if (row[2]): - img(src=row[2]) - p("Description: " + row[3]) + img(src="data:image/jpg;base64," + base64.b64encode(row[2]).decode('ascii')) + if (row[3]): + p("Description: " + row[3]) + if (html_path): - converter.intoHTML(html_document, html_path) + converter.intoHTML(html_document, html_path, url) elif (pdf_path): - converter.intoPDF(html_document, pdf_path) \ No newline at end of file + converter.intoPDF(html_document, pdf_path, url) \ No newline at end of file diff --git a/final_task/rss_reader/news_parser.py b/final_task/rss_reader/news_parser.py index 8365057..e729477 100644 --- a/final_task/rss_reader/news_parser.py +++ b/final_task/rss_reader/news_parser.py @@ -1,11 +1,11 @@ from bs4 import BeautifulSoup import json import os -import urllib.request from datetime import datetime import time import sqlite3 import html +import requests def checkMediaContent(item): @@ -25,13 +25,16 @@ def getDescription(item): def intoJson(item): '''print news in json format''' - return json.dumps({ + json_news = { 'Title: ': html.unescape(item.title), 'Date: ': item.published, - 'Link: ': item.link, - 'Description: ': getDescription(item.description), - 'Media_link': checkMediaContent(item) - }) + 'Link: ': item.link + } + description = getDescription(item.description) + media_link = checkMediaContent(item) + if(description): json_news['Description: '] = description + if(media_link): json_news['Media link: '] = media_link + return json.dumps(json_news) def cacheNews(url, channel): @@ -45,11 +48,11 @@ def cacheNews(url, channel): try: cursor.execute("""CREATE TABLE news - (title text, link text, image text, + (title text, link text, image BLOB, description text, pub_date_stamp real, UNIQUE (title, link, pub_date_stamp)) """) - except: + except Exception: print("Table already exists") insertNewsIntoTable(channel, cursor) @@ -66,19 +69,17 @@ def insertNewsIntoTable(channel, cursor): ''' for index, item in enumerate(channel.entries): - descr = getDescription(item.description) + description = getDescription(item.description) pub_date = getPublishedDate(item.published) pub_date_stamp = time.mktime(datetime.strptime(pub_date, '%d %m %Y %H:%M:%S').timetuple()) - if not os.path.exists('cache_images/'): - os.makedirs('cache_images/') - time_name = datetime.strftime(datetime.now(), "%H%M%S") + str(index) - image_file_name = 'cache_images/' + time_name + '.jpg' - if (checkMediaContent(item)): - urllib.request.urlretrieve(checkMediaContent(item), image_file_name) + media_content = checkMediaContent(item) + if (media_content): + response = requests.get(media_content) + image = sqlite3.Binary(response.content) - row = (html.unescape(item.title), item.link, descr, os.path.abspath(image_file_name), pub_date_stamp) + row = (html.unescape(item.title), item.link, image, description, pub_date_stamp) try: cursor.execute("INSERT INTO news VALUES (?,?,?,?,?)", row) except sqlite3.IntegrityError: diff --git a/final_task/rss_reader/parser.log b/final_task/rss_reader/parser.log deleted file mode 100644 index 33bdcd3..0000000 --- a/final_task/rss_reader/parser.log +++ /dev/null @@ -1,416 +0,0 @@ -ERROR [2019-11-09 00:03:49,421] no such table: news -INFO [2019-11-09 00:04:08,350] Website is working -DEBUG [2019-11-09 00:04:13,168] Title: Impeachment transcript details intrusion by GOP Rep. Gaetz -DEBUG [2019-11-09 00:04:13,168] Date: Fri, 08 Nov 2019 14:24:27 -0500 -DEBUG [2019-11-09 00:04:13,169] Link: https://news.yahoo.com/impeachment-transcript-details-intrusion-by-gop-rep-gaetz-192427892.html -DEBUG [2019-11-09 00:04:13,170] Description: House Intelligence Committee Chairman Adam Schiff did not take kindly to a protest waged by Republican Rep. Matt Gaetz. -DEBUG [2019-11-09 00:04:13,170] Links: -[1]: https://news.yahoo.com/impeachment-transcript-details-intrusion-by-gop-rep-gaetz-192427892.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/p0LFQDFOarpxYzakr0szxQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/dc1729d0-0256-11ea-bdff-89108ee6f919 - -DEBUG [2019-11-09 00:04:13,170] Title: South Korea deports North Koreans who fled after killing 16 -DEBUG [2019-11-09 00:04:13,171] Date: Thu, 07 Nov 2019 20:32:32 -0500 -DEBUG [2019-11-09 00:04:13,171] Link: https://news.yahoo.com/south-korea-deports-north-koreans-070243995.html -DEBUG [2019-11-09 00:04:13,172] Description: In an extremely unusual case, South Korea deported two North Korean fishermen on Thursday after determining they had killed 16 other crew members on their boat and then fled to South Korean waters, Seoul officials said. South Korea has a policy of accepting North Koreans who want to resettle in the South to avoid political oppression and poverty at home. This week's deportations were the first South Korea has carried out of any North Korean who came to the South since the end of the 1950-53 Korean War, according to Seoul's Unification Ministry, which deals with North Korean affairs. -DEBUG [2019-11-09 00:04:13,172] Links: -[1]: https://news.yahoo.com/south-korea-deports-north-koreans-070243995.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/sofRs2pkM3o6YIHAQ1lmgQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/9737049a3c4448d9d28f347f50dadf0f - -DEBUG [2019-11-09 00:04:13,172] Title: Iranian beauty queen wins asylum in Philippines -DEBUG [2019-11-09 00:04:13,172] Date: Fri, 08 Nov 2019 09:19:56 -0500 -DEBUG [2019-11-09 00:04:13,173] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html -DEBUG [2019-11-09 00:04:13,174] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. -DEBUG [2019-11-09 00:04:13,174] Links: -[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg - -DEBUG [2019-11-09 00:04:13,174] Title: Man serving life sentence says it ended once he died, was revived in medical emergency -DEBUG [2019-11-09 00:04:13,174] Date: Thu, 07 Nov 2019 17:43:49 -0500 -DEBUG [2019-11-09 00:04:13,175] Link: https://news.yahoo.com/man-serving-life-sentence-says-194608585.html -DEBUG [2019-11-09 00:04:13,175] Description: An Iowa man convicted of murder was rushed from prison to a hospital where his heart was restarted five times. He claims he should be freed. -DEBUG [2019-11-09 00:04:13,176] Links: -[1]: https://news.yahoo.com/man-serving-life-sentence-says-194608585.html(link) -[2]: http://l2.yimg.com/uu/api/res/1.2/6an.31kqozxWL0y05iSU7Q--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2019-11/54ba34b0-0250-11ea-bdbb-18b75000726e - -DEBUG [2019-11-09 00:04:13,176] Title: Bill Gates addressed his multiple meetings with Jeffrey Epstein: 'I made a mistake in judgment' -DEBUG [2019-11-09 00:04:13,176] Date: Wed, 06 Nov 2019 17:22:49 -0500 -DEBUG [2019-11-09 00:04:13,176] Link: https://news.yahoo.com/bill-gates-addressed-multiple-meetings-222249632.html -DEBUG [2019-11-09 00:04:13,177] Description: Bill Gates addressed his ties to Jeffrey Epstein Wednesday, saying that he "made a mistake in judgement" by associating with the convicted sex offender. -DEBUG [2019-11-09 00:04:13,177] Links: -[1]: https://news.yahoo.com/bill-gates-addressed-multiple-meetings-222249632.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/7RadwILn16dXQ_MIl0z7sw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/business_insider_articles_888/50e2156b9598bbedc0e8df96e877f8a3 - -INFO [2019-11-09 00:07:18,972] Successfull parsing -INFO [2019-11-09 00:08:09,057] Successfull parsing -ERROR [2019-11-09 00:29:22,423] URLError: -INFO [2019-11-09 00:29:37,818] Successfull converting into html -INFO [2019-11-09 00:29:58,662] Successfull converting into pdf -INFO [2019-11-09 00:55:01,092] Website is working -DEBUG [2019-11-09 00:55:06,802] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 00:55:06,802] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 00:55:06,802] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 00:55:06,804] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 00:55:06,804] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -DEBUG [2019-11-09 00:55:06,810] Title: Greta Thunberg shuts down heckler at climate rally -DEBUG [2019-11-09 00:55:06,811] Date: Fri, 08 Nov 2019 15:40:15 -0500 -DEBUG [2019-11-09 00:55:06,811] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html -DEBUG [2019-11-09 00:55:06,812] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. -DEBUG [2019-11-09 00:55:06,823] Links: -[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 - -DEBUG [2019-11-09 00:55:06,841] Title: Families come from across U.S. to grieve relatives slain in Mexico -DEBUG [2019-11-09 00:55:06,842] Date: Thu, 07 Nov 2019 01:06:45 -0500 -DEBUG [2019-11-09 00:55:06,842] Link: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html -DEBUG [2019-11-09 00:55:06,843] Description: An American man whose grandchildren were slain in a massacre in Mexico demanded justice on Thursday for other victims of the country's drug war, as relatives gathered from across the United States for a funeral guarded by heavily armed military. Kenneth Miller lost his daughter-in-law and four grandchildren, all dual citizens, in an ambush on Monday in the northern border state of Sonora that killed three mothers and six children. The attack on members of breakaway Mormon communities who settled in Mexico decades ago prompted U.S. President Donald Trump to urge Mexico and the United States to "wage war" together on drug cartels. -DEBUG [2019-11-09 00:55:06,844] Links: -[1]: https://news.yahoo.com/under-armed-escort-mourner-convoys-060645935.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/rRx_J3xHKYzIQ4EsiCPRTw--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/9cbb9d9c38b8bbe10243ebfde0f6db48 - -INFO [2019-11-09 01:17:55,483] Website is working -DEBUG [2019-11-09 01:18:00,501] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 01:18:00,502] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 01:18:00,502] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 01:18:00,503] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 01:18:00,503] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -INFO [2019-11-09 01:18:50,287] Website is working -DEBUG [2019-11-09 01:18:54,766] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 01:18:54,767] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 01:18:54,767] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 01:18:54,768] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 01:18:54,768] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -INFO [2019-11-09 01:19:16,963] Website is working -DEBUG [2019-11-09 01:19:21,463] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 01:19:21,463] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 01:19:21,463] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 01:19:21,464] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 01:19:21,464] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -INFO [2019-11-09 01:20:24,404] Website is working -DEBUG [2019-11-09 01:20:28,607] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 01:20:28,607] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 01:20:28,608] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 01:20:28,609] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 01:20:28,609] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -ERROR [2019-11-09 01:25:45,080] no such table: news -INFO [2019-11-09 01:25:45,130] Successfull converting into html -INFO [2019-11-09 01:25:54,605] Website is working -DEBUG [2019-11-09 01:25:59,750] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 01:25:59,750] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 01:25:59,750] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 01:25:59,751] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 01:25:59,752] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -DEBUG [2019-11-09 01:25:59,755] Title: Greta Thunberg shuts down heckler at climate rally -DEBUG [2019-11-09 01:25:59,756] Date: Fri, 08 Nov 2019 15:40:15 -0500 -DEBUG [2019-11-09 01:25:59,756] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html -DEBUG [2019-11-09 01:25:59,757] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. -DEBUG [2019-11-09 01:25:59,757] Links: -[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 - -DEBUG [2019-11-09 01:25:59,762] Title: Suspects linked to New Hampshire couple's deaths found in Mexico -DEBUG [2019-11-09 01:25:59,763] Date: Thu, 07 Nov 2019 18:42:41 -0500 -DEBUG [2019-11-09 01:25:59,763] Link: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html -DEBUG [2019-11-09 01:25:59,765] Description: A man and a woman wanted in connection with the missing New Hampshire couple found buried at a Texas beach will be turned over to the Texas Rangers. -DEBUG [2019-11-09 01:25:59,766] Links: -[1]: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/xwz8.ULZexSl6yl6pIr7Sg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-us/usa_today_news_641/2b1782f8c4fb14398798220447951841 - -DEBUG [2019-11-09 01:25:59,775] Title: Iranian beauty queen wins asylum in Philippines -DEBUG [2019-11-09 01:25:59,776] Date: Fri, 08 Nov 2019 09:19:56 -0500 -DEBUG [2019-11-09 01:25:59,776] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html -DEBUG [2019-11-09 01:25:59,777] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. -DEBUG [2019-11-09 01:25:59,777] Links: -[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg - -DEBUG [2019-11-09 01:25:59,785] Title: Spain court agrees to extradite Venezuelan ex-intelligence chief to USA: EFE -DEBUG [2019-11-09 01:25:59,786] Date: Fri, 08 Nov 2019 14:16:29 -0500 -DEBUG [2019-11-09 01:25:59,786] Link: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html -DEBUG [2019-11-09 01:25:59,787] Description: Spain's High Court has agreed to extradite former Venezuelan intelligence chief Hugo Carvajal to the United States, reversing a previous ruling to deny the extradition request, EFE news agency reported on Friday, citing judicial sources. Carvajal's lawyer told Reuters she had not been notified of any court decision. The former general was arrested by Spanish police in April at the request of U.S. authorities, but Spain's High Court then ruled in September that he should be released and his extradition request denied. -DEBUG [2019-11-09 01:25:59,787] Links: -[1]: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/_TymIShCAQtjIx56GTvg1A--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/33f2a5c1e4a02bed717f72be3dd95826 - -INFO [2019-11-09 01:26:07,686] Website is working -DEBUG [2019-11-09 01:26:12,104] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 01:26:12,104] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 01:26:12,104] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 01:26:12,106] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 01:26:12,106] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -INFO [2019-11-09 01:26:19,464] Successfull converting into html -INFO [2019-11-09 01:28:02,226] Website is working -INFO [2019-11-09 01:28:09,781] Successfull converting into html -INFO [2019-11-09 01:28:59,137] Website is working -DEBUG [2019-11-09 01:29:03,694] Title: Giuliani floats new explanation for pressuring Ukraine to investigate Biden -DEBUG [2019-11-09 01:29:03,694] Date: Wed, 06 Nov 2019 19:41:52 -0500 -DEBUG [2019-11-09 01:29:03,694] Link: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html -DEBUG [2019-11-09 01:29:03,695] Description: Trump's personal lawyer said on Wednesday that he pressured Ukrainian officials to investigate Joe Biden to defend President Trump "against false charges." -DEBUG [2019-11-09 01:29:03,696] Links: -[1]: https://news.yahoo.com/rudy-giuliani-floats-new-explanation-for-pressuring-ukraine-to-investigate-biden-004152601.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/ehDCUyKzVh3ITYpe0InPlg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/bf3da590-00f4-11ea-bb5b-601d63d8caba - -DEBUG [2019-11-09 01:29:03,699] Title: Greta Thunberg shuts down heckler at climate rally -DEBUG [2019-11-09 01:29:03,699] Date: Fri, 08 Nov 2019 15:40:15 -0500 -DEBUG [2019-11-09 01:29:03,700] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html -DEBUG [2019-11-09 01:29:03,700] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. -DEBUG [2019-11-09 01:29:03,701] Links: -[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 - -DEBUG [2019-11-09 01:29:03,708] Title: Suspects linked to New Hampshire couple's deaths found in Mexico -DEBUG [2019-11-09 01:29:03,708] Date: Thu, 07 Nov 2019 18:42:41 -0500 -DEBUG [2019-11-09 01:29:03,708] Link: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html -DEBUG [2019-11-09 01:29:03,709] Description: A man and a woman wanted in connection with the missing New Hampshire couple found buried at a Texas beach will be turned over to the Texas Rangers. -DEBUG [2019-11-09 01:29:03,709] Links: -[1]: https://news.yahoo.com/reports-man-linked-killings-hampshire-123827582.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/xwz8.ULZexSl6yl6pIr7Sg--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-us/usa_today_news_641/2b1782f8c4fb14398798220447951841 - -DEBUG [2019-11-09 01:29:03,715] Title: Iranian beauty queen wins asylum in Philippines -DEBUG [2019-11-09 01:29:03,715] Date: Fri, 08 Nov 2019 09:19:56 -0500 -DEBUG [2019-11-09 01:29:03,715] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html -DEBUG [2019-11-09 01:29:03,716] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. -DEBUG [2019-11-09 01:29:03,716] Links: -[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg - -DEBUG [2019-11-09 01:29:03,723] Title: Spain court agrees to extradite Venezuelan ex-intelligence chief to USA: EFE -DEBUG [2019-11-09 01:29:03,724] Date: Fri, 08 Nov 2019 14:16:29 -0500 -DEBUG [2019-11-09 01:29:03,724] Link: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html -DEBUG [2019-11-09 01:29:03,725] Description: Spain's High Court has agreed to extradite former Venezuelan intelligence chief Hugo Carvajal to the United States, reversing a previous ruling to deny the extradition request, EFE news agency reported on Friday, citing judicial sources. Carvajal's lawyer told Reuters she had not been notified of any court decision. The former general was arrested by Spanish police in April at the request of U.S. authorities, but Spain's High Court then ruled in September that he should be released and his extradition request denied. -DEBUG [2019-11-09 01:29:03,725] Links: -[1]: https://news.yahoo.com/spain-court-agrees-extradite-venezuelan-191629429.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/_TymIShCAQtjIx56GTvg1A--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-US/reuters.com/33f2a5c1e4a02bed717f72be3dd95826 - -INFO [2019-11-09 01:32:08,975] Website is working -DEBUG [2019-11-09 01:32:13,681] Title: Greta Thunberg shuts down heckler at climate rally -DEBUG [2019-11-09 01:32:13,681] Date: Fri, 08 Nov 2019 15:40:15 -0500 -DEBUG [2019-11-09 01:32:13,681] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html -DEBUG [2019-11-09 01:32:13,682] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. -DEBUG [2019-11-09 01:32:13,682] Links: -[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 - -DEBUG [2019-11-09 01:32:13,688] Title: Rockets hit Iraq base with US troops; no word on casualties -DEBUG [2019-11-09 01:32:13,688] Date: Fri, 08 Nov 2019 14:53:04 -0500 -DEBUG [2019-11-09 01:32:13,688] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html -DEBUG [2019-11-09 01:32:13,689] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. -DEBUG [2019-11-09 01:32:13,689] Links: -[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 - -DEBUG [2019-11-09 01:32:13,697] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives -DEBUG [2019-11-09 01:32:13,698] Date: Wed, 06 Nov 2019 21:45:35 -0500 -DEBUG [2019-11-09 01:32:13,698] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html -DEBUG [2019-11-09 01:32:13,699] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. -DEBUG [2019-11-09 01:32:13,699] Links: -[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) -[2]: - -DEBUG [2019-11-09 01:32:13,707] Title: Iranian beauty queen wins asylum in Philippines -DEBUG [2019-11-09 01:32:13,708] Date: Fri, 08 Nov 2019 09:19:56 -0500 -DEBUG [2019-11-09 01:32:13,708] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html -DEBUG [2019-11-09 01:32:13,709] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. -DEBUG [2019-11-09 01:32:13,709] Links: -[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg - -DEBUG [2019-11-09 01:32:13,807] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars -DEBUG [2019-11-09 01:32:13,807] Date: Thu, 07 Nov 2019 16:11:32 -0500 -DEBUG [2019-11-09 01:32:13,807] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html -DEBUG [2019-11-09 01:32:13,808] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. -DEBUG [2019-11-09 01:32:13,809] Links: -[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 - -INFO [2019-11-09 01:35:58,915] Website is working -DEBUG [2019-11-09 01:36:04,288] Title: Greta Thunberg shuts down heckler at climate rally -DEBUG [2019-11-09 01:36:04,289] Date: Fri, 08 Nov 2019 15:40:15 -0500 -DEBUG [2019-11-09 01:36:04,289] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html -DEBUG [2019-11-09 01:36:04,360] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. -DEBUG [2019-11-09 01:36:04,361] Links: -[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 - -DEBUG [2019-11-09 01:36:04,380] Title: Rockets hit Iraq base with US troops; no word on casualties -DEBUG [2019-11-09 01:36:04,380] Date: Fri, 08 Nov 2019 14:53:04 -0500 -DEBUG [2019-11-09 01:36:04,381] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html -DEBUG [2019-11-09 01:36:04,382] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. -DEBUG [2019-11-09 01:36:04,382] Links: -[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 - -DEBUG [2019-11-09 01:36:04,389] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives -DEBUG [2019-11-09 01:36:04,389] Date: Wed, 06 Nov 2019 21:45:35 -0500 -DEBUG [2019-11-09 01:36:04,389] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html -DEBUG [2019-11-09 01:36:04,390] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. -DEBUG [2019-11-09 01:36:04,391] Links: -[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) -[2]: - -DEBUG [2019-11-09 01:36:04,424] Title: Iranian beauty queen wins asylum in Philippines -DEBUG [2019-11-09 01:36:04,424] Date: Fri, 08 Nov 2019 09:19:56 -0500 -DEBUG [2019-11-09 01:36:04,425] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html -DEBUG [2019-11-09 01:36:04,426] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. -DEBUG [2019-11-09 01:36:04,426] Links: -[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg - -DEBUG [2019-11-09 01:36:04,434] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars -DEBUG [2019-11-09 01:36:04,434] Date: Thu, 07 Nov 2019 16:11:32 -0500 -DEBUG [2019-11-09 01:36:04,434] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html -DEBUG [2019-11-09 01:36:04,435] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. -DEBUG [2019-11-09 01:36:04,436] Links: -[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 - -INFO [2019-11-09 01:38:02,188] Website is working -DEBUG [2019-11-09 01:38:06,738] Title: Greta Thunberg shuts down heckler at climate rally -DEBUG [2019-11-09 01:38:06,738] Date: Fri, 08 Nov 2019 15:40:15 -0500 -DEBUG [2019-11-09 01:38:06,738] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html -DEBUG [2019-11-09 01:38:06,739] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. -DEBUG [2019-11-09 01:38:06,740] Links: -[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 - -DEBUG [2019-11-09 01:38:06,745] Title: Rockets hit Iraq base with US troops; no word on casualties -DEBUG [2019-11-09 01:38:06,746] Date: Fri, 08 Nov 2019 14:53:04 -0500 -DEBUG [2019-11-09 01:38:06,746] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html -DEBUG [2019-11-09 01:38:06,747] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. -DEBUG [2019-11-09 01:38:06,748] Links: -[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 - -DEBUG [2019-11-09 01:38:06,754] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives -DEBUG [2019-11-09 01:38:06,754] Date: Wed, 06 Nov 2019 21:45:35 -0500 -DEBUG [2019-11-09 01:38:06,755] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html -DEBUG [2019-11-09 01:38:06,756] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. -DEBUG [2019-11-09 01:38:06,756] Links: -[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) -[2]: - -DEBUG [2019-11-09 01:38:06,761] Title: Iranian beauty queen wins asylum in Philippines -DEBUG [2019-11-09 01:38:06,762] Date: Fri, 08 Nov 2019 09:19:56 -0500 -DEBUG [2019-11-09 01:38:06,762] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html -DEBUG [2019-11-09 01:38:06,763] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. -DEBUG [2019-11-09 01:38:06,763] Links: -[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg - -DEBUG [2019-11-09 01:38:06,787] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars -DEBUG [2019-11-09 01:38:06,787] Date: Thu, 07 Nov 2019 16:11:32 -0500 -DEBUG [2019-11-09 01:38:06,787] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html -DEBUG [2019-11-09 01:38:06,788] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. -DEBUG [2019-11-09 01:38:06,789] Links: -[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 - -INFO [2019-11-09 01:38:35,554] Website is working -DEBUG [2019-11-09 01:38:40,072] Title: Greta Thunberg shuts down heckler at climate rally -DEBUG [2019-11-09 01:38:40,072] Date: Fri, 08 Nov 2019 15:40:15 -0500 -DEBUG [2019-11-09 01:38:40,073] Link: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html -DEBUG [2019-11-09 01:38:40,074] Description: “I think if you want to speak with me personally, maybe you can do it later,” the 16-year-old Swedish activist said to a woman who tried to interrupt her speech in Charlotte, N.C., on Friday. -DEBUG [2019-11-09 01:38:40,074] Links: -[1]: https://news.yahoo.com/greta-thunberg-heckler-climate-rally-charlotte-204015615.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/GBkFr.r6OPKO_7pYCI.YhQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-11/43d3a6a0-0268-11ea-bfff-955a6cddbfc4 - -DEBUG [2019-11-09 01:38:40,082] Title: Rockets hit Iraq base with US troops; no word on casualties -DEBUG [2019-11-09 01:38:40,082] Date: Fri, 08 Nov 2019 14:53:04 -0500 -DEBUG [2019-11-09 01:38:40,082] Link: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html -DEBUG [2019-11-09 01:38:40,083] Description: A barrage of Katyusha rockets targeted an Iraqi air base that houses American troops south of the city of Mosul on Friday, two security officials said. The rocket fire appears to have originated in Mosul and struck the Iraqi army base in Qayyara, about 60 kilometers (38 miles) south of Mosul, where a U.S.-led coalition is helping Iraqi forces battle remnants of the Islamic State group. Iraq announced victory over IS two years ago, but the extremist group is still active through sleeper cells and frequently mount attacks on Iraqi security forces. -DEBUG [2019-11-09 01:38:40,084] Links: -[1]: https://news.yahoo.com/rockets-hit-iraq-us-troops-192839317.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/9xdrC9N8iYIOpXlwK6buBA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d550bdbf40f21d98a4b33aa32e277f84 - -DEBUG [2019-11-09 01:38:40,090] Title: UPDATE 1-Killed American family may have been 'bait' in Mexican cartel fight -relatives -DEBUG [2019-11-09 01:38:40,091] Date: Wed, 06 Nov 2019 21:45:35 -0500 -DEBUG [2019-11-09 01:38:40,091] Link: https://news.yahoo.com/1-killed-american-family-may-024535922.html -DEBUG [2019-11-09 01:38:40,092] Description: The nine American women and children killed in northern Mexico were victims of a territorial dispute between an arm of the Sinaloa Cartel and a rival gang, officials said on Wednesday, and may have been used to lure one side into a firefight. Members of breakaway Mormon communities that settled in Mexico decades ago, the three families were ambushed as they drove along a dirt track in Sonora state, leading to U.S. President Donald Trump urging Mexico and the United States to "wage war' together on the drug cartels. Accounts emerging of Monday morning's slayings detailed the heroism of a surviving boy who walked for miles to get help for his siblings, and heavy gun battles in the remote hill area that lasted for hours into the night after the attack. -DEBUG [2019-11-09 01:38:40,092] Links: -[1]: https://news.yahoo.com/1-killed-american-family-may-024535922.html(link) -[2]: - -DEBUG [2019-11-09 01:38:40,102] Title: Iranian beauty queen wins asylum in Philippines -DEBUG [2019-11-09 01:38:40,103] Date: Fri, 08 Nov 2019 09:19:56 -0500 -DEBUG [2019-11-09 01:38:40,103] Link: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html -DEBUG [2019-11-09 01:38:40,104] Description: An Iranian beauty queen sought by Tehran on criminal charges has been granted political asylum in the Philippines, an official said Friday, ending a three-week standoff at Manila airport. Bahareh Zare Bahari, based in the Philippines since 2014, was denied entry into the Southeast Asian nation on October 17 when she returned from Dubai, with Philippine authorities citing an Iranian warrant for her arrest. Claiming Tehran wanted to punish her for opposition to Iran's theocratic regime, Bahari then sought refugee status, holed up in a room at Manila's international airport and using social media to rally support from the international community -- including a plea to Philippine President Rodrigo Duterte. -DEBUG [2019-11-09 01:38:40,105] Links: -[1]: https://news.yahoo.com/iranian-beauty-queen-wins-asylum-philippines-141956872.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/jjR.ItnN_b.S5BsFyyxDYQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/41118c0daebfa140a4894f5bb9e4ee8d57921daa.jpg - -DEBUG [2019-11-09 01:38:40,118] Title: Child seats in Italy to be fitted with alarms after spate of deaths of children trapped in hot cars -DEBUG [2019-11-09 01:38:40,118] Date: Thu, 07 Nov 2019 16:11:32 -0500 -DEBUG [2019-11-09 01:38:40,119] Link: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html -DEBUG [2019-11-09 01:38:40,120] Description: Parents of babies and toddlers will be required to use special alarmed child seats under a new law in Italy, in response to a spate of children dying in cars from extreme heat. Parents who fail to buy the alarmed car seats, or buy alarm attachments, face fines of up to €326 and five points being docked from their driving licence. If, within two years, a parent is caught again without the special seat, their driving licence will be suspended for two weeks. The special car seats work by motion sensor and set off audio alarms and flashing lights if a child is left alone in the car. Devices can also be linked to a parent’s mobile phone. Under the law adopted on Thursday, they are now compulsory for all children under the age of four. The government has promised to contribute €30 to each family that has to buy the specially-equipped seats, which cost around €100. It will operate on a first-come-first-served basis, with warnings that there is unlikely to be enough money for every family in the country. The law was introduced in response to cases of babies and children dying in cars after being accidentally forgotten by their parents or carers during the scorching heat of summer. It applies not only to Italians but to foreigners visiting the country. An Italian road safety group said that parents “need to hurry” to buy the seats or fit alarms to their existing seats, or risk fines and the docking of licence points. Aside from car accidents and collisions, heat stroke is the main cause of vehicle-related death for children under the age of 15, according to the American Academy of Paediatrics. A small child’s body heats up much faster than that of an adult’s and vital organs start to shut down quicker. -DEBUG [2019-11-09 01:38:40,120] Links: -[1]: https://news.yahoo.com/child-seats-italy-fitted-alarms-211132153.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/wkwZ8jU80m513fkpRekvQA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-GB/the_telegraph_258/b1c2862ed08edcc3a923d85a4dc3b387 - -DEBUG [2019-11-09 01:38:40,133] Title: Jim Jordan to be moved to House Intelligence Committee -DEBUG [2019-11-09 01:38:40,133] Date: Fri, 08 Nov 2019 06:43:37 -0500 -DEBUG [2019-11-09 01:38:40,134] Link: https://news.yahoo.com/jim-jordan-moved-house-intelligence-090813170.html -DEBUG [2019-11-09 01:38:40,135] Description: Jordan will replace Congressman Rick Crawford on the House Intelligence Committee -DEBUG [2019-11-09 01:38:40,135] Links: -[1]: https://news.yahoo.com/jim-jordan-moved-house-intelligence-090813170.html(link) -[2]: http://l2.yimg.com/uu/api/res/1.2/P1oiZSR9PCdnaNHZwQ4k1g--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/cbs_news_897/b702b30415bfa6f67827761aa3e6a22c - -DEBUG [2019-11-09 01:38:40,164] Title: Researchers didn't think humans attacked woolly mammoths – until they uncovered a trap in Mexico -DEBUG [2019-11-09 01:38:40,164] Date: Fri, 08 Nov 2019 12:57:37 -0500 -DEBUG [2019-11-09 01:38:40,164] Link: https://news.yahoo.com/researchers-didnt-think-humans-attacked-175224059.html -DEBUG [2019-11-09 01:38:40,165] Description: Woolly mammoth bones found in Mexico prove that hunters actually attacked the mammal, instead of waiting for them to die -DEBUG [2019-11-09 01:38:40,165] Links: -[1]: https://news.yahoo.com/researchers-didnt-think-humans-attacked-175224059.html(link) -[2]: http://l.yimg.com/uu/api/res/1.2/QudPZtOUjqY6CTO76EmojQ--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en-us/usa_today_news_641/7b2a450e11e613f76319fe6a3451ce25 - -INFO [2019-11-09 01:45:28,134] Website is working -INFO [2019-11-09 01:45:43,131] Successfull converting into html -INFO [2019-11-09 01:47:14,912] Website is working -INFO [2019-11-09 01:47:21,471] Successfull converting into html -INFO [2019-11-09 16:24:13,687] Website is working -DEBUG [2019-11-09 16:24:14,825] Title: Mulvaney asks to join lawsuit over conflicting demands for impeachment testimony - The Washington Post -DEBUG [2019-11-09 16:24:14,826] Date: Sat, 09 Nov 2019 10:36:00 GMT -DEBUG [2019-11-09 16:24:14,826] Link: https://news.google.com/__i/rss/rd/articles/CBMifWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkv0gGMAWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkvP291dHB1dFR5cGU9YW1w?oc=5 -DEBUG [2019-11-09 16:24:14,829] Description: Mulvaney asks to join lawsuit over conflicting demands for impeachment testimony  The Washington PostTrump says he had a second Ukraine call  CBS NewsIvanka Trump: Whistleblower's ID irrelevant to impeachment  POLITICOTrump’s whistleblower attacks set a dangerous precedent  The Washington PostGOP can make a Senate impeachment trial just as painful for Democrats as it will be for Trump  Washington ExaminerView full coverage on Google News -DEBUG [2019-11-09 16:24:14,829] Links: -[1]: https://news.google.com/__i/rss/rd/articles/CBMifWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkv0gGMAWh0dHBzOi8vd3d3Lndhc2hpbmd0b25wb3N0LmNvbS9wb2xpdGljcy8yMDE5LzExLzA5L211bHZhbmV5LWFza3Mtam9pbi1sYXdzdWl0LW92ZXItY29uZmxpY3RpbmctZGVtYW5kcy1pbXBlYWNobWVudC10ZXN0aW1vbnkvP291dHB1dFR5cGU9YW1w?oc=5(link) -[2]: - -INFO [2019-11-09 16:24:42,961] Website is working -INFO [2019-11-09 16:24:43,917] Successfull converting into html -INFO [2019-11-09 17:47:44,450] Website is working -DEBUG [2019-11-09 17:47:52,644] Title: Iran says case open on ex-FBI agent missing there on CIA job -DEBUG [2019-11-09 17:47:52,644] Date: Sat, 09 Nov 2019 03:47:16 -0500 -DEBUG [2019-11-09 17:47:52,644] Link: https://news.yahoo.com/iran-says-case-open-ex-070518919.html -DEBUG [2019-11-09 17:47:52,645] Description: Iran is acknowledging for the first time it has an open case before its Revolutionary Court over the 2007 disappearance of a former FBI agent on an unauthorized CIA mission to the country, renewing questions over what happened to him. In a filing to the United Nations, Iran said the case over Robert Levinson was "on going," without elaborating. -DEBUG [2019-11-09 17:47:52,645] Links: -[1]: https://news.yahoo.com/iran-says-case-open-ex-070518919.html(link) -[2]: http://l2.yimg.com/uu/api/res/1.2/pRZD03lq5AKphL6noxcB1g--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/https://media.zenfs.com/en/ap.org/d83a715f4d3ea8efbb2129040f542c4a - -DEBUG [2019-11-09 17:47:52,651] Title: Huge Hong Kong rally after student dies and lawmakers arrested -DEBUG [2019-11-09 17:47:52,651] Date: Fri, 08 Nov 2019 19:01:45 -0500 -DEBUG [2019-11-09 17:47:52,651] Link: https://news.yahoo.com/hong-kong-pro-democracy-lawmakers-arrested-tensions-soar-060439204.html -DEBUG [2019-11-09 17:47:52,653] Description: Tens of thousands of Hong Kongers packed into a park Saturday night to mourn a student who died during recent clashes as police arrested a group of pro-democracy lawmakers, deepening the city's political crisis. The international finance hub has been upended by five months of huge and increasingly violent pro-democracy protests, but Beijing has refused to give in to most of the movement's demands. Tensions have soared since the death on Friday of Alex Chow, 22, who succumbed to head injuries sustained during a fall as police skirmished with demonstrators inside a car park last weekend. -DEBUG [2019-11-09 17:47:52,653] Links: -[1]: https://news.yahoo.com/hong-kong-pro-democracy-lawmakers-arrested-tensions-soar-060439204.html(link) -[2]: http://l1.yimg.com/uu/api/res/1.2/Cigo.wzpuf0tmOWdzOr1VA--/YXBwaWQ9eXRhY2h5b247aD04Njt3PTEzMDs-/http://media.zenfs.com/en_us/News/afp.com/574f0ffc2ba7be5934fcc6cdef859af0ee007aa3.jpg - diff --git a/final_task/rss_reader/requirements.txt b/final_task/rss_reader/requirements.txt deleted file mode 100644 index 95da44f..0000000 --- a/final_task/rss_reader/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -beautifulsoup4==4.8.1 -dominate==2.4.0 -feedparser==5.2.1 -pdfkit==0.6.1 -twine==2.0.0 \ 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 0631cd3..4c853c2 100644 --- a/final_task/rss_reader/rss_reader.py +++ b/final_task/rss_reader/rss_reader.py @@ -22,7 +22,7 @@ def main(channel, limit): if (args.tohtml or args.topdf): html_document = dominate.document(title="HTML document") - converter.createHtmlStructure(channel, limit, html_document, args.tohtml, args.topdf) + converter.createHtmlStructure(args.source, channel, limit, html_document, args.tohtml, args.topdf) else: for index, item in enumerate(channel.entries): if (index == limit): @@ -35,9 +35,13 @@ def main(channel, limit): print("\nTitle: ", html.unescape(item.title)) print("Date: ", item.published) print("Link: ", item.link, '\n') - print("Description: ", news_parser.getDescription(item.description), '\n') - print("Links:", "\n[1]: ", item.link, "(link)\n[2]: ", - news_parser.checkMediaContent(item), '\n') + description = news_parser.getDescription(item.description) + if(description): + print("Description: ", description, '\n') + print("Links:", "\n[1]: ", item.link, "(link)") + media_content = news_parser.checkMediaContent(item) + if(media_content): + print("\n[2]: ", media_content, '\n') def checkArguments(): @@ -48,21 +52,17 @@ def checkArguments(): return True +class checkConnectionException(Exception): + pass + + def checkConnection(source): '''Check connection to server''' source = Request(source) try: response = urlopen(source) - except HTTPError as e: - print('The server couldn\'t fulfill the request.') - print('Error code: ', e.code) - logger.logging.error("HTTPError: " + str(e)) - sys.exit() - except URLError as e: - print('Failed to reach a server.') - print('Reason: ', e.reason) - logger.logging.error("URLError: " + str(e)) - sys.exit() + except Exception as e: + raise checkConnectionException(e) else: logger.logging.info('Website is working') @@ -78,15 +78,15 @@ def checkConnection(source): get_cache.convertCache(args.source, args.date, args.limit, args.tohtml, args.topdf) else: if (checkArguments()): - checkConnection(args.source) - # set object of feedparser library - channel = feedparser.parse(args.source) - print("Feed: ", channel.feed.title, '\n') - limit = args.limit or len(channel.entries) - - main(channel, limit) - -# for faste paste -# python rss_reader.py https://news.yahoo.com/rss -# python rss_reader.py https://news.google.com/rss -# python rss_reader.py https://www.theguardian.com/world/rss + try: + checkConnection(args.source) + + # set object of feedparser library + channel = feedparser.parse(args.source) + print("Feed: ", channel.feed.title, '\n') + limit = args.limit or len(channel.entries) + + main(channel, limit) + except checkConnectionException as e: + logger.logging.error("Connection error" + str(e)) + print("Connection error: ", e) diff --git a/final_task/rss_reader/topdf.py b/final_task/rss_reader/topdf.py new file mode 100644 index 0000000..51c161a --- /dev/null +++ b/final_task/rss_reader/topdf.py @@ -0,0 +1,40 @@ +import sys +from xhtml2pdf import pisa +import cgi +import html +cgi.escape = html.escape +import urllib +import urllib.parse +import urllib.request + + +def splithost_polyfill(url): + parsed = urllib.parse.urlsplit(url) + netloc = parsed[1] if parsed[1] else None + path = parsed[2] + path += '?' + parsed[3] if parsed[3] else '' + path += '#' + parsed[4] if parsed[4] else '' + return netloc, path + + +def convertHtmlToPdf(sourceHtml, outputFilename): + ''' + 1. replace splithost with custom function splithost_polyfill cause splithost was removed + from python 3.8 + 2. open output file for writing + 3. convert HTML to PDF + 4. close output file + 5. return True on success and False on errors + ''' + urllib.splithost = splithost_polyfill + urllib.request.splithost = splithost_polyfill + + resultFile = open(outputFilename, "w+b") + + pisaStatus = pisa.CreatePDF( + sourceHtml, + dest=resultFile) + + resultFile.close() + + return pisaStatus.err diff --git a/final_task/setup.py b/final_task/setup.py index 86a2e13..48df3fc 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -12,10 +12,10 @@ name="rss-reader", version=get_version(), packages=find_packages(), - install_requires=['beautifulsoup4','dominate','feedparser','pdfkit','twine','urllib3'], + install_requires=['beautifulsoup4','dominate','feedparser', 'urllib3', 'xhtml2pdf'], author="ilya khonenko", author_email="honenkoi@gmail.com", - # url="https://github.com" + url="https://github.com/kingofmidas" description="This is rss-reader", long_description = long_description, long_description_content_type="text/markdown", @@ -26,11 +26,3 @@ keywords="rss reader", python_requires='>=3.8', ) - -#python setup.py sdist bdist_wheel -#(for testing) -#python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/* -#python -m pip install --index-url https://test.pypi.org/simple/ --no-deps ilyakhonenko (in venv) -#(for real) -#python -m twine upload dist/* (https://pypi.org by default) -#pip install [your-package] From 534d3cafad9d7562ea60b953b59ea707ed81458b Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Fri, 22 Nov 2019 04:03:30 +0300 Subject: [PATCH 13/15] 5 iteration: add colorize mode with --colorize argument. 6 iteration: create web server on flask with postgresql. create dockerfile and docker-compose for docker containers. --- final_task/README.md | 11 +- final_task/rss_reader/.dockerignore | 3 + final_task/rss_reader/.env.dev | 1 + final_task/rss_reader/app.py | 48 ++++++++ .../rss_reader/{ => client}/arg_parser.py | 13 +- final_task/rss_reader/client/logg.py | 19 +++ final_task/rss_reader/client/parser.log | 67 +++++++++++ final_task/rss_reader/client/rss_reader.py | 111 ++++++++++++++++++ final_task/rss_reader/collect_news.py | 45 +++++++ final_task/rss_reader/converter.py | 50 ++------ final_task/rss_reader/docker-compose.yml | 33 ++++++ final_task/rss_reader/dockerfile | 19 +++ final_task/rss_reader/get_cache.py | 85 ++++++++------ final_task/rss_reader/{logger.py => logg.py} | 7 +- final_task/rss_reader/news_parser.py | 65 +++++----- final_task/rss_reader/requirements.txt | 9 ++ final_task/rss_reader/rss_reader.py | 92 --------------- final_task/rss_reader/style.css | 28 ----- final_task/rss_reader/topdf.py | 33 +++--- final_task/rss_reader/version.py | 2 +- final_task/setup.py | 2 +- 21 files changed, 490 insertions(+), 253 deletions(-) create mode 100644 final_task/rss_reader/.dockerignore create mode 100644 final_task/rss_reader/.env.dev create mode 100644 final_task/rss_reader/app.py rename final_task/rss_reader/{ => client}/arg_parser.py (69%) create mode 100644 final_task/rss_reader/client/logg.py create mode 100644 final_task/rss_reader/client/parser.log create mode 100644 final_task/rss_reader/client/rss_reader.py create mode 100644 final_task/rss_reader/collect_news.py create mode 100644 final_task/rss_reader/docker-compose.yml create mode 100644 final_task/rss_reader/dockerfile rename final_task/rss_reader/{logger.py => logg.py} (91%) create mode 100644 final_task/rss_reader/requirements.txt delete mode 100644 final_task/rss_reader/rss_reader.py delete mode 100644 final_task/rss_reader/style.css diff --git a/final_task/README.md b/final_task/README.md index 5a4d630..90d1b69 100644 --- a/final_task/README.md +++ b/final_task/README.md @@ -51,7 +51,7 @@ pip install rss-reader ## Data caching -I wrote a program that creates a database for convenient storage of news. The **sqlite3** database (built into python) is perfectly suited for this. Pictures are also stored in the database in binary format. +I wrote a program that creates a database for convenient storage of news. The **postgresql** database is perfectly suited for this. Pictures are also stored in the database in binary format. ## Converting @@ -66,4 +66,13 @@ with html_document: h2("Title: " + news_title) p("Link: " + news_link) p("Description: " + news_description) +``` + +To convert data to pdf format from html document, I used the **xhtml2pdf** library. + +Example: +``` +from xhtml2pdf import pisa + +pdf_file = pisa.CreatePDF(sourceHtmlFile) ``` \ No newline at end of file diff --git a/final_task/rss_reader/.dockerignore b/final_task/rss_reader/.dockerignore new file mode 100644 index 0000000..c9af2ac --- /dev/null +++ b/final_task/rss_reader/.dockerignore @@ -0,0 +1,3 @@ +__pycache__/ +.vscode/ +client/__pycache__/ diff --git a/final_task/rss_reader/.env.dev b/final_task/rss_reader/.env.dev new file mode 100644 index 0000000..b6619f5 --- /dev/null +++ b/final_task/rss_reader/.env.dev @@ -0,0 +1 @@ +FLASK_ENV=development \ No newline at end of file diff --git a/final_task/rss_reader/app.py b/final_task/rss_reader/app.py new file mode 100644 index 0000000..7119e33 --- /dev/null +++ b/final_task/rss_reader/app.py @@ -0,0 +1,48 @@ +from flask import Flask, request, Response, send_from_directory +import os +import sys + +from . import collect_news, version, get_cache, logg + + +app = Flask(__name__) + + +@app.route('/print/', methods=['GET', 'POST']) +def getNews(): + req = request.get_json() + news = collect_news.collectNews(req['limit'], req['tojson'], req['tohtml'], req['topdf'], req['color'], req['url']) + return sendResponse(req, news) + + +@app.route('/getcache/') +def getCacheNews(): + req = request.get_json() + news = get_cache.getCache(req['limit'], req['tojson'], req['tohtml'], req['topdf'], req['color'], req['date']) + return sendResponse(req, news) + + +def sendResponse(req, news): + if(req['topdf']): + try: + return send_from_directory(req['topdf'], filename=news, as_attachment=True) + except FileNotFoundError: + abort(404) + else: + return Response(news) + + +@app.route('/version/', methods=['GET', 'POST']) +def getVersion(): + req = request.get_json() + return version.VERSION + + +@app.route('/verbose/', methods=['GET', 'POST']) +def setLogging(): + logg.makeVerbose() + return '' + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/final_task/rss_reader/arg_parser.py b/final_task/rss_reader/client/arg_parser.py similarity index 69% rename from final_task/rss_reader/arg_parser.py rename to final_task/rss_reader/client/arg_parser.py index 80ad36a..47e4f9a 100644 --- a/final_task/rss_reader/arg_parser.py +++ b/final_task/rss_reader/client/arg_parser.py @@ -1,18 +1,19 @@ import argparse -import version -def createArgparser(): + +def createArgparser(vers): '''Add argument commands''' arguments = argparse.ArgumentParser(description='Pure Python command-line RSS reader') - arguments.add_argument('source', type=str, help='RSS URL') - arguments.add_argument('--version', action='version', version=f'{version.VERSION}', - help='Print version info') + arguments.add_argument('source', type=str, nargs='?', help='RSS URL') + arguments.add_argument('--version', action='version', version=f'{vers}', + help='Print version info') arguments.add_argument('--json', action='store_true', help='Print result as JSON in stdout') arguments.add_argument('--verbose', action='store_true', help='Outputs verbose') arguments.add_argument('--limit', action='store', type=int, help='Limit news topics') arguments.add_argument('--date', action='store', help='Print news from the specified day') arguments.add_argument('--tohtml', action='store', help='Convert news in html format') arguments.add_argument('--topdf', action='store', help='Convert news in pdf format') + arguments.add_argument('--colorize', action='store_true', help='print news in colorized mode') - return arguments.parse_args() \ No newline at end of file + return arguments.parse_args() diff --git a/final_task/rss_reader/client/logg.py b/final_task/rss_reader/client/logg.py new file mode 100644 index 0000000..6671da3 --- /dev/null +++ b/final_task/rss_reader/client/logg.py @@ -0,0 +1,19 @@ +import logging +import sys + + +# Set basic configs for logging +stdoutHandler = logging.StreamHandler(sys.stdout) +fileHandler = logging.FileHandler("parser.log", "a", encoding="utf-8") +logging.basicConfig(format=u'%(levelname)-8s [%(asctime)s] %(message)s', + level=logging.DEBUG, + handlers=[fileHandler]) + + +def makeVerbose(): + ''' + 1. print logs in stdout if there is --verbose argument + ''' + stderrLogger = logging.StreamHandler() + stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) + logging.getLogger().addHandler(stderrLogger) diff --git a/final_task/rss_reader/client/parser.log b/final_task/rss_reader/client/parser.log new file mode 100644 index 0000000..843ef23 --- /dev/null +++ b/final_task/rss_reader/client/parser.log @@ -0,0 +1,67 @@ +DEBUG [2019-11-19 12:17:24,844] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-19 12:17:24,860] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-19 12:17:24,875] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-19 12:17:24,886] http://127.0.0.1:5000 "GET /verbose/ HTTP/1.1" 200 0 +INFO [2019-11-19 12:17:25,297] Website is working +DEBUG [2019-11-19 12:17:25,305] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-19 12:17:32,106] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-19 12:33:21,760] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-19 12:34:28,544] Starting new HTTP connection (1): 127.0.0.1:5000 +ERROR [2019-11-19 12:34:29,548] ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /version/ (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение')) +DEBUG [2019-11-19 12:35:02,649] Starting new HTTP connection (1): 127.0.0.1:5000 +ERROR [2019-11-19 12:35:03,656] ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /version/ (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение')) +DEBUG [2019-11-19 12:35:12,507] Starting new HTTP connection (1): 127.0.0.1:5000 +ERROR [2019-11-19 12:35:13,511] ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /version/ (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение')) +DEBUG [2019-11-19 12:35:25,576] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-19 12:35:25,587] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-21 02:50:36,539] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-21 02:50:36,574] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +ERROR [2019-11-21 02:50:58,359] Connection error +DEBUG [2019-11-22 02:15:05,272] Starting new HTTP connection (1): 127.0.0.1:5000 +ERROR [2019-11-22 02:15:05,366] ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +DEBUG [2019-11-22 02:15:27,113] Starting new HTTP connection (1): 127.0.0.1:5000 +ERROR [2019-11-22 02:15:27,125] ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +DEBUG [2019-11-22 03:00:39,414] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:00:39,472] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-22 03:00:41,260] Website is working +DEBUG [2019-11-22 03:00:41,267] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:00:44,885] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 777 +DEBUG [2019-11-22 03:13:33,562] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:13:33,672] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +INFO [2019-11-22 03:13:35,075] Website is working +DEBUG [2019-11-22 03:13:35,082] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:13:35,152] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None +DEBUG [2019-11-22 03:23:23,729] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:23:23,740] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-22 03:23:24,220] Website is working +DEBUG [2019-11-22 03:23:24,227] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:23:25,694] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 800 +DEBUG [2019-11-22 03:23:48,151] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:23:48,162] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-22 03:23:48,190] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:23:48,246] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None +DEBUG [2019-11-22 03:54:12,252] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:54:12,441] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-22 03:54:12,725] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:54:13,055] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 0 +DEBUG [2019-11-22 03:54:21,601] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:54:21,614] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-22 03:54:22,684] Website is working +DEBUG [2019-11-22 03:54:22,692] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:54:34,691] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-22 03:54:50,161] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:54:50,179] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-22 03:54:50,223] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:54:50,361] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 0 +DEBUG [2019-11-22 03:55:00,515] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:55:00,526] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-22 03:55:00,560] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:55:00,596] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-22 03:55:04,853] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:55:04,875] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-22 03:55:04,913] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:55:04,988] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-22 03:57:42,465] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:57:42,486] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-22 03:57:42,528] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-22 03:57:42,584] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None diff --git a/final_task/rss_reader/client/rss_reader.py b/final_task/rss_reader/client/rss_reader.py new file mode 100644 index 0000000..72ceffa --- /dev/null +++ b/final_task/rss_reader/client/rss_reader.py @@ -0,0 +1,111 @@ +from urllib.request import Request, urlopen +from datetime import datetime +from colored import stylize +import arg_parser +import feedparser +import requests +import colored +import html +import sys +import os + +import logg + + +def main(version): + + args = arg_parser.createArgparser(version) + params = dict() + + if (args.verbose): + logg.makeVerbose() + requests.get('http://127.0.0.1:5000/verbose/') + + if(args.colorize): + color = colored.fg(0)+colored.bg(220) + else: + color = colored.attr('reset') + + params = {'limit':args.limit, 'tojson': args.json, + 'tohtml':args.tohtml, 'topdf':args.topdf, 'color':color} + + if (args.date): + params['date'] = args.date + r = requests.get('http://127.0.0.1:5000/getcache/', json=params) + news = r.text + + if(args.tohtml): + saveHTML(news, args.tohtml) + elif(args.topdf): + pdf_document = bytes(news, 'utf-8') + savePDF(pdf_document, args.topdf) + else: + print(news) + else: + try: + checkConnection(args.source) + params['url'] = args.source + r = requests.get('http://127.0.0.1:5000/print/', json=params) + news = r.text + + if(args.tohtml): + saveHTML(news, args.tohtml) + elif(args.topdf): + pdf_document = bytes(news, 'utf-8') + savePDF(pdf_document, args.topdf) + else: + print(news) + except Exception as e: + logg.logging.error("Connection error" + str(e)) + print("Connection error: ", e) + + + +def saveHTML(html_document, html_path): + ''' + 1. create folder with html file + 2. write html structure in file + ''' + if not os.path.exists(html_path): + os.makedirs(html_path) + time_name = datetime.strftime(datetime.now(), "%H%M%S") + file_name = 'NewsFeed' + '-' + time_name + '.html' + html_file = os.path.join(html_path, file_name) + + with open(html_file, 'w', encoding='utf-8') as f: + f.write(str(html_document)) + + +def savePDF(doc, pdf_path): + ''' + 1. create folder with pdf file + 2. write pdf in file + ''' + if not os.path.exists(pdf_path): + os.makedirs(pdf_path) + time_name = datetime.strftime(datetime.now(), "%H%M%S") + file_name = 'NewsFeed' + '-' + time_name + '.pdf' + pdf_file = os.path.join(pdf_path, file_name) + + with open(pdf_file, "w+b") as resultFile: + resultFile.write(doc) + + +def checkConnection(source): + '''Check connection to server''' + try: + source = Request(source) + response = urlopen(source) + except Exception as e: + raise Exception(e) + else: + logg.logging.info('Website is working') + + +if __name__ == "__main__": + try: + version = (requests.get('http://127.0.0.1:5000/version/')).text + main(version) + except requests.exceptions.ConnectionError as error: + print("ConnectionError: " + str(error)) + logg.logging.error("ConnectionError: " + str(error)) diff --git a/final_task/rss_reader/collect_news.py b/final_task/rss_reader/collect_news.py new file mode 100644 index 0000000..f8044e7 --- /dev/null +++ b/final_task/rss_reader/collect_news.py @@ -0,0 +1,45 @@ +import feedparser +import html + +from . import logg, converter, news_parser + + +def collectNews(limit, tojson, tohtml, topdf, color, source): + ''' + 1. cache news + 2. create html or pdf document + 3. or return news in json or normal format + ''' + news = list() + + channel = feedparser.parse(source) + news.append(color + "Feed: " + channel.feed.title + '\n') + limit = limit or len(channel.entries) + + news_parser.cacheNews(source, channel) + + if (tohtml or topdf): + html_doc = converter.createHtmlStructure(channel, limit, tohtml, topdf) + return html_doc + else: + for index, item in enumerate(channel.entries): + if (index == limit): + break + logg.createLogs(item) + + if (tojson): + news.append(news_parser.intoJson(item)) + else: + news.append("\nTitle: " + html.unescape(item.title)) + news.append("\nDate: " + item.published) + news.append("\nLink: " + item.link + '\n') + description = news_parser.getDescription(item.description) + if(description): + news.append("Description: " + description + '\n') + news.append("Links:" + "\n[1]: " + item.link + "(link)") + media_content = news_parser.checkMediaContent(item) + if(media_content): + news.append("\n[2]: " + media_content + '\n') + + return news + diff --git a/final_task/rss_reader/converter.py b/final_task/rss_reader/converter.py index ddd72a5..2e73db6 100644 --- a/final_task/rss_reader/converter.py +++ b/final_task/rss_reader/converter.py @@ -1,23 +1,18 @@ -import os -import re from dominate.tags import div, h2, img, p, link +import dominate from datetime import datetime +import os import html -import news_parser -import logger -import topdf +from . import news_parser, topdf -def createHtmlStructure(url, channel, limit, html_document, html_path, pdf_path): +def createHtmlStructure(channel, limit, html_path, pdf_path): ''' 1. in loop create html structure - 2. create html document or convert html structure into pdf + 2. return html_structure or file name of pdf document ''' - #uncomment if u want html with styles - # with html_document.head: - # styles = os.path.abspath('style.css') - # link(rel='stylesheet', href=styles) + html_document = dominate.document(title="HTML document") for index, item in enumerate(channel.entries): if (index == limit): @@ -34,41 +29,22 @@ def createHtmlStructure(url, channel, limit, html_document, html_path, pdf_path) p("Description: " + description) if (html_path): - intoHTML(html_document, html_path, url) + return str(html_document) else: - intoPDF(html_document, pdf_path, url) - - -def intoHTML(html_document, html_path, url): - ''' - 1. create folder with html file - 2. write html structure in file - ''' - if not os.path.exists(html_path): - os.makedirs(html_path) - time_name = datetime.strftime(datetime.now(), "%H%M%S") - html_file = html_path + '/' + getSiteName(url) + '-' + time_name + '.html' - - with open(html_file, 'w', encoding='utf-8') as f: - f.write(str(html_document)) - logger.logging.info("Successfull converting into html") - - -def getSiteName(url): - regex = re.search(r"^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?([a-z0-9]+[\-\.]{1}[a-z0-9]+)*(\.[a-z]{2,5})(\/.*)?$", url) - - return ''.join(filter(None,regex.group(2,3))) + return intoPDF(html_document, pdf_path) -def intoPDF(doc, pdf_path, url): +def intoPDF(doc, pdf_path): ''' 1. create folder with pdf file 2. convert html into pdf + 3. return only filename for send_from_directory function ''' if not os.path.exists(pdf_path): os.makedirs(pdf_path) time_name = datetime.strftime(datetime.now(), "%H%M%S") - pdf_file = pdf_path + '/' + getSiteName(url) + '-' + time_name + '.pdf' + file_name = 'NewsFeed' + '-' + time_name + '.pdf' + pdf_file = os.path.join(pdf_path, file_name) topdf.convertHtmlToPdf(str(doc), pdf_file) - logger.logging.info("Successfull converting into pdf") + return file_name diff --git a/final_task/rss_reader/docker-compose.yml b/final_task/rss_reader/docker-compose.yml new file mode 100644 index 0000000..3a62650 --- /dev/null +++ b/final_task/rss_reader/docker-compose.yml @@ -0,0 +1,33 @@ +version: '3.7' + +services: + + flask: + build: . + restart: always + container_name: flask_app + ports: + - 5000:5000 + volumes: + - .:/rss_reader_app + env_file: + - ./.env.dev + depends_on: + - db + links: + - db + + db: + image: postgres:12.1-alpine + restart: always + ports: + - 5432:5432 + environment: + POSTGRES_DB: postgres + POSTGRES_USER: postgres + POSTGRES_PASSWORD: rssreader + volumes: + - 'pg_data:/var/lib/postgresql/data' + +volumes: + pg_data: \ No newline at end of file diff --git a/final_task/rss_reader/dockerfile b/final_task/rss_reader/dockerfile new file mode 100644 index 0000000..0715940 --- /dev/null +++ b/final_task/rss_reader/dockerfile @@ -0,0 +1,19 @@ +FROM python:3.8.0-slim + +WORKDIR /rss_reader_app + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# install psycopg2 dependencies +RUN apt update \ + && apt install -y libpq-dev gcc python3-dev + +COPY ./requirements.txt /rss_reader_app/requirements.txt +RUN pip install -r requirements.txt + +# copy project +COPY . /rss_reader_app + +CMD [ "flask", "run" , "--host=0.0.0.0"] \ No newline at end of file diff --git a/final_task/rss_reader/get_cache.py b/final_task/rss_reader/get_cache.py index 24d51c4..dcf7685 100644 --- a/final_task/rss_reader/get_cache.py +++ b/final_task/rss_reader/get_cache.py @@ -1,14 +1,14 @@ -import dominate from dominate.tags import div, h2, img, p, link -import sqlite3 +from contextlib import closing from datetime import datetime +import dominate +import psycopg2 +import base64 import time +import json import os -import base64 -import logger -import converter -import rss_reader +from . import logg, converter, news_parser def dateToStamp(arg_date): @@ -20,53 +20,64 @@ def dateToStamp(arg_date): return arg_date -def convertCache(url, arg_date, limit, html_path, pdf_path): +def getCache(limit, tojson, html_path, pdf_path, color, arg_date): ''' 1. connect to database 2. select from table news with published date equals --date 3. convert news into html or pdf if there are --tohtml or --topdf arguments 3. or print news in stdout ''' - conn = sqlite3.connect("newsdatabase.db") - cursor = conn.cursor() - try: - cursor.execute("SELECT title, link, image, description FROM news WHERE pub_date_stamp >= ? and pub_date_stamp < ? ", - (dateToStamp(arg_date), dateToStamp(int(arg_date) + 1))) - except sqlite3.OperationalError as e: - print("No such table") - logger.logging.error(str(e)) + with closing(psycopg2.connect(database="postgres",user='postgres',password='rssreader',host='db',port='5432')) as con: + with con.cursor() as cur: + cur.execute('''SELECT title, link, image, description FROM news WHERE pub_date_stamp >= %s and pub_date_stamp < %s''', + (dateToStamp(arg_date), dateToStamp(int(arg_date) + 1))) - records = cursor.fetchall() + records = cur.fetchall() + if (html_path or pdf_path): + return createHtmlStructure(records, limit, html_path, pdf_path) + else: + return getNewsFromDB(records, limit, tojson, color) - if (html_path is None and pdf_path is None): - for index, row in enumerate(records): - if(limit and index == limit): - break - print("\nTitle: ", row[0]) - print("Link: ", row[1], '\n') - if (row[3]): - print("Description: ", row[3], '\n') - else: - createHtmlStructure(records, limit, html_path, pdf_path, url) + except psycopg2.ProgrammingError as e: + print("psycopg2.ProgrammingError: " + str(e)) + logg.logging.error(str(e)) - cursor.close() - conn.close() + +def getNewsFromDB(records, limit, tojson, color): + ''' + 1. create list for news + 2. collect all cache news from db in list + ''' + news = list() + for index, row in enumerate(records): + if(limit and index == limit): + break + if (tojson): + json_news = { + 'Title: ': row[0], + 'Link: ': row[1], + } + if(row[3]): + json_news['Description'] = row[3] + news.append(json.dumps(json_news)) + else: + news.append(color + "\nTitle: " + row[0]) + news.append("\nLink: " + row[1] + '\n') + if (row[3]): + news.append("Description: " + row[3] + '\n') + return news -def createHtmlStructure(records, limit, html_path, pdf_path, url): +def createHtmlStructure(records, limit, html_path, pdf_path): ''' 1. in loop create html structure 2. create html document or convert html structure into pdf ''' html_document = dominate.document(title="HTML document") - #uncomment if u want html with styles - # with html_document.head: - # styles = os.path.abspath('style.css') - # link(rel='stylesheet', href=styles) - + for index, row in enumerate(records): - if(limit and index==limit): + if(limit and index == limit): break with html_document: with div(): @@ -78,6 +89,6 @@ def createHtmlStructure(records, limit, html_path, pdf_path, url): p("Description: " + row[3]) if (html_path): - converter.intoHTML(html_document, html_path, url) + return str(html_document) elif (pdf_path): - converter.intoPDF(html_document, pdf_path, url) \ No newline at end of file + return converter.intoPDF(html_document, pdf_path) diff --git a/final_task/rss_reader/logger.py b/final_task/rss_reader/logg.py similarity index 91% rename from final_task/rss_reader/logger.py rename to final_task/rss_reader/logg.py index 2180ec8..3869a07 100644 --- a/final_task/rss_reader/logger.py +++ b/final_task/rss_reader/logg.py @@ -1,7 +1,8 @@ import logging -import news_parser import sys +from . import news_parser + # Set basic configs for logging stdoutHandler = logging.StreamHandler(sys.stdout) @@ -15,7 +16,7 @@ def makeVerbose(): ''' 1. print logs in stdout if there is --verbose argument ''' - stderrLogger=logging.StreamHandler() + stderrLogger = logging.StreamHandler() stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) logging.getLogger().addHandler(stderrLogger) @@ -29,4 +30,4 @@ def createLogs(item): logging.debug("Link: " + str(item.link)) logging.debug("Description: " + news_parser.getDescription(item.description)) logging.debug("Links:"+"\n[1]: " + str(item.link) + - "(link)\n[2]: " + str(news_parser.checkMediaContent(item)) + '\n') \ No newline at end of file + "(link)\n[2]: " + str(news_parser.checkMediaContent(item)) + '\n') diff --git a/final_task/rss_reader/news_parser.py b/final_task/rss_reader/news_parser.py index e729477..58090ff 100644 --- a/final_task/rss_reader/news_parser.py +++ b/final_task/rss_reader/news_parser.py @@ -1,11 +1,15 @@ +from contextlib import closing from bs4 import BeautifulSoup -import json -import os from datetime import datetime -import time -import sqlite3 -import html import requests +import sqlite3 +import psycopg2 +import json +import time +import html +import os + +from . import logg def checkMediaContent(item): @@ -32,8 +36,10 @@ def intoJson(item): } description = getDescription(item.description) media_link = checkMediaContent(item) - if(description): json_news['Description: '] = description - if(media_link): json_news['Media link: '] = media_link + if(description): + json_news['Description: '] = description + if(media_link): + json_news['Media link: '] = media_link return json.dumps(json_news) @@ -43,25 +49,22 @@ def cacheNews(url, channel): 2. create table in database 3. insert news into table ''' - conn = sqlite3.connect("newsdatabase.db") - cursor = conn.cursor() - try: - cursor.execute("""CREATE TABLE news - (title text, link text, image BLOB, - description text, pub_date_stamp real, - UNIQUE (title, link, pub_date_stamp)) - """) - except Exception: - print("Table already exists") - - insertNewsIntoTable(channel, cursor) - conn.commit() - cursor.close() - conn.close() - - -def insertNewsIntoTable(channel, cursor): + with closing(psycopg2.connect(database="postgres",user='postgres',password='rssreader', + host='db',port='5432')) as con: + with con.cursor() as cur: + cur.execute("""CREATE TABLE IF NOT EXISTS news + (title text, link text, image bytea, + description text, pub_date_stamp real, + UNIQUE (title, link, pub_date_stamp)) + """) + insertNewsIntoTable(channel, cur) + con.commit() + except (Exception, psycopg2.DatabaseError) as e: + logg.logging.error(str(e)) + + +def insertNewsIntoTable(channel, con): ''' 1. fill table with news 2. convert date into timestamp @@ -77,13 +80,13 @@ def insertNewsIntoTable(channel, cursor): media_content = checkMediaContent(item) if (media_content): response = requests.get(media_content) - image = sqlite3.Binary(response.content) + image = psycopg2.Binary(response.content) row = (html.unescape(item.title), item.link, image, description, pub_date_stamp) try: - cursor.execute("INSERT INTO news VALUES (?,?,?,?,?)", row) - except sqlite3.IntegrityError: - pass + con.execute("INSERT INTO news VALUES (%s,%s,%s,%s,%s)", row) + except (Exception, psycopg2.DatabaseError) as e: + logg.logging.error(str(e)) def isEmpty(cursor): @@ -104,8 +107,8 @@ def getPublishedDate(pub_date): ''' pub_date = ((pub_date).split(' ')[1:5]) - month = {'Jan':'01','Feb':'02','Mar':'03','Apr':'04','May':'05','Jun':'06', - 'Jul':'07','Aug':'08','Sep':'09','Oct':'10','Nov':'11','Dec':'12'} + month = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', + 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'} pub_date[1] = month[pub_date[1]] pub_date = ' '.join(pub_date) diff --git a/final_task/rss_reader/requirements.txt b/final_task/rss_reader/requirements.txt new file mode 100644 index 0000000..5fc88fb --- /dev/null +++ b/final_task/rss_reader/requirements.txt @@ -0,0 +1,9 @@ +xhtml2pdf==0.2.3 +urllib3==1.25.6 +feedparser==5.2.1 +dominate==2.4.0 +beautifulsoup4==4.8.1 +colored==1.4.0 +requests==2.22.0 +psycopg2==2.8.4 +Flask==1.1.1 \ No newline at end of file diff --git a/final_task/rss_reader/rss_reader.py b/final_task/rss_reader/rss_reader.py deleted file mode 100644 index 4c853c2..0000000 --- a/final_task/rss_reader/rss_reader.py +++ /dev/null @@ -1,92 +0,0 @@ -import arg_parser -import feedparser -import sys -from urllib.request import Request, urlopen -from urllib.error import URLError, HTTPError -import dominate -import html - -import news_parser -import logger -import get_cache -import converter - - -def main(channel, limit): - ''' - 1. cache news - 2. create html or pdf document - 3. or print in stdout news in json or normal format - ''' - news_parser.cacheNews(args.source, channel) - - if (args.tohtml or args.topdf): - html_document = dominate.document(title="HTML document") - converter.createHtmlStructure(args.source, channel, limit, html_document, args.tohtml, args.topdf) - else: - for index, item in enumerate(channel.entries): - if (index == limit): - break - logger.createLogs(item) - - if (args.json): - print(news_parser.intoJson(item)) - else: - print("\nTitle: ", html.unescape(item.title)) - print("Date: ", item.published) - print("Link: ", item.link, '\n') - description = news_parser.getDescription(item.description) - if(description): - print("Description: ", description, '\n') - print("Links:", "\n[1]: ", item.link, "(link)") - media_content = news_parser.checkMediaContent(item) - if(media_content): - print("\n[2]: ", media_content, '\n') - - -def checkArguments(): - '''Check if in argument are --version or --help''' - if ('--version' in sys.argv or '--help' in sys.argv or '-h' in sys.argv): - return False - else: - return True - - -class checkConnectionException(Exception): - pass - - -def checkConnection(source): - '''Check connection to server''' - source = Request(source) - try: - response = urlopen(source) - except Exception as e: - raise checkConnectionException(e) - else: - logger.logging.info('Website is working') - - -if __name__ == "__main__": - - args = arg_parser.createArgparser() - - if (args.verbose): - logger.makeVerbose() - - if (args.date): - get_cache.convertCache(args.source, args.date, args.limit, args.tohtml, args.topdf) - else: - if (checkArguments()): - try: - checkConnection(args.source) - - # set object of feedparser library - channel = feedparser.parse(args.source) - print("Feed: ", channel.feed.title, '\n') - limit = args.limit or len(channel.entries) - - main(channel, limit) - except checkConnectionException as e: - logger.logging.error("Connection error" + str(e)) - print("Connection error: ", e) diff --git a/final_task/rss_reader/style.css b/final_task/rss_reader/style.css deleted file mode 100644 index 13bfecd..0000000 --- a/final_task/rss_reader/style.css +++ /dev/null @@ -1,28 +0,0 @@ -div { - border: 2px solid black; - border-radius: 10px; - padding: 2px 20px 20px 20px; - margin: auto; - box-sizing: border-box; - - background-color: rgb(20, 166, 192); - - width: 700px; - height: 300px; - - display: flex; - flex-direction: column; - overflow-y: auto; -} -div h3{ - font-family: Courier, monospace; -} -div p { - font-family: Sans MS, Comic Sans, cursive; -} -body { - background-color: black; -} -div img { - width: 150px; -} \ No newline at end of file diff --git a/final_task/rss_reader/topdf.py b/final_task/rss_reader/topdf.py index 51c161a..c11c454 100644 --- a/final_task/rss_reader/topdf.py +++ b/final_task/rss_reader/topdf.py @@ -1,14 +1,20 @@ -import sys from xhtml2pdf import pisa -import cgi +import urllib.request +import urllib.parse +import urllib import html +import cgi +import sys + + cgi.escape = html.escape -import urllib -import urllib.parse -import urllib.request def splithost_polyfill(url): + '''This function replaces deprecated splithost function. + Same result is achieved by mean of splitting original URL into components + and joining extracted components into host and path strings, retaining + format of original function''' parsed = urllib.parse.urlsplit(url) netloc = parsed[1] if parsed[1] else None path = parsed[2] @@ -23,18 +29,13 @@ def convertHtmlToPdf(sourceHtml, outputFilename): from python 3.8 2. open output file for writing 3. convert HTML to PDF - 4. close output file - 5. return True on success and False on errors + 4. return True on success and False on errors ''' urllib.splithost = splithost_polyfill urllib.request.splithost = splithost_polyfill - resultFile = open(outputFilename, "w+b") - - pisaStatus = pisa.CreatePDF( - sourceHtml, - dest=resultFile) - - resultFile.close() - - return pisaStatus.err + with open(outputFilename, "w+b") as resultFile: + pisaStatus = pisa.CreatePDF( + sourceHtml, + dest=resultFile) + return pisaStatus.err diff --git a/final_task/rss_reader/version.py b/final_task/rss_reader/version.py index b553b53..ef730b2 100644 --- a/final_task/rss_reader/version.py +++ b/final_task/rss_reader/version.py @@ -1 +1 @@ -VERSION = '0.4' \ No newline at end of file +VERSION = '0.6' \ No newline at end of file diff --git a/final_task/setup.py b/final_task/setup.py index 48df3fc..cb80a21 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -12,7 +12,7 @@ name="rss-reader", version=get_version(), packages=find_packages(), - install_requires=['beautifulsoup4','dominate','feedparser', 'urllib3', 'xhtml2pdf'], + install_requires=['beautifulsoup4','dominate','feedparser', 'urllib3', 'xhtml2pdf', 'colored'], author="ilya khonenko", author_email="honenkoi@gmail.com", url="https://github.com/kingofmidas" From e0462c394618133bbbd77fd07de879ff7719c1a5 Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Mon, 25 Nov 2019 23:48:39 +0300 Subject: [PATCH 14/15] Add tests. Change structure of code. Last commit :( --- .gitignore | 5 +- final_task/README.md | 23 +- final_task/rss_reader/app.py | 5 +- final_task/rss_reader/client/__init__.py | 0 final_task/rss_reader/client/parser.log | 486 ++++++++++++++++++--- final_task/rss_reader/client/rss_reader.py | 5 +- final_task/rss_reader/collect_news.py | 14 +- final_task/rss_reader/converter.py | 25 +- final_task/rss_reader/docker-compose.yml | 6 +- final_task/rss_reader/get_cache.py | 40 +- final_task/rss_reader/logg.py | 2 +- final_task/rss_reader/news_parser.py | 73 ++-- final_task/rss_reader/requirements.txt | 11 +- final_task/rss_reader/topdf.py | 16 +- final_task/setup.py | 8 +- final_task/tests/collectnews_test.py | 11 + final_task/tests/expected_results.py | 35 ++ final_task/tests/getcache_test.py | 55 +++ final_task/tests/parser_test.py | 63 +++ final_task/tests/topdf_test.py | 24 + 20 files changed, 732 insertions(+), 175 deletions(-) create mode 100644 final_task/rss_reader/client/__init__.py create mode 100644 final_task/tests/collectnews_test.py create mode 100644 final_task/tests/expected_results.py create mode 100644 final_task/tests/getcache_test.py create mode 100644 final_task/tests/parser_test.py create mode 100644 final_task/tests/topdf_test.py diff --git a/.gitignore b/.gitignore index b18621a..c0fee05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ __pycache__/ .vscode/ -.idea/ final_task/rss_reader/parser.log -final_task/rss_reader/newsdatabase.db \ No newline at end of file +final_task/rss_reader/client/parser.log +final_task/rss_reader/client/__pycache__/ +final_task/env/ \ No newline at end of file diff --git a/final_task/README.md b/final_task/README.md index 90d1b69..5ada52f 100644 --- a/final_task/README.md +++ b/final_task/README.md @@ -45,9 +45,14 @@ The recommended way to install rss-reader is with pip: ``` -pip install rss-reader +pip install rssreaderih ``` +or from source distribution: + +``` +python setup.py install +``` ## Data caching @@ -75,4 +80,18 @@ Example: from xhtml2pdf import pisa pdf_file = pisa.CreatePDF(sourceHtmlFile) -``` \ No newline at end of file +``` + +## Deploying + +The application has a **dockerfile** for creating an application image. And **docker-compose.yml** file for linking application and database images. + +To deploy application use this command: +``` +docker-compose up +``` + +If you made changes to the application then use command: +``` +docker-compose up --build +``` diff --git a/final_task/rss_reader/app.py b/final_task/rss_reader/app.py index 7119e33..840ecf4 100644 --- a/final_task/rss_reader/app.py +++ b/final_task/rss_reader/app.py @@ -18,7 +18,10 @@ def getNews(): @app.route('/getcache/') def getCacheNews(): req = request.get_json() - news = get_cache.getCache(req['limit'], req['tojson'], req['tohtml'], req['topdf'], req['color'], req['date']) + if(req['tohtml'] or req['topdf']): + news = get_cache.createHtmlFromDB(req['limit'], req['tohtml'], req['topdf'], req['date']) + else: + news = get_cache.collectNewsFromDB(req['limit'], req['tojson'], req['color'], req['date']) return sendResponse(req, news) diff --git a/final_task/rss_reader/client/__init__.py b/final_task/rss_reader/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/final_task/rss_reader/client/parser.log b/final_task/rss_reader/client/parser.log index 843ef23..330cfac 100644 --- a/final_task/rss_reader/client/parser.log +++ b/final_task/rss_reader/client/parser.log @@ -1,67 +1,419 @@ -DEBUG [2019-11-19 12:17:24,844] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-19 12:17:24,860] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-19 12:17:24,875] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-19 12:17:24,886] http://127.0.0.1:5000 "GET /verbose/ HTTP/1.1" 200 0 -INFO [2019-11-19 12:17:25,297] Website is working -DEBUG [2019-11-19 12:17:25,305] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-19 12:17:32,106] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-19 12:33:21,760] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-19 12:34:28,544] Starting new HTTP connection (1): 127.0.0.1:5000 -ERROR [2019-11-19 12:34:29,548] ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /version/ (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение')) -DEBUG [2019-11-19 12:35:02,649] Starting new HTTP connection (1): 127.0.0.1:5000 -ERROR [2019-11-19 12:35:03,656] ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /version/ (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение')) -DEBUG [2019-11-19 12:35:12,507] Starting new HTTP connection (1): 127.0.0.1:5000 -ERROR [2019-11-19 12:35:13,511] ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /version/ (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение')) -DEBUG [2019-11-19 12:35:25,576] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-19 12:35:25,587] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-21 02:50:36,539] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-21 02:50:36,574] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -ERROR [2019-11-21 02:50:58,359] Connection error -DEBUG [2019-11-22 02:15:05,272] Starting new HTTP connection (1): 127.0.0.1:5000 -ERROR [2019-11-22 02:15:05,366] ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) -DEBUG [2019-11-22 02:15:27,113] Starting new HTTP connection (1): 127.0.0.1:5000 -ERROR [2019-11-22 02:15:27,125] ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) -DEBUG [2019-11-22 03:00:39,414] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:00:39,472] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-22 03:00:41,260] Website is working -DEBUG [2019-11-22 03:00:41,267] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:00:44,885] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 777 -DEBUG [2019-11-22 03:13:33,562] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:13:33,672] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -INFO [2019-11-22 03:13:35,075] Website is working -DEBUG [2019-11-22 03:13:35,082] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:13:35,152] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None -DEBUG [2019-11-22 03:23:23,729] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:23:23,740] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-22 03:23:24,220] Website is working -DEBUG [2019-11-22 03:23:24,227] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:23:25,694] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 800 -DEBUG [2019-11-22 03:23:48,151] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:23:48,162] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-22 03:23:48,190] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:23:48,246] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None -DEBUG [2019-11-22 03:54:12,252] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:54:12,441] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-22 03:54:12,725] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:54:13,055] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 0 -DEBUG [2019-11-22 03:54:21,601] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:54:21,614] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-22 03:54:22,684] Website is working -DEBUG [2019-11-22 03:54:22,692] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:54:34,691] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-22 03:54:50,161] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:54:50,179] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-22 03:54:50,223] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:54:50,361] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 0 -DEBUG [2019-11-22 03:55:00,515] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:55:00,526] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-22 03:55:00,560] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:55:00,596] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-22 03:55:04,853] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:55:04,875] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-22 03:55:04,913] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:55:04,988] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-22 03:57:42,465] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:57:42,486] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-22 03:57:42,528] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-22 03:57:42,584] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 00:44:29,990] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:44:30,191] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +DEBUG [2019-11-23 00:44:30,371] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:44:30,441] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None +DEBUG [2019-11-23 00:45:04,990] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:45:05,055] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +DEBUG [2019-11-23 00:45:05,088] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:45:05,158] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None +DEBUG [2019-11-23 00:47:23,317] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:47:24,570] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 00:47:24,600] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:47:24,644] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 00:48:58,276] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:48:58,289] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 00:48:58,319] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:48:58,367] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 00:49:25,354] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:49:28,491] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 00:49:28,525] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:49:28,572] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 00:50:29,952] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:50:29,963] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 00:50:39,103] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:50:39,121] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 00:50:39,149] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:50:39,179] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1221 +DEBUG [2019-11-23 00:50:58,770] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:51:00,654] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 00:51:00,692] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:51:00,737] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1253 +DEBUG [2019-11-23 00:53:02,785] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:53:02,798] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 00:53:02,829] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:53:02,871] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 00:53:45,795] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:53:45,809] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 00:53:46,876] Website is working +DEBUG [2019-11-23 00:53:46,884] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:53:58,625] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 826 +DEBUG [2019-11-23 00:54:24,876] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:54:24,886] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 00:54:25,295] Website is working +DEBUG [2019-11-23 00:54:25,303] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:54:32,692] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 827 +DEBUG [2019-11-23 00:54:56,654] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:54:56,669] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 00:54:57,137] Website is working +DEBUG [2019-11-23 00:54:57,146] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:55:03,732] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 828 +DEBUG [2019-11-23 00:55:19,733] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:55:19,743] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 00:55:20,150] Website is working +DEBUG [2019-11-23 00:55:20,157] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:55:26,570] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 828 +DEBUG [2019-11-23 00:56:27,423] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:56:29,291] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 00:56:29,731] Website is working +DEBUG [2019-11-23 00:56:29,738] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 00:56:35,738] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None +DEBUG [2019-11-23 01:00:16,159] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:00:17,899] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:00:18,306] Website is working +DEBUG [2019-11-23 01:00:18,314] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:00:24,491] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 886 +DEBUG [2019-11-23 01:00:53,547] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:00:53,559] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:00:53,963] Website is working +DEBUG [2019-11-23 01:00:53,971] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:00:59,399] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 839 +DEBUG [2019-11-23 01:01:36,178] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:01:36,195] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:01:36,234] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:01:36,294] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:01:56,588] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:01:56,599] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:01:56,631] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:01:56,676] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1253 +DEBUG [2019-11-23 01:02:53,313] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:02:53,323] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:02:53,744] Website is working +DEBUG [2019-11-23 01:02:53,751] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:02:59,397] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 776 +DEBUG [2019-11-23 01:03:06,903] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:03:08,070] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:03:08,107] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:03:08,221] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1255 +DEBUG [2019-11-23 01:05:04,955] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:05,106] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:05:05,248] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:05,355] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1255 +DEBUG [2019-11-23 01:05:28,162] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:28,176] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:05:28,206] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:28,278] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1223 +DEBUG [2019-11-23 01:05:38,579] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:38,590] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:05:38,626] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:38,681] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1193 +DEBUG [2019-11-23 01:05:54,856] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:55,885] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:05:55,923] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:05:55,987] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1191 +DEBUG [2019-11-23 01:06:11,730] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:06:11,747] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:06:11,778] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:06:11,854] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:06:42,074] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:06:42,115] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:06:43,025] Website is working +DEBUG [2019-11-23 01:06:43,032] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:06:48,709] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 848 +DEBUG [2019-11-23 01:08:16,611] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:08:16,622] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:08:17,167] Website is working +DEBUG [2019-11-23 01:08:17,174] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:08:23,709] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None +DEBUG [2019-11-23 01:09:20,387] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:09:20,401] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:09:20,872] Website is working +DEBUG [2019-11-23 01:09:20,879] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:09:26,501] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 812 +DEBUG [2019-11-23 01:09:36,741] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:09:36,752] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:09:36,785] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:09:36,836] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1191 +DEBUG [2019-11-23 01:09:53,294] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:09:53,307] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:09:53,341] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:09:53,394] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:10:03,448] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:10:03,459] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:10:03,494] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:10:03,557] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:10:12,679] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:10:12,689] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:10:13,080] Website is working +DEBUG [2019-11-23 01:10:13,088] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:10:18,617] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 860 +DEBUG [2019-11-23 01:10:23,831] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:10:23,841] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:10:24,336] Website is working +DEBUG [2019-11-23 01:10:24,344] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:10:29,905] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 886 +DEBUG [2019-11-23 01:11:18,644] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:11:18,657] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:11:19,063] Website is working +DEBUG [2019-11-23 01:11:19,071] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:11:24,598] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 860 +DEBUG [2019-11-23 01:11:36,709] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:11:36,719] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:11:37,185] Website is working +DEBUG [2019-11-23 01:11:37,192] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:11:42,440] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 776 +DEBUG [2019-11-23 01:12:23,086] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:12:23,099] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:12:23,518] Website is working +DEBUG [2019-11-23 01:12:23,525] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:12:28,975] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 754 +DEBUG [2019-11-23 01:12:32,226] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:12:32,236] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:12:32,651] Website is working +DEBUG [2019-11-23 01:12:32,658] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:12:38,060] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 761 +DEBUG [2019-11-23 01:15:03,367] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:15:03,381] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:15:03,849] Website is working +DEBUG [2019-11-23 01:15:03,857] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:15:09,713] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 754 +DEBUG [2019-11-23 01:15:38,500] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:15:38,515] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:15:38,545] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:15:38,619] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1214 +DEBUG [2019-11-23 01:15:53,779] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:15:55,786] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:15:55,833] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:15:55,943] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1213 +DEBUG [2019-11-23 01:16:34,671] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:16:35,942] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:16:35,977] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:16:36,098] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1246 +DEBUG [2019-11-23 01:17:12,595] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:17:15,495] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:17:15,532] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:17:15,610] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1244 +DEBUG [2019-11-23 01:18:04,741] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:18:04,756] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:18:04,795] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:18:04,856] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:18:47,031] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:18:47,045] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:18:47,863] Website is working +DEBUG [2019-11-23 01:18:47,870] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:18:53,280] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 754 +DEBUG [2019-11-23 01:19:02,139] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:19:02,150] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:19:02,558] Website is working +DEBUG [2019-11-23 01:19:02,565] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:19:07,677] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 761 +DEBUG [2019-11-23 01:20:14,516] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:20:16,578] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:20:17,049] Website is working +DEBUG [2019-11-23 01:20:17,057] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:20:22,279] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 771 +DEBUG [2019-11-23 01:21:08,589] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:21:10,269] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:21:10,671] Website is working +DEBUG [2019-11-23 01:21:10,679] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:21:16,205] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 771 +DEBUG [2019-11-23 01:22:01,882] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:22:04,281] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:22:04,790] Website is working +DEBUG [2019-11-23 01:22:04,798] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:22:10,523] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 772 +DEBUG [2019-11-23 01:22:22,176] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:22:22,188] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:22:22,583] Website is working +DEBUG [2019-11-23 01:22:22,591] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:22:28,488] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 771 +DEBUG [2019-11-23 01:22:39,781] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:22:39,792] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:22:40,177] Website is working +DEBUG [2019-11-23 01:22:40,185] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:22:45,480] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 772 +DEBUG [2019-11-23 01:23:26,652] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:23:26,665] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:23:26,697] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:23:26,765] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:23:43,759] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:23:43,771] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:23:44,197] Website is working +DEBUG [2019-11-23 01:23:44,205] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:23:49,950] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 758 +DEBUG [2019-11-23 01:23:55,331] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:23:55,342] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:23:55,735] Website is working +DEBUG [2019-11-23 01:23:55,743] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:24:00,825] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 772 +DEBUG [2019-11-23 01:28:25,064] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:28:25,079] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:28:25,109] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:28:25,172] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1245 +DEBUG [2019-11-23 01:28:38,275] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:28:38,286] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:28:38,334] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:28:38,394] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 4938 +DEBUG [2019-11-23 01:28:58,985] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:28:58,994] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 01:28:59,027] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:28:59,082] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:31:21,744] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:31:21,758] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:31:22,163] Website is working +DEBUG [2019-11-23 01:31:22,172] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:31:27,956] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 5199 +DEBUG [2019-11-23 01:31:43,715] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:31:43,725] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:31:44,143] Website is working +DEBUG [2019-11-23 01:31:44,151] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:31:49,515] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:32:42,933] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:32:44,882] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:32:45,299] Website is working +DEBUG [2019-11-23 01:32:45,307] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:32:50,848] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:33:10,945] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:33:13,190] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 01:33:13,580] Website is working +DEBUG [2019-11-23 01:33:13,588] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:33:19,335] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-23 01:44:41,977] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:44:42,088] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +INFO [2019-11-23 01:44:43,295] Website is working +DEBUG [2019-11-23 01:44:43,303] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 01:44:43,342] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None +DEBUG [2019-11-23 02:26:42,608] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:26:42,727] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +INFO [2019-11-23 02:26:44,381] Website is working +DEBUG [2019-11-23 02:26:44,389] Starting new HTTP connection (1): 127.0.0.1:5000 +ERROR [2019-11-23 02:26:44,803] Connection error('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +DEBUG [2019-11-23 02:26:54,504] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:26:54,550] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +INFO [2019-11-23 02:26:54,973] Website is working +DEBUG [2019-11-23 02:26:54,980] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:26:55,021] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None +DEBUG [2019-11-23 02:29:46,280] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:29:46,419] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +INFO [2019-11-23 02:29:46,913] Website is working +DEBUG [2019-11-23 02:29:46,923] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:29:47,037] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None +DEBUG [2019-11-23 02:30:34,762] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:30:34,926] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None +INFO [2019-11-23 02:30:35,440] Website is working +DEBUG [2019-11-23 02:30:35,447] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:30:35,489] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None +DEBUG [2019-11-23 02:36:57,226] Starting new HTTP connection (1): 127.0.0.1:5000 +ERROR [2019-11-23 02:36:58,529] ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) +DEBUG [2019-11-23 02:37:05,092] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:37:05,105] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-23 02:37:05,631] Website is working +DEBUG [2019-11-23 02:37:05,638] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 02:37:16,710] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 812 +DEBUG [2019-11-23 23:44:01,402] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 23:44:01,935] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 23:44:02,349] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 23:44:06,846] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 37762 +DEBUG [2019-11-23 23:44:07,783] EUC-JP Japanese prober hit error at byte 10 +DEBUG [2019-11-23 23:44:08,048] EUC-KR Korean prober hit error at byte 10 +DEBUG [2019-11-23 23:44:08,295] Big5 Chinese prober hit error at byte 10 +DEBUG [2019-11-23 23:44:08,296] EUC-TW Taiwan prober hit error at byte 10 +DEBUG [2019-11-23 23:44:08,663] utf-8 not active +DEBUG [2019-11-23 23:44:08,666] SHIFT_JIS Japanese confidence = 0.01 +DEBUG [2019-11-23 23:44:08,689] EUC-JP not active +DEBUG [2019-11-23 23:44:08,689] GB2312 Chinese confidence = 0.01 +DEBUG [2019-11-23 23:44:08,689] EUC-KR not active +DEBUG [2019-11-23 23:44:08,689] CP949 Korean confidence = 0.01 +DEBUG [2019-11-23 23:44:08,690] Big5 not active +DEBUG [2019-11-23 23:44:08,690] EUC-TW not active +DEBUG [2019-11-23 23:44:08,690] windows-1251 Russian confidence = 0.01 +DEBUG [2019-11-23 23:44:08,690] KOI8-R Russian confidence = 0.01 +DEBUG [2019-11-23 23:44:08,691] ISO-8859-5 Russian confidence = 0.01 +DEBUG [2019-11-23 23:44:08,691] MacCyrillic Russian confidence = 0.0 +DEBUG [2019-11-23 23:44:08,691] IBM866 Russian confidence = 0.0 +DEBUG [2019-11-23 23:44:08,691] IBM855 Russian confidence = 0.01 +DEBUG [2019-11-23 23:44:08,691] ISO-8859-7 Greek confidence = 0.01 +DEBUG [2019-11-23 23:44:08,692] windows-1253 Greek confidence = 0.01 +DEBUG [2019-11-23 23:44:08,692] ISO-8859-5 Bulgairan confidence = 0.01 +DEBUG [2019-11-23 23:44:08,692] windows-1251 Bulgarian confidence = 0.01 +DEBUG [2019-11-23 23:44:08,697] TIS-620 Thai confidence = 0.01 +DEBUG [2019-11-23 23:44:08,698] ISO-8859-9 Turkish confidence = 0.1995084336143655 +DEBUG [2019-11-23 23:44:08,698] windows-1255 Hebrew confidence = 0.0 +DEBUG [2019-11-23 23:44:08,698] windows-1255 Hebrew confidence = 0.01 +DEBUG [2019-11-23 23:44:08,698] windows-1255 Hebrew confidence = 0.01 +DEBUG [2019-11-23 23:50:04,586] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 23:50:04,626] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 23:50:04,679] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 23:50:06,712] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 404 232 +DEBUG [2019-11-23 23:50:46,850] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 23:50:46,861] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-23 23:50:46,924] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-23 23:50:48,688] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 37758 +DEBUG [2019-11-23 23:50:49,428] EUC-JP Japanese prober hit error at byte 10 +DEBUG [2019-11-23 23:50:49,812] EUC-KR Korean prober hit error at byte 10 +DEBUG [2019-11-23 23:50:50,052] Big5 Chinese prober hit error at byte 10 +DEBUG [2019-11-23 23:50:50,053] EUC-TW Taiwan prober hit error at byte 10 +DEBUG [2019-11-23 23:50:50,365] utf-8 not active +DEBUG [2019-11-23 23:50:50,366] SHIFT_JIS Japanese confidence = 0.01 +DEBUG [2019-11-23 23:50:50,367] EUC-JP not active +DEBUG [2019-11-23 23:50:50,400] GB2312 Chinese confidence = 0.01 +DEBUG [2019-11-23 23:50:50,400] EUC-KR not active +DEBUG [2019-11-23 23:50:50,400] CP949 Korean confidence = 0.01 +DEBUG [2019-11-23 23:50:50,400] Big5 not active +DEBUG [2019-11-23 23:50:50,400] EUC-TW not active +DEBUG [2019-11-23 23:50:50,401] windows-1251 Russian confidence = 0.01 +DEBUG [2019-11-23 23:50:50,401] KOI8-R Russian confidence = 0.01 +DEBUG [2019-11-23 23:50:50,401] ISO-8859-5 Russian confidence = 0.01 +DEBUG [2019-11-23 23:50:50,401] MacCyrillic Russian confidence = 0.0 +DEBUG [2019-11-23 23:50:50,401] IBM866 Russian confidence = 0.0 +DEBUG [2019-11-23 23:50:50,402] IBM855 Russian confidence = 0.01 +DEBUG [2019-11-23 23:50:50,402] ISO-8859-7 Greek confidence = 0.01 +DEBUG [2019-11-23 23:50:50,402] windows-1253 Greek confidence = 0.01 +DEBUG [2019-11-23 23:50:50,402] ISO-8859-5 Bulgairan confidence = 0.01 +DEBUG [2019-11-23 23:50:50,402] windows-1251 Bulgarian confidence = 0.01 +DEBUG [2019-11-23 23:50:50,402] TIS-620 Thai confidence = 0.01 +DEBUG [2019-11-23 23:50:50,403] ISO-8859-9 Turkish confidence = 0.1991101760911726 +DEBUG [2019-11-23 23:50:50,403] windows-1255 Hebrew confidence = 0.0 +DEBUG [2019-11-23 23:50:50,404] windows-1255 Hebrew confidence = 0.01 +DEBUG [2019-11-23 23:50:50,404] windows-1255 Hebrew confidence = 0.01 +DEBUG [2019-11-25 02:54:15,159] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 02:54:17,178] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 02:54:18,119] Website is working +DEBUG [2019-11-25 02:54:18,127] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 02:54:22,516] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 903 +DEBUG [2019-11-25 16:01:44,926] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:01:44,964] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 16:01:45,942] Website is working +DEBUG [2019-11-25 16:01:45,949] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:01:49,421] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-25 16:02:10,691] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:02:10,698] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-25 16:02:10,725] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:02:13,083] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None +DEBUG [2019-11-25 16:04:43,917] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:04:43,926] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 16:04:44,412] Website is working +DEBUG [2019-11-25 16:04:44,420] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:04:47,728] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-25 16:05:33,618] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:05:34,753] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 16:05:35,159] Website is working +DEBUG [2019-11-25 16:05:35,167] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:05:44,332] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-25 16:06:37,673] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:06:37,685] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 16:06:38,082] Website is working +DEBUG [2019-11-25 16:06:38,089] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:06:43,724] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-25 16:07:01,962] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:07:01,972] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 16:07:02,857] Website is working +DEBUG [2019-11-25 16:07:02,864] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:07:06,294] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-25 16:07:24,010] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:07:24,019] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 16:07:24,423] Website is working +DEBUG [2019-11-25 16:07:24,431] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:07:30,212] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None +DEBUG [2019-11-25 16:08:53,003] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:08:53,060] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-25 16:08:53,113] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 16:08:53,703] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 952 +DEBUG [2019-11-25 21:08:08,384] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 21:08:08,402] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +DEBUG [2019-11-25 21:08:08,440] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 21:08:10,098] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 952 +DEBUG [2019-11-25 21:10:55,754] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 21:10:55,763] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 +INFO [2019-11-25 21:10:57,182] Website is working +DEBUG [2019-11-25 21:10:57,189] Starting new HTTP connection (1): 127.0.0.1:5000 +DEBUG [2019-11-25 21:11:41,580] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 10160 diff --git a/final_task/rss_reader/client/rss_reader.py b/final_task/rss_reader/client/rss_reader.py index 72ceffa..19f82f1 100644 --- a/final_task/rss_reader/client/rss_reader.py +++ b/final_task/rss_reader/client/rss_reader.py @@ -22,9 +22,9 @@ def main(version): requests.get('http://127.0.0.1:5000/verbose/') if(args.colorize): - color = colored.fg(0)+colored.bg(220) + color = [colored.fg(150), colored.fg(50), colored.fg(189)] else: - color = colored.attr('reset') + color = [colored.attr('reset'), colored.attr('reset'), colored.attr('reset')] params = {'limit':args.limit, 'tojson': args.json, 'tohtml':args.tohtml, 'topdf':args.topdf, 'color':color} @@ -103,6 +103,7 @@ def checkConnection(source): if __name__ == "__main__": + # Check connection to server try: version = (requests.get('http://127.0.0.1:5000/version/')).text main(version) diff --git a/final_task/rss_reader/collect_news.py b/final_task/rss_reader/collect_news.py index f8044e7..c2fa853 100644 --- a/final_task/rss_reader/collect_news.py +++ b/final_task/rss_reader/collect_news.py @@ -13,10 +13,10 @@ def collectNews(limit, tojson, tohtml, topdf, color, source): news = list() channel = feedparser.parse(source) - news.append(color + "Feed: " + channel.feed.title + '\n') + news.append(color[0] + "Feed: " + channel.feed.title + '\n') limit = limit or len(channel.entries) - news_parser.cacheNews(source, channel) + news_parser.cacheNews(channel) if (tohtml or topdf): html_doc = converter.createHtmlStructure(channel, limit, tohtml, topdf) @@ -25,6 +25,12 @@ def collectNews(limit, tojson, tohtml, topdf, color, source): for index, item in enumerate(channel.entries): if (index == limit): break + + if(index%2==0): + news.append(color[1]) + else: + news.append(color[2]) + logg.createLogs(item) if (tojson): @@ -35,8 +41,8 @@ def collectNews(limit, tojson, tohtml, topdf, color, source): news.append("\nLink: " + item.link + '\n') description = news_parser.getDescription(item.description) if(description): - news.append("Description: " + description + '\n') - news.append("Links:" + "\n[1]: " + item.link + "(link)") + news.append(color[0] + "Description: " + description + '\n') + news.append(color[1] + "Links:" + "\n[1]: " + item.link + "(link)") media_content = news_parser.checkMediaContent(item) if(media_content): news.append("\n[2]: " + media_content + '\n') diff --git a/final_task/rss_reader/converter.py b/final_task/rss_reader/converter.py index 2e73db6..1beee14 100644 --- a/final_task/rss_reader/converter.py +++ b/final_task/rss_reader/converter.py @@ -1,7 +1,5 @@ from dominate.tags import div, h2, img, p, link import dominate -from datetime import datetime -import os import html from . import news_parser, topdf @@ -10,7 +8,8 @@ def createHtmlStructure(channel, limit, html_path, pdf_path): ''' 1. in loop create html structure - 2. return html_structure or file name of pdf document + 2. return html_structure + 3. or file name of pdf for send_from_directory function ''' html_document = dominate.document(title="HTML document") @@ -30,21 +29,5 @@ def createHtmlStructure(channel, limit, html_path, pdf_path): if (html_path): return str(html_document) - else: - return intoPDF(html_document, pdf_path) - - -def intoPDF(doc, pdf_path): - ''' - 1. create folder with pdf file - 2. convert html into pdf - 3. return only filename for send_from_directory function - ''' - if not os.path.exists(pdf_path): - os.makedirs(pdf_path) - time_name = datetime.strftime(datetime.now(), "%H%M%S") - file_name = 'NewsFeed' + '-' + time_name + '.pdf' - pdf_file = os.path.join(pdf_path, file_name) - - topdf.convertHtmlToPdf(str(doc), pdf_file) - return file_name + elif(pdf_path): + return topdf.convertHtmlToPdf(str(html_document), pdf_path) diff --git a/final_task/rss_reader/docker-compose.yml b/final_task/rss_reader/docker-compose.yml index 3a62650..c14acaf 100644 --- a/final_task/rss_reader/docker-compose.yml +++ b/final_task/rss_reader/docker-compose.yml @@ -13,11 +13,11 @@ services: env_file: - ./.env.dev depends_on: - - db + - localhost links: - - db + - localhost - db: + localhost: image: postgres:12.1-alpine restart: always ports: diff --git a/final_task/rss_reader/get_cache.py b/final_task/rss_reader/get_cache.py index dcf7685..b32dfff 100644 --- a/final_task/rss_reader/get_cache.py +++ b/final_task/rss_reader/get_cache.py @@ -1,14 +1,12 @@ from dominate.tags import div, h2, img, p, link -from contextlib import closing from datetime import datetime import dominate import psycopg2 import base64 import time import json -import os -from . import logg, converter, news_parser +from . import logg, converter, news_parser, topdf def dateToStamp(arg_date): @@ -20,7 +18,7 @@ def dateToStamp(arg_date): return arg_date -def getCache(limit, tojson, html_path, pdf_path, color, arg_date): +def getCacheFromDB(arg_date): ''' 1. connect to database 2. select from table news with published date equals --date @@ -28,31 +26,39 @@ def getCache(limit, tojson, html_path, pdf_path, color, arg_date): 3. or print news in stdout ''' try: - with closing(psycopg2.connect(database="postgres",user='postgres',password='rssreader',host='db',port='5432')) as con: + with psycopg2.connect(database="postgres",user='postgres',password='rssreader',host='localhost',port='5432') as con: with con.cursor() as cur: - cur.execute('''SELECT title, link, image, description FROM news WHERE pub_date_stamp >= %s and pub_date_stamp < %s''', + cur.execute('''SELECT title, link, image, description FROM news WHERE pub_date_stamp >= %s and pub_date_stamp < %s''', (dateToStamp(arg_date), dateToStamp(int(arg_date) + 1))) records = cur.fetchall() - if (html_path or pdf_path): - return createHtmlStructure(records, limit, html_path, pdf_path) - else: - return getNewsFromDB(records, limit, tojson, color) - + return records except psycopg2.ProgrammingError as e: print("psycopg2.ProgrammingError: " + str(e)) logg.logging.error(str(e)) + finally: + con.close() -def getNewsFromDB(records, limit, tojson, color): +def collectNewsFromDB(limit, tojson, color, arg_date): ''' 1. create list for news 2. collect all cache news from db in list ''' + records = getCacheFromDB(arg_date) + news = list() + news.append(color[0] + 'Cache News: ') + for index, row in enumerate(records): if(limit and index == limit): break + + if(index%2==0): + news.append(color[1]) + else: + news.append(color[2]) + if (tojson): json_news = { 'Title: ': row[0], @@ -62,18 +68,20 @@ def getNewsFromDB(records, limit, tojson, color): json_news['Description'] = row[3] news.append(json.dumps(json_news)) else: - news.append(color + "\nTitle: " + row[0]) + news.append("\nTitle: " + row[0]) news.append("\nLink: " + row[1] + '\n') if (row[3]): - news.append("Description: " + row[3] + '\n') + news.append(color[0] + "Description: " + row[3] + '\n') return news -def createHtmlStructure(records, limit, html_path, pdf_path): +def createHtmlFromDB(limit, html_path, pdf_path, arg_date): ''' 1. in loop create html structure 2. create html document or convert html structure into pdf ''' + records = getCacheFromDB(arg_date) + html_document = dominate.document(title="HTML document") for index, row in enumerate(records): @@ -91,4 +99,4 @@ def createHtmlStructure(records, limit, html_path, pdf_path): if (html_path): return str(html_document) elif (pdf_path): - return converter.intoPDF(html_document, pdf_path) + return topdf.convertHtmlToPdf(str(html_document), pdf_path) diff --git a/final_task/rss_reader/logg.py b/final_task/rss_reader/logg.py index 3869a07..6a9bef1 100644 --- a/final_task/rss_reader/logg.py +++ b/final_task/rss_reader/logg.py @@ -30,4 +30,4 @@ def createLogs(item): logging.debug("Link: " + str(item.link)) logging.debug("Description: " + news_parser.getDescription(item.description)) logging.debug("Links:"+"\n[1]: " + str(item.link) + - "(link)\n[2]: " + str(news_parser.checkMediaContent(item)) + '\n') + "(link)\n[2]: " + str(news_parser.checkMediaContent(item)) + '\n') \ No newline at end of file diff --git a/final_task/rss_reader/news_parser.py b/final_task/rss_reader/news_parser.py index 58090ff..d4ef7a2 100644 --- a/final_task/rss_reader/news_parser.py +++ b/final_task/rss_reader/news_parser.py @@ -1,3 +1,4 @@ +from email.utils import parsedate_to_datetime from contextlib import closing from bs4 import BeautifulSoup from datetime import datetime @@ -22,9 +23,9 @@ def checkMediaContent(item): return media_content -def getDescription(item): +def getDescription(description): '''return description without html tags''' - return BeautifulSoup(item, features="html.parser").getText() + return BeautifulSoup(description, features="html.parser").getText() def intoJson(item): @@ -43,74 +44,60 @@ def intoJson(item): return json.dumps(json_news) -def cacheNews(url, channel): +def cacheNews(channel): ''' 1. connect to database 2. create table in database 3. insert news into table ''' try: - with closing(psycopg2.connect(database="postgres",user='postgres',password='rssreader', - host='db',port='5432')) as con: + with psycopg2.connect(database="postgres",user='postgres',password='rssreader', + host='localhost',port='5432') as con: with con.cursor() as cur: cur.execute("""CREATE TABLE IF NOT EXISTS news (title text, link text, image bytea, description text, pub_date_stamp real, UNIQUE (title, link, pub_date_stamp)) """) - insertNewsIntoTable(channel, cur) + + news = insertNewsIntoTable(channel) + cur.executemany("INSERT INTO news VALUES (%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING", news) con.commit() + + logg.logging.info("News cached into database") except (Exception, psycopg2.DatabaseError) as e: logg.logging.error(str(e)) + finally: + con.close() -def insertNewsIntoTable(channel, con): +def insertNewsIntoTable(channel): ''' 1. fill table with news 2. convert date into timestamp - 3. create folder with cache images in loop ''' + news = list() + for index, item in enumerate(channel.entries): description = getDescription(item.description) - pub_date = getPublishedDate(item.published) - pub_date_stamp = time.mktime(datetime.strptime(pub_date, '%d %m %Y %H:%M:%S').timetuple()) - + try: + pub_date_stamp = time.mktime(parsedate_to_datetime(item.published).timetuple()) + except ValueError as error: + logg.logging.error("ValueError: " + str(error)) + media_content = checkMediaContent(item) + image = '' + if (media_content): - response = requests.get(media_content) - image = psycopg2.Binary(response.content) + try: + response = requests.get(media_content) + image = psycopg2.Binary(response.content) + except Exception as error: + logg.logging.error("Exception: " + str(e)) row = (html.unescape(item.title), item.link, image, description, pub_date_stamp) - try: - con.execute("INSERT INTO news VALUES (%s,%s,%s,%s,%s)", row) - except (Exception, psycopg2.DatabaseError) as e: - logg.logging.error(str(e)) - - -def isEmpty(cursor): - ''' - 1. check if table is empty - ''' - cursor.execute("SELECT COUNT(*) FROM news") - exist = cursor.fetchone() - if not(exist[0]): - return True - else: - return False - - -def getPublishedDate(pub_date): - ''' - 1. convert published date into --date argument format - ''' - pub_date = ((pub_date).split(' ')[1:5]) - - month = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06', - 'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'} - - pub_date[1] = month[pub_date[1]] - pub_date = ' '.join(pub_date) + news.append(row) + return news - return pub_date diff --git a/final_task/rss_reader/requirements.txt b/final_task/rss_reader/requirements.txt index 5fc88fb..a8b2ffe 100644 --- a/final_task/rss_reader/requirements.txt +++ b/final_task/rss_reader/requirements.txt @@ -1,9 +1,10 @@ -xhtml2pdf==0.2.3 -urllib3==1.25.6 -feedparser==5.2.1 -dominate==2.4.0 beautifulsoup4==4.8.1 colored==1.4.0 +dominate==2.4.0 +feedparser==5.2.1 requests==2.22.0 +urllib3==1.25.6 +xhtml2pdf==0.2.3 +argparse==1.4.0 psycopg2==2.8.4 -Flask==1.1.1 \ No newline at end of file +flask==1.1.1 diff --git a/final_task/rss_reader/topdf.py b/final_task/rss_reader/topdf.py index c11c454..9e7518b 100644 --- a/final_task/rss_reader/topdf.py +++ b/final_task/rss_reader/topdf.py @@ -1,10 +1,12 @@ from xhtml2pdf import pisa +from datetime import datetime import urllib.request import urllib.parse import urllib import html import cgi import sys +import os cgi.escape = html.escape @@ -23,7 +25,7 @@ def splithost_polyfill(url): return netloc, path -def convertHtmlToPdf(sourceHtml, outputFilename): +def convertHtmlToPdf(html_document, pdf_path): ''' 1. replace splithost with custom function splithost_polyfill cause splithost was removed from python 3.8 @@ -34,8 +36,14 @@ def convertHtmlToPdf(sourceHtml, outputFilename): urllib.splithost = splithost_polyfill urllib.request.splithost = splithost_polyfill - with open(outputFilename, "w+b") as resultFile: + if not os.path.exists(pdf_path): + os.makedirs(pdf_path) + time_name = datetime.strftime(datetime.now(), "%H%M%S") + file_name = 'NewsFeed' + '-' + time_name + '.pdf' + pdf_file = os.path.join(pdf_path, file_name) + + with open(pdf_file, "w+b") as resultFile: pisaStatus = pisa.CreatePDF( - sourceHtml, + html_document, dest=resultFile) - return pisaStatus.err + return file_name diff --git a/final_task/setup.py b/final_task/setup.py index cb80a21..fbeefeb 100644 --- a/final_task/setup.py +++ b/final_task/setup.py @@ -9,13 +9,13 @@ setup( - name="rss-reader", - version=get_version(), + name="rss_reader", + version=version.VERSION, packages=find_packages(), - install_requires=['beautifulsoup4','dominate','feedparser', 'urllib3', 'xhtml2pdf', 'colored'], + install_requires=['argparse','beautifulsoup4','dominate','feedparser', 'urllib3', 'xhtml2pdf', 'colored', 'requests', 'psycopg2','flask'], author="ilya khonenko", author_email="honenkoi@gmail.com", - url="https://github.com/kingofmidas" + url="https://github.com/kingofmidas", description="This is rss-reader", long_description = long_description, long_description_content_type="text/markdown", diff --git a/final_task/tests/collectnews_test.py b/final_task/tests/collectnews_test.py new file mode 100644 index 0000000..83218a4 --- /dev/null +++ b/final_task/tests/collectnews_test.py @@ -0,0 +1,11 @@ +import unittest +from unittest.mock import patch +from rss_reader import collect_news +from expected_results import normal_result, json_result, html_result + + +def testCollectNews(self): + self.assertEqual(collect_news.collectNews(1, False, '', '', ['','',''], self.item), normal_result) + self.assertEqual(collect_news.collectNews(1, True, '', '', ['','',''], self.item)[2], json_result) + self.assertEqual(collect_news.collectNews(1, False, 'tohtml', '', ['','',''], self.item), html_result) + diff --git a/final_task/tests/expected_results.py b/final_task/tests/expected_results.py new file mode 100644 index 0000000..c66138b --- /dev/null +++ b/final_task/tests/expected_results.py @@ -0,0 +1,35 @@ +normal_result = ['Feed: Yahoo News - Latest News & Headlines\n', '', '\nTitle: item_title', '\nDate: item_date', '\nLink: item_link\n', 'Description: item_description\n', 'Links:\n[1]: item_link(link)', '\n[2]: media_link_url\n'] + + +json_result = '{"Title: ": "item_title", "Date: ": "item_date", "Link: ": "item_link", "Description: ": "item_description", "Media link: ": "media_link_url"}' + + +html_result = ''' + + + HTML document + + +
+

Title: item_title

+

Link: item_link

+ +

Description: item_description

+
+ +''' + + +html_result_from_db = ''' + + + HTML document + + +
+

Title: item_title

+

Link: item_link

+

Description: item_description

+
+ +''' diff --git a/final_task/tests/getcache_test.py b/final_task/tests/getcache_test.py new file mode 100644 index 0000000..b133c2e --- /dev/null +++ b/final_task/tests/getcache_test.py @@ -0,0 +1,55 @@ +import unittest +from unittest.mock import patch +from rss_reader import get_cache +from expected_results import html_result_from_db + + + +ef testGetCacheFromDB(self): + with patch("psycopg2.connect") as mock_connect: + expected = ['title', 'link', 'image', 'description'] + + mock_con_cm = mock_connect.return_value + mock_con = mock_con_cm.__enter__.return_value + + mock_cur_cm = mock_con.cursor.return_value + mock_cur = mock_cur_cm.__enter__.return_value + + mock_cur.fetchall.return_value = expected + + result = get_cache.getCacheFromDB('20191125') + self.assertEqual(result, expected) + + mock_connect.assert_called_with(database="postgres",user='postgres',password='rssreader',host='localhost',port='5432') + + +def testCollectNewsFromDB(self): + with patch('rss_reader.get_cache.getCacheFromDB') as mock_cache: + mock_cache.return_value = [('title', 'link', 'image', 'description')] + + expected = ['Cache News: ', '', '\nTitle: title', '\nLink: link\n', 'Description: description\n'] + + result = get_cache.collectNewsFromDB(1, False, ['','',''], '20191125') + self.assertEqual(result, expected) + + expectedJson = ['Cache News: ', '', '{"Title: ": "title", "Link: ": "link", "Description": "description"}'] + + result = get_cache.collectNewsFromDB(1, True, ['','',''], '20191125') + self.assertEqual(result, expectedJson) + + +def testCreateHtmlFromDB(self): + with patch('rss_reader.get_cache.getCacheFromDB') as mock_cache: + mock_cache.return_value = [('item_title', 'item_link', '', 'item_description')] + + expected = html_result_from_db + + result = get_cache.createHtmlFromDB(1, 'html_path', '', '20191125') + self.assertEqual(result, expected) + + +def testDateToStamp(self): + expected = 1574629200.0 + result = get_cache.dateToStamp('20191125') + + self.assertEqual(result, expected) \ No newline at end of file diff --git a/final_task/tests/parser_test.py b/final_task/tests/parser_test.py new file mode 100644 index 0000000..10a4c16 --- /dev/null +++ b/final_task/tests/parser_test.py @@ -0,0 +1,63 @@ +import unittest +from unittest.mock import patch +from feedparser import parse +from rss_reader import converter, news_parser +from expected_results import json_result, html_result + + +class TestParser(unittest.TestCase): + + def setUp(self): + self.item = '' \ + 'Yahoo News - Latest News & Headlines' \ + 'item_title' \ + 'item_description' \ + 'item_linkitem_date' \ + 'media_link' + self.parse_item = parse(self.item) + + + def testGetDescription(self): + self.assertEqual(news_parser.getDescription(self.parse_item.entries[0].description), 'item_description') + + + def testJson(self): + self.assertEqual(news_parser.intoJson(self.parse_item.entries[0]), json_result) + + + def testCheckMediaContent(self): + self.assertEqual(news_parser.checkMediaContent(self.parse_item.entries[0]), 'media_link_url') + + + def testHtml(self): + html_doc = converter.createHtmlStructure(self.parse_item, 1, 'html_path', '') + self.assertEqual(html_doc, html_result) + + + def testCacheNews(self): + with patch("psycopg2.connect") as mock_connect: + mock_con_cm = mock_connect.return_value + mock_con = mock_con_cm.__enter__.return_value + + mock_cur_cm = mock_con.cursor.return_value + mock_cur = mock_cur_cm.__enter__.return_value + + news_parser.cacheNews(self.parse_item) + mock_connect.assert_called_with(database="postgres",user='postgres',password='rssreader',host='localhost',port='5432') + mock_cur.executemany.called_with("INSERT INTO news VALUES (%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING") + + + def testInsertNewsIntoTable(self): + item = '' \ + 'Yahoo News - Latest News & Headlines' \ + 'item_title' \ + 'item_description' \ + 'item_linkMon, 25 Nov 2019 13:04:03' + parse_item = parse(item) + news = news_parser.insertNewsIntoTable(parse_item) + self.assertEqual(news, [('item_title', 'item_link', '', 'item_description', 1574676243.0)]) + + +if __name__=='__main__': + + unittest.main() \ No newline at end of file diff --git a/final_task/tests/topdf_test.py b/final_task/tests/topdf_test.py new file mode 100644 index 0000000..6a53ed6 --- /dev/null +++ b/final_task/tests/topdf_test.py @@ -0,0 +1,24 @@ +import unittest +from unittest.mock import patch +from rss_reader import topdf +from expected_results import html_result + + +@patch('rss_reader.topdf.datetime') +@patch('builtins.open') +def testConvertToPdf(self, mock_file, mock_time): + mock_time.strftime.return_value = '191749' + + result = topdf.convertHtmlToPdf(html_result, 'pdf_path/') + expected = 'NewsFeed-191749.pdf' + + mock_file.assert_called_with('pdf_path/NewsFeed-191749.pdf', "w+b") + self.assertEqual(result, expected) + + + +def testSplitHost(self): + url = 'https://news.yahoo.com/rss' + expected = ('news.yahoo.com', '/rss') + result = topdf.splithost_polyfill(url) + self.assertEqual(result, expected) From ab3c013441176bea31adbd8368c3bd69974ba9f9 Mon Sep 17 00:00:00 2001 From: kingofmidas Date: Thu, 28 Nov 2019 21:49:37 +0300 Subject: [PATCH 15/15] Fix tests and database problems. --- final_task/rss_reader/app.py | 3 +- final_task/rss_reader/client/parser.log | 419 ----------------------- final_task/rss_reader/config.py | 20 ++ final_task/rss_reader/database.ini | 12 + final_task/rss_reader/docker-compose.yml | 14 +- final_task/rss_reader/get_cache.py | 15 +- final_task/rss_reader/news_parser.py | 13 +- final_task/tests/collectnews_test.py | 24 +- final_task/tests/getcache_test.py | 65 ++-- final_task/tests/parser.log | 20 ++ final_task/tests/topdf_test.py | 32 +- 11 files changed, 153 insertions(+), 484 deletions(-) delete mode 100644 final_task/rss_reader/client/parser.log create mode 100644 final_task/rss_reader/config.py create mode 100644 final_task/rss_reader/database.ini create mode 100644 final_task/tests/parser.log diff --git a/final_task/rss_reader/app.py b/final_task/rss_reader/app.py index 840ecf4..7c9bacb 100644 --- a/final_task/rss_reader/app.py +++ b/final_task/rss_reader/app.py @@ -44,8 +44,7 @@ def getVersion(): @app.route('/verbose/', methods=['GET', 'POST']) def setLogging(): logg.makeVerbose() - return '' if __name__ == '__main__': - app.run(debug=True) + app.run() diff --git a/final_task/rss_reader/client/parser.log b/final_task/rss_reader/client/parser.log deleted file mode 100644 index 330cfac..0000000 --- a/final_task/rss_reader/client/parser.log +++ /dev/null @@ -1,419 +0,0 @@ -DEBUG [2019-11-23 00:44:29,990] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:44:30,191] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -DEBUG [2019-11-23 00:44:30,371] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:44:30,441] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None -DEBUG [2019-11-23 00:45:04,990] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:45:05,055] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -DEBUG [2019-11-23 00:45:05,088] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:45:05,158] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None -DEBUG [2019-11-23 00:47:23,317] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:47:24,570] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 00:47:24,600] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:47:24,644] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 00:48:58,276] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:48:58,289] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 00:48:58,319] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:48:58,367] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 00:49:25,354] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:49:28,491] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 00:49:28,525] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:49:28,572] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 00:50:29,952] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:50:29,963] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 00:50:39,103] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:50:39,121] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 00:50:39,149] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:50:39,179] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1221 -DEBUG [2019-11-23 00:50:58,770] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:51:00,654] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 00:51:00,692] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:51:00,737] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1253 -DEBUG [2019-11-23 00:53:02,785] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:53:02,798] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 00:53:02,829] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:53:02,871] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 00:53:45,795] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:53:45,809] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 00:53:46,876] Website is working -DEBUG [2019-11-23 00:53:46,884] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:53:58,625] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 826 -DEBUG [2019-11-23 00:54:24,876] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:54:24,886] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 00:54:25,295] Website is working -DEBUG [2019-11-23 00:54:25,303] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:54:32,692] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 827 -DEBUG [2019-11-23 00:54:56,654] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:54:56,669] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 00:54:57,137] Website is working -DEBUG [2019-11-23 00:54:57,146] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:55:03,732] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 828 -DEBUG [2019-11-23 00:55:19,733] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:55:19,743] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 00:55:20,150] Website is working -DEBUG [2019-11-23 00:55:20,157] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:55:26,570] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 828 -DEBUG [2019-11-23 00:56:27,423] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:56:29,291] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 00:56:29,731] Website is working -DEBUG [2019-11-23 00:56:29,738] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 00:56:35,738] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None -DEBUG [2019-11-23 01:00:16,159] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:00:17,899] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:00:18,306] Website is working -DEBUG [2019-11-23 01:00:18,314] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:00:24,491] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 886 -DEBUG [2019-11-23 01:00:53,547] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:00:53,559] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:00:53,963] Website is working -DEBUG [2019-11-23 01:00:53,971] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:00:59,399] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 839 -DEBUG [2019-11-23 01:01:36,178] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:01:36,195] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:01:36,234] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:01:36,294] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:01:56,588] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:01:56,599] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:01:56,631] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:01:56,676] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1253 -DEBUG [2019-11-23 01:02:53,313] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:02:53,323] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:02:53,744] Website is working -DEBUG [2019-11-23 01:02:53,751] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:02:59,397] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 776 -DEBUG [2019-11-23 01:03:06,903] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:03:08,070] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:03:08,107] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:03:08,221] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1255 -DEBUG [2019-11-23 01:05:04,955] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:05,106] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:05:05,248] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:05,355] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1255 -DEBUG [2019-11-23 01:05:28,162] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:28,176] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:05:28,206] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:28,278] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1223 -DEBUG [2019-11-23 01:05:38,579] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:38,590] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:05:38,626] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:38,681] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1193 -DEBUG [2019-11-23 01:05:54,856] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:55,885] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:05:55,923] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:05:55,987] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1191 -DEBUG [2019-11-23 01:06:11,730] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:06:11,747] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:06:11,778] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:06:11,854] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:06:42,074] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:06:42,115] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:06:43,025] Website is working -DEBUG [2019-11-23 01:06:43,032] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:06:48,709] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 848 -DEBUG [2019-11-23 01:08:16,611] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:08:16,622] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:08:17,167] Website is working -DEBUG [2019-11-23 01:08:17,174] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:08:23,709] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None -DEBUG [2019-11-23 01:09:20,387] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:09:20,401] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:09:20,872] Website is working -DEBUG [2019-11-23 01:09:20,879] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:09:26,501] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 812 -DEBUG [2019-11-23 01:09:36,741] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:09:36,752] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:09:36,785] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:09:36,836] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1191 -DEBUG [2019-11-23 01:09:53,294] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:09:53,307] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:09:53,341] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:09:53,394] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:10:03,448] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:10:03,459] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:10:03,494] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:10:03,557] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:10:12,679] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:10:12,689] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:10:13,080] Website is working -DEBUG [2019-11-23 01:10:13,088] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:10:18,617] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 860 -DEBUG [2019-11-23 01:10:23,831] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:10:23,841] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:10:24,336] Website is working -DEBUG [2019-11-23 01:10:24,344] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:10:29,905] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 886 -DEBUG [2019-11-23 01:11:18,644] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:11:18,657] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:11:19,063] Website is working -DEBUG [2019-11-23 01:11:19,071] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:11:24,598] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 860 -DEBUG [2019-11-23 01:11:36,709] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:11:36,719] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:11:37,185] Website is working -DEBUG [2019-11-23 01:11:37,192] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:11:42,440] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 776 -DEBUG [2019-11-23 01:12:23,086] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:12:23,099] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:12:23,518] Website is working -DEBUG [2019-11-23 01:12:23,525] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:12:28,975] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 754 -DEBUG [2019-11-23 01:12:32,226] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:12:32,236] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:12:32,651] Website is working -DEBUG [2019-11-23 01:12:32,658] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:12:38,060] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 761 -DEBUG [2019-11-23 01:15:03,367] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:15:03,381] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:15:03,849] Website is working -DEBUG [2019-11-23 01:15:03,857] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:15:09,713] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 754 -DEBUG [2019-11-23 01:15:38,500] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:15:38,515] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:15:38,545] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:15:38,619] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1214 -DEBUG [2019-11-23 01:15:53,779] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:15:55,786] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:15:55,833] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:15:55,943] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1213 -DEBUG [2019-11-23 01:16:34,671] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:16:35,942] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:16:35,977] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:16:36,098] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1246 -DEBUG [2019-11-23 01:17:12,595] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:17:15,495] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:17:15,532] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:17:15,610] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1244 -DEBUG [2019-11-23 01:18:04,741] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:18:04,756] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:18:04,795] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:18:04,856] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:18:47,031] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:18:47,045] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:18:47,863] Website is working -DEBUG [2019-11-23 01:18:47,870] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:18:53,280] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 754 -DEBUG [2019-11-23 01:19:02,139] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:19:02,150] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:19:02,558] Website is working -DEBUG [2019-11-23 01:19:02,565] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:19:07,677] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 761 -DEBUG [2019-11-23 01:20:14,516] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:20:16,578] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:20:17,049] Website is working -DEBUG [2019-11-23 01:20:17,057] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:20:22,279] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 771 -DEBUG [2019-11-23 01:21:08,589] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:21:10,269] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:21:10,671] Website is working -DEBUG [2019-11-23 01:21:10,679] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:21:16,205] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 771 -DEBUG [2019-11-23 01:22:01,882] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:22:04,281] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:22:04,790] Website is working -DEBUG [2019-11-23 01:22:04,798] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:22:10,523] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 772 -DEBUG [2019-11-23 01:22:22,176] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:22:22,188] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:22:22,583] Website is working -DEBUG [2019-11-23 01:22:22,591] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:22:28,488] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 771 -DEBUG [2019-11-23 01:22:39,781] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:22:39,792] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:22:40,177] Website is working -DEBUG [2019-11-23 01:22:40,185] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:22:45,480] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 772 -DEBUG [2019-11-23 01:23:26,652] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:23:26,665] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:23:26,697] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:23:26,765] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:23:43,759] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:23:43,771] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:23:44,197] Website is working -DEBUG [2019-11-23 01:23:44,205] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:23:49,950] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 758 -DEBUG [2019-11-23 01:23:55,331] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:23:55,342] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:23:55,735] Website is working -DEBUG [2019-11-23 01:23:55,743] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:24:00,825] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 772 -DEBUG [2019-11-23 01:28:25,064] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:28:25,079] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:28:25,109] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:28:25,172] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 1245 -DEBUG [2019-11-23 01:28:38,275] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:28:38,286] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:28:38,334] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:28:38,394] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 4938 -DEBUG [2019-11-23 01:28:58,985] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:28:58,994] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 01:28:59,027] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:28:59,082] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:31:21,744] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:31:21,758] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:31:22,163] Website is working -DEBUG [2019-11-23 01:31:22,172] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:31:27,956] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 5199 -DEBUG [2019-11-23 01:31:43,715] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:31:43,725] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:31:44,143] Website is working -DEBUG [2019-11-23 01:31:44,151] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:31:49,515] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:32:42,933] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:32:44,882] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:32:45,299] Website is working -DEBUG [2019-11-23 01:32:45,307] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:32:50,848] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:33:10,945] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:33:13,190] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 01:33:13,580] Website is working -DEBUG [2019-11-23 01:33:13,588] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:33:19,335] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-23 01:44:41,977] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:44:42,088] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -INFO [2019-11-23 01:44:43,295] Website is working -DEBUG [2019-11-23 01:44:43,303] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 01:44:43,342] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None -DEBUG [2019-11-23 02:26:42,608] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:26:42,727] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -INFO [2019-11-23 02:26:44,381] Website is working -DEBUG [2019-11-23 02:26:44,389] Starting new HTTP connection (1): 127.0.0.1:5000 -ERROR [2019-11-23 02:26:44,803] Connection error('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) -DEBUG [2019-11-23 02:26:54,504] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:26:54,550] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -INFO [2019-11-23 02:26:54,973] Website is working -DEBUG [2019-11-23 02:26:54,980] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:26:55,021] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None -DEBUG [2019-11-23 02:29:46,280] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:29:46,419] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -INFO [2019-11-23 02:29:46,913] Website is working -DEBUG [2019-11-23 02:29:46,923] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:29:47,037] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None -DEBUG [2019-11-23 02:30:34,762] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:30:34,926] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 500 None -INFO [2019-11-23 02:30:35,440] Website is working -DEBUG [2019-11-23 02:30:35,447] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:30:35,489] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 500 None -DEBUG [2019-11-23 02:36:57,226] Starting new HTTP connection (1): 127.0.0.1:5000 -ERROR [2019-11-23 02:36:58,529] ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) -DEBUG [2019-11-23 02:37:05,092] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:37:05,105] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-23 02:37:05,631] Website is working -DEBUG [2019-11-23 02:37:05,638] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 02:37:16,710] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 812 -DEBUG [2019-11-23 23:44:01,402] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 23:44:01,935] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 23:44:02,349] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 23:44:06,846] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 37762 -DEBUG [2019-11-23 23:44:07,783] EUC-JP Japanese prober hit error at byte 10 -DEBUG [2019-11-23 23:44:08,048] EUC-KR Korean prober hit error at byte 10 -DEBUG [2019-11-23 23:44:08,295] Big5 Chinese prober hit error at byte 10 -DEBUG [2019-11-23 23:44:08,296] EUC-TW Taiwan prober hit error at byte 10 -DEBUG [2019-11-23 23:44:08,663] utf-8 not active -DEBUG [2019-11-23 23:44:08,666] SHIFT_JIS Japanese confidence = 0.01 -DEBUG [2019-11-23 23:44:08,689] EUC-JP not active -DEBUG [2019-11-23 23:44:08,689] GB2312 Chinese confidence = 0.01 -DEBUG [2019-11-23 23:44:08,689] EUC-KR not active -DEBUG [2019-11-23 23:44:08,689] CP949 Korean confidence = 0.01 -DEBUG [2019-11-23 23:44:08,690] Big5 not active -DEBUG [2019-11-23 23:44:08,690] EUC-TW not active -DEBUG [2019-11-23 23:44:08,690] windows-1251 Russian confidence = 0.01 -DEBUG [2019-11-23 23:44:08,690] KOI8-R Russian confidence = 0.01 -DEBUG [2019-11-23 23:44:08,691] ISO-8859-5 Russian confidence = 0.01 -DEBUG [2019-11-23 23:44:08,691] MacCyrillic Russian confidence = 0.0 -DEBUG [2019-11-23 23:44:08,691] IBM866 Russian confidence = 0.0 -DEBUG [2019-11-23 23:44:08,691] IBM855 Russian confidence = 0.01 -DEBUG [2019-11-23 23:44:08,691] ISO-8859-7 Greek confidence = 0.01 -DEBUG [2019-11-23 23:44:08,692] windows-1253 Greek confidence = 0.01 -DEBUG [2019-11-23 23:44:08,692] ISO-8859-5 Bulgairan confidence = 0.01 -DEBUG [2019-11-23 23:44:08,692] windows-1251 Bulgarian confidence = 0.01 -DEBUG [2019-11-23 23:44:08,697] TIS-620 Thai confidence = 0.01 -DEBUG [2019-11-23 23:44:08,698] ISO-8859-9 Turkish confidence = 0.1995084336143655 -DEBUG [2019-11-23 23:44:08,698] windows-1255 Hebrew confidence = 0.0 -DEBUG [2019-11-23 23:44:08,698] windows-1255 Hebrew confidence = 0.01 -DEBUG [2019-11-23 23:44:08,698] windows-1255 Hebrew confidence = 0.01 -DEBUG [2019-11-23 23:50:04,586] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 23:50:04,626] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 23:50:04,679] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 23:50:06,712] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 404 232 -DEBUG [2019-11-23 23:50:46,850] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 23:50:46,861] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-23 23:50:46,924] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-23 23:50:48,688] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 37758 -DEBUG [2019-11-23 23:50:49,428] EUC-JP Japanese prober hit error at byte 10 -DEBUG [2019-11-23 23:50:49,812] EUC-KR Korean prober hit error at byte 10 -DEBUG [2019-11-23 23:50:50,052] Big5 Chinese prober hit error at byte 10 -DEBUG [2019-11-23 23:50:50,053] EUC-TW Taiwan prober hit error at byte 10 -DEBUG [2019-11-23 23:50:50,365] utf-8 not active -DEBUG [2019-11-23 23:50:50,366] SHIFT_JIS Japanese confidence = 0.01 -DEBUG [2019-11-23 23:50:50,367] EUC-JP not active -DEBUG [2019-11-23 23:50:50,400] GB2312 Chinese confidence = 0.01 -DEBUG [2019-11-23 23:50:50,400] EUC-KR not active -DEBUG [2019-11-23 23:50:50,400] CP949 Korean confidence = 0.01 -DEBUG [2019-11-23 23:50:50,400] Big5 not active -DEBUG [2019-11-23 23:50:50,400] EUC-TW not active -DEBUG [2019-11-23 23:50:50,401] windows-1251 Russian confidence = 0.01 -DEBUG [2019-11-23 23:50:50,401] KOI8-R Russian confidence = 0.01 -DEBUG [2019-11-23 23:50:50,401] ISO-8859-5 Russian confidence = 0.01 -DEBUG [2019-11-23 23:50:50,401] MacCyrillic Russian confidence = 0.0 -DEBUG [2019-11-23 23:50:50,401] IBM866 Russian confidence = 0.0 -DEBUG [2019-11-23 23:50:50,402] IBM855 Russian confidence = 0.01 -DEBUG [2019-11-23 23:50:50,402] ISO-8859-7 Greek confidence = 0.01 -DEBUG [2019-11-23 23:50:50,402] windows-1253 Greek confidence = 0.01 -DEBUG [2019-11-23 23:50:50,402] ISO-8859-5 Bulgairan confidence = 0.01 -DEBUG [2019-11-23 23:50:50,402] windows-1251 Bulgarian confidence = 0.01 -DEBUG [2019-11-23 23:50:50,402] TIS-620 Thai confidence = 0.01 -DEBUG [2019-11-23 23:50:50,403] ISO-8859-9 Turkish confidence = 0.1991101760911726 -DEBUG [2019-11-23 23:50:50,403] windows-1255 Hebrew confidence = 0.0 -DEBUG [2019-11-23 23:50:50,404] windows-1255 Hebrew confidence = 0.01 -DEBUG [2019-11-23 23:50:50,404] windows-1255 Hebrew confidence = 0.01 -DEBUG [2019-11-25 02:54:15,159] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 02:54:17,178] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 02:54:18,119] Website is working -DEBUG [2019-11-25 02:54:18,127] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 02:54:22,516] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 903 -DEBUG [2019-11-25 16:01:44,926] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:01:44,964] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 16:01:45,942] Website is working -DEBUG [2019-11-25 16:01:45,949] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:01:49,421] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-25 16:02:10,691] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:02:10,698] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-25 16:02:10,725] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:02:13,083] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 500 None -DEBUG [2019-11-25 16:04:43,917] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:04:43,926] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 16:04:44,412] Website is working -DEBUG [2019-11-25 16:04:44,420] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:04:47,728] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-25 16:05:33,618] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:05:34,753] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 16:05:35,159] Website is working -DEBUG [2019-11-25 16:05:35,167] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:05:44,332] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-25 16:06:37,673] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:06:37,685] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 16:06:38,082] Website is working -DEBUG [2019-11-25 16:06:38,089] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:06:43,724] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-25 16:07:01,962] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:07:01,972] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 16:07:02,857] Website is working -DEBUG [2019-11-25 16:07:02,864] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:07:06,294] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-25 16:07:24,010] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:07:24,019] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 16:07:24,423] Website is working -DEBUG [2019-11-25 16:07:24,431] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:07:30,212] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 None -DEBUG [2019-11-25 16:08:53,003] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:08:53,060] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-25 16:08:53,113] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 16:08:53,703] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 952 -DEBUG [2019-11-25 21:08:08,384] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 21:08:08,402] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -DEBUG [2019-11-25 21:08:08,440] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 21:08:10,098] http://127.0.0.1:5000 "GET /getcache/ HTTP/1.1" 200 952 -DEBUG [2019-11-25 21:10:55,754] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 21:10:55,763] http://127.0.0.1:5000 "GET /version/ HTTP/1.1" 200 3 -INFO [2019-11-25 21:10:57,182] Website is working -DEBUG [2019-11-25 21:10:57,189] Starting new HTTP connection (1): 127.0.0.1:5000 -DEBUG [2019-11-25 21:11:41,580] http://127.0.0.1:5000 "GET /print/ HTTP/1.1" 200 10160 diff --git a/final_task/rss_reader/config.py b/final_task/rss_reader/config.py new file mode 100644 index 0000000..e8963fa --- /dev/null +++ b/final_task/rss_reader/config.py @@ -0,0 +1,20 @@ +from configparser import ConfigParser + + +def config(filename='database.ini', section='docker'): + # create a parser + parser = ConfigParser() + # read config file + parser.read(filename) + + # get section, default to postgresql + db = {} + if parser.has_section(section): + params = parser.items(section) + print(params) + for param in params: + db[param[0]] = param[1] + else: + raise Exception('Section {0} not found in the {1} file'.format(section, filename)) + + return db diff --git a/final_task/rss_reader/database.ini b/final_task/rss_reader/database.ini new file mode 100644 index 0000000..b6128d3 --- /dev/null +++ b/final_task/rss_reader/database.ini @@ -0,0 +1,12 @@ +[postgresql] +database=postgres +user=postgres +password=rssreader +host=localhost +port=5432 +[docker] +database=postgres +user=postgres +password=rssreader +host=db +port=5432 \ No newline at end of file diff --git a/final_task/rss_reader/docker-compose.yml b/final_task/rss_reader/docker-compose.yml index c14acaf..7a3644e 100644 --- a/final_task/rss_reader/docker-compose.yml +++ b/final_task/rss_reader/docker-compose.yml @@ -10,22 +10,18 @@ services: - 5000:5000 volumes: - .:/rss_reader_app - env_file: - - ./.env.dev + # env_file: + # - ./.env.dev depends_on: - - localhost + - db links: - - localhost + - db - localhost: + db: image: postgres:12.1-alpine restart: always ports: - 5432:5432 - environment: - POSTGRES_DB: postgres - POSTGRES_USER: postgres - POSTGRES_PASSWORD: rssreader volumes: - 'pg_data:/var/lib/postgresql/data' diff --git a/final_task/rss_reader/get_cache.py b/final_task/rss_reader/get_cache.py index b32dfff..1553d2d 100644 --- a/final_task/rss_reader/get_cache.py +++ b/final_task/rss_reader/get_cache.py @@ -6,7 +6,8 @@ import time import json -from . import logg, converter, news_parser, topdf +from . import logg, converter, news_parser, topdf, +from . import config def dateToStamp(arg_date): @@ -25,19 +26,25 @@ def getCacheFromDB(arg_date): 3. convert news into html or pdf if there are --tohtml or --topdf arguments 3. or print news in stdout ''' + con = None try: - with psycopg2.connect(database="postgres",user='postgres',password='rssreader',host='localhost',port='5432') as con: + params = config.config() + + with psycopg2.connect(**params) as con: with con.cursor() as cur: cur.execute('''SELECT title, link, image, description FROM news WHERE pub_date_stamp >= %s and pub_date_stamp < %s''', (dateToStamp(arg_date), dateToStamp(int(arg_date) + 1))) records = cur.fetchall() return records + except psycopg2.ProgrammingError as e: print("psycopg2.ProgrammingError: " + str(e)) logg.logging.error(str(e)) finally: - con.close() + if con is not None: + con.close() + logg.logging.info("Database connection closed") def collectNewsFromDB(limit, tojson, color, arg_date): @@ -49,7 +56,7 @@ def collectNewsFromDB(limit, tojson, color, arg_date): news = list() news.append(color[0] + 'Cache News: ') - + for index, row in enumerate(records): if(limit and index == limit): break diff --git a/final_task/rss_reader/news_parser.py b/final_task/rss_reader/news_parser.py index d4ef7a2..ef96d4f 100644 --- a/final_task/rss_reader/news_parser.py +++ b/final_task/rss_reader/news_parser.py @@ -11,6 +11,7 @@ import os from . import logg +from . import config def checkMediaContent(item): @@ -50,9 +51,11 @@ def cacheNews(channel): 2. create table in database 3. insert news into table ''' + con = None try: - with psycopg2.connect(database="postgres",user='postgres',password='rssreader', - host='localhost',port='5432') as con: + params = config.config() + + with psycopg2.connect(**params) as con: with con.cursor() as cur: cur.execute("""CREATE TABLE IF NOT EXISTS news (title text, link text, image bytea, @@ -63,12 +66,14 @@ def cacheNews(channel): news = insertNewsIntoTable(channel) cur.executemany("INSERT INTO news VALUES (%s,%s,%s,%s,%s) ON CONFLICT DO NOTHING", news) con.commit() - logg.logging.info("News cached into database") + except (Exception, psycopg2.DatabaseError) as e: logg.logging.error(str(e)) finally: - con.close() + if con is not None: + con.close() + logg.logging.info("Database connection closed") def insertNewsIntoTable(channel): diff --git a/final_task/tests/collectnews_test.py b/final_task/tests/collectnews_test.py index 83218a4..b2ff47c 100644 --- a/final_task/tests/collectnews_test.py +++ b/final_task/tests/collectnews_test.py @@ -1,11 +1,27 @@ import unittest from unittest.mock import patch +from feedparser import parse from rss_reader import collect_news from expected_results import normal_result, json_result, html_result -def testCollectNews(self): - self.assertEqual(collect_news.collectNews(1, False, '', '', ['','',''], self.item), normal_result) - self.assertEqual(collect_news.collectNews(1, True, '', '', ['','',''], self.item)[2], json_result) - self.assertEqual(collect_news.collectNews(1, False, 'tohtml', '', ['','',''], self.item), html_result) +class TestParser(unittest.TestCase): + def setUp(self): + self.item = '' \ + 'Yahoo News - Latest News & Headlines' \ + 'item_title' \ + 'item_description' \ + 'item_linkitem_date' \ + 'media_link' + self.parse_item = parse(self.item) + + def testCollectNews(self): + self.assertEqual(collect_news.collectNews(1, False, '', '', ['','',''], self.item), normal_result) + self.assertEqual(collect_news.collectNews(1, True, '', '', ['','',''], self.item)[2], json_result) + self.assertEqual(collect_news.collectNews(1, False, 'tohtml', '', ['','',''], self.item), html_result) + + +if __name__=='__main__': + + unittest.main() \ No newline at end of file diff --git a/final_task/tests/getcache_test.py b/final_task/tests/getcache_test.py index b133c2e..7019316 100644 --- a/final_task/tests/getcache_test.py +++ b/final_task/tests/getcache_test.py @@ -4,52 +4,59 @@ from expected_results import html_result_from_db +class TestParser(unittest.TestCase): -ef testGetCacheFromDB(self): - with patch("psycopg2.connect") as mock_connect: - expected = ['title', 'link', 'image', 'description'] + def testGetCacheFromDB(self): + with patch("psycopg2.connect") as mock_connect: + expected = ['title', 'link', 'image', 'description'] - mock_con_cm = mock_connect.return_value - mock_con = mock_con_cm.__enter__.return_value + mock_con_cm = mock_connect.return_value + mock_con = mock_con_cm.__enter__.return_value - mock_cur_cm = mock_con.cursor.return_value - mock_cur = mock_cur_cm.__enter__.return_value + mock_cur_cm = mock_con.cursor.return_value + mock_cur = mock_cur_cm.__enter__.return_value - mock_cur.fetchall.return_value = expected + mock_cur.fetchall.return_value = expected - result = get_cache.getCacheFromDB('20191125') - self.assertEqual(result, expected) + result = get_cache.getCacheFromDB('20191125') + self.assertEqual(result, expected) - mock_connect.assert_called_with(database="postgres",user='postgres',password='rssreader',host='localhost',port='5432') + mock_connect.assert_called_with(database="postgres",user='postgres',password='rssreader',host='localhost',port='5432') -def testCollectNewsFromDB(self): - with patch('rss_reader.get_cache.getCacheFromDB') as mock_cache: - mock_cache.return_value = [('title', 'link', 'image', 'description')] + def testCollectNewsFromDB(self): + with patch('rss_reader.get_cache.getCacheFromDB') as mock_cache: + mock_cache.return_value = [('title', 'link', 'image', 'description')] - expected = ['Cache News: ', '', '\nTitle: title', '\nLink: link\n', 'Description: description\n'] + expected = ['Cache News: ', '', '\nTitle: title', '\nLink: link\n', 'Description: description\n'] - result = get_cache.collectNewsFromDB(1, False, ['','',''], '20191125') - self.assertEqual(result, expected) + result = get_cache.collectNewsFromDB(1, False, ['','',''], '20191125') + self.assertEqual(result, expected) + + expectedJson = ['Cache News: ', '', '{"Title: ": "title", "Link: ": "link", "Description": "description"}'] - expectedJson = ['Cache News: ', '', '{"Title: ": "title", "Link: ": "link", "Description": "description"}'] + result = get_cache.collectNewsFromDB(1, True, ['','',''], '20191125') + self.assertEqual(result, expectedJson) - result = get_cache.collectNewsFromDB(1, True, ['','',''], '20191125') - self.assertEqual(result, expectedJson) + def testCreateHtmlFromDB(self): + with patch('rss_reader.get_cache.getCacheFromDB') as mock_cache: + mock_cache.return_value = [('item_title', 'item_link', '', 'item_description')] -def testCreateHtmlFromDB(self): - with patch('rss_reader.get_cache.getCacheFromDB') as mock_cache: - mock_cache.return_value = [('item_title', 'item_link', '', 'item_description')] + expected = html_result_from_db - expected = html_result_from_db + result = get_cache.createHtmlFromDB(1, 'html_path', '', '20191125') + self.assertEqual(result, expected) + + + def testDateToStamp(self): + expected = 1574629200.0 + result = get_cache.dateToStamp('20191125') - result = get_cache.createHtmlFromDB(1, 'html_path', '', '20191125') self.assertEqual(result, expected) -def testDateToStamp(self): - expected = 1574629200.0 - result = get_cache.dateToStamp('20191125') +if __name__=='__main__': + + unittest.main() - self.assertEqual(result, expected) \ No newline at end of file diff --git a/final_task/tests/parser.log b/final_task/tests/parser.log new file mode 100644 index 0000000..fcd70ed --- /dev/null +++ b/final_task/tests/parser.log @@ -0,0 +1,20 @@ +ERROR [2019-11-27 01:01:27,452] cannot unpack non-iterable NoneType object +DEBUG [2019-11-27 01:01:27,453] Title: item_title +DEBUG [2019-11-27 01:01:27,453] Date: item_date +DEBUG [2019-11-27 01:01:27,453] Link: item_link +DEBUG [2019-11-27 01:01:27,454] Description: item_description +DEBUG [2019-11-27 01:01:27,454] Links: +[1]: item_link(link) +[2]: media_link_url + +ERROR [2019-11-27 01:01:27,659] cannot unpack non-iterable NoneType object +DEBUG [2019-11-27 01:01:27,660] Title: item_title +DEBUG [2019-11-27 01:01:27,660] Date: item_date +DEBUG [2019-11-27 01:01:27,661] Link: item_link +DEBUG [2019-11-27 01:01:27,661] Description: item_description +DEBUG [2019-11-27 01:01:27,662] Links: +[1]: item_link(link) +[2]: media_link_url + +ERROR [2019-11-27 01:01:27,884] cannot unpack non-iterable NoneType object +ERROR [2019-11-27 01:03:26,385] cannot unpack non-iterable NoneType object diff --git a/final_task/tests/topdf_test.py b/final_task/tests/topdf_test.py index 6a53ed6..c700f54 100644 --- a/final_task/tests/topdf_test.py +++ b/final_task/tests/topdf_test.py @@ -4,21 +4,27 @@ from expected_results import html_result -@patch('rss_reader.topdf.datetime') -@patch('builtins.open') -def testConvertToPdf(self, mock_file, mock_time): - mock_time.strftime.return_value = '191749' +class TestParser(unittest.TestCase): - result = topdf.convertHtmlToPdf(html_result, 'pdf_path/') - expected = 'NewsFeed-191749.pdf' + @patch('rss_reader.topdf.datetime') + @patch('builtins.open') + def testConvertToPdf(self, mock_file, mock_time): + mock_time.strftime.return_value = '191749' - mock_file.assert_called_with('pdf_path/NewsFeed-191749.pdf', "w+b") - self.assertEqual(result, expected) + result = topdf.convertHtmlToPdf(html_result, 'pdf_path/') + expected = 'NewsFeed-191749.pdf' + mock_file.assert_called_with('pdf_path/NewsFeed-191749.pdf', "w+b") + self.assertEqual(result, expected) -def testSplitHost(self): - url = 'https://news.yahoo.com/rss' - expected = ('news.yahoo.com', '/rss') - result = topdf.splithost_polyfill(url) - self.assertEqual(result, expected) + def testSplitHost(self): + url = 'https://news.yahoo.com/rss' + expected = ('news.yahoo.com', '/rss') + result = topdf.splithost_polyfill(url) + self.assertEqual(result, expected) + + +if __name__=='__main__': + + unittest.main()