Skip to content

Mlynarchik Artyom #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e8be149
Initial commit
Nov 10, 2019
b060fb3
Main functionality made
Nov 17, 2019
8b8d8a1
Added display of article hrefs
Nov 17, 2019
bebd6cf
Create README.md
Archeex Nov 17, 2019
bfa6e10
Update README.md
Archeex Nov 17, 2019
4df505d
Fix imports for project distribution
Nov 17, 2019
56dd8eb
Update README.md
Nov 17, 2019
94a0554
Update README.md
Archeex Nov 17, 2019
29766e9
Update README.md
Archeex Nov 17, 2019
1b5b06d
Update utility name in setup.py
Nov 17, 2019
01ce0c7
Add opportunity to print json to console, enumerate replaced with slices
Nov 26, 2019
1953b34
News caching done
Nov 30, 2019
22ac84d
Update README.md
Archeex Nov 30, 2019
db491e0
Argument '--date' implemented [Iteration 3 completed]
Nov 30, 2019
2bd4b78
Argument '--date' implemented [Iteration 3 completed]
Nov 30, 2019
3ca84ef
Code refactor
Nov 30, 2019
719b0cf
Code refactor
Nov 30, 2019
4111eaa
Update json_schema.json
Archeex Nov 30, 2019
abe312f
Update README.md
Archeex Nov 30, 2019
9ada778
Code refactor and update setup.py
Nov 30, 2019
466ffd7
Code refactor and update setup.py
Nov 30, 2019
28bc86d
PDFConverter implemented, some code refactor
Dec 1, 2019
93d4e04
Update README.md
Archeex Dec 1, 2019
f9b6fa9
Update README.md
Archeex Dec 1, 2019
8e7209b
Update README.md
Archeex Dec 1, 2019
4cb5555
Update README.md
Archeex Dec 1, 2019
208e667
HTMLConverter implemented
Dec 1, 2019
23ff39e
HTMLConverter implemented
Dec 1, 2019
c677187
Fix launch problems
Dec 1, 2019
f5767c1
Console output colorized. Iteration 5 completed
Dec 1, 2019
87a1e92
Fix launch problems
Dec 1, 2019
92bd0d1
Add requirements.txt
Dec 1, 2019
6e786eb
Code refactoring
Dec 1, 2019
3785b40
Code refactor and adding tests
Dec 1, 2019
cf99556
Fix launch problems
Dec 1, 2019
fc70aa5
Delete cached_news.json
Archeex Dec 1, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Pure Python RSS Reader [PythonHomework]

Python version: v3.8

Current version: v0.5

Code checking: Code correspond to pep8
#### Usage:
```shell
usage: __main__.py [-h] [--version] [--json] [--verbose] [--limit LIMIT] [--date DATE] [--to-pdf] [--to-html] source

Pure Python command-line RSS reader.

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
--date DATE Show cached news by input date
--to-pdf Convert news to pdf format
--to-html Convert news to html format
```
JSON scheme is described in `json_schema.json`.

News cache in json file `cached_news.json` in root application directory.
31 changes: 31 additions & 0 deletions json_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/schema#",
"title": "feed",
"type": "object",
"required": ["title", "date", "text", "link", "hrefs"],
"properties": {
"title": {
"type": "string",
"description": "News title"
},
"date": {
"type": "date",
"description": "News published date"
},
"text": {
"type": "string",
"description": "News text"
},
"link": {
"type": "string",
"description": "News static link"
},
"hrefs": {
"type": "array",
"items": {
"type": "string",
"description": "News hrefs"
}
}
}
}
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
feedparser==5.2.1
requests==2.22.0
tldextract==2.2.2
fpdf==1.7.2
colorama==0.4.1
lxml==4.4.2
Binary file added rss_reader/FreeSans.ttf
Binary file not shown.
Empty file added rss_reader/__init__.py
Empty file.
43 changes: 43 additions & 0 deletions rss_reader/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Main module"""

import argparse
import logging

from colorama import init

from .rss_reader import NewsReader


def main():
init() # colorama init
args = parse_args()

if args.verbose:
logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.INFO)
else:
logging.basicConfig(format="%(levelname)s: %(message)s")

reader = NewsReader(args.source, args.limit, args.json, args.date, args.to_pdf, args.to_html, args.colorize)
reader.parse_url()

reader.print_news()


def parse_args():
parser = argparse.ArgumentParser(description='Pure Python command-line RSS reader.')

parser.add_argument('source', help='RSS URL')
parser.add_argument('--version', help='Print version info', action='version', version='%(prog)s 0.4')
parser.add_argument('--json', help='Print result as JSON in stdout', action='store_true')
parser.add_argument('--verbose', help='Outputs verbose status messages', action='store_true')
parser.add_argument('--limit', help='Limit news topics if this parameter provided', type=int)
parser.add_argument('--date', help='Show cached news by input date', type=str)
parser.add_argument('--to-pdf', help='Convert news to pdf format', action='store_true')
parser.add_argument('--to-html', help='Convert news to html format', action='store_true')
parser.add_argument('--colorize', help='Colorize output text', action='store_true')

return parser.parse_args()


if __name__ == '__main__':
main()
8 changes: 8 additions & 0 deletions rss_reader/date_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from datetime import datetime

def is_valid_date(date, pattern):
try:
datetime.strptime(date, pattern)
return True
except ValueError:
return False
35 changes: 35 additions & 0 deletions rss_reader/html_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""HTML converter module"""

import logging


class HTMLConverter:
def __init__(self, data, file_name):
self.data = data
self.file_name = file_name

def dump(self):
"""Create and fill HTML-file"""
logging.info("Create and fill HTML-file")

html_code = f'<html>\n<head>\n<title>{self.file_name}</title>\n<meta http-equiv="Content-Type" \
content="text/html; charset=utf-8">\n</head>\n<body>\n'

for element in self.data:
html_code += f'<b><p align="center", style="font-family:\'Segoe UI\', Tahoma, Geneva, Verdana, \
sans-serif; color:green; font-size: 26px">{element["title"]}</p></b>\n'
html_code += f'<p align="center", style="font-family:\'Segoe UI\', Tahoma, Geneva, Verdana, \
sans-serif; color:rgb(25, 92, 25); font-size: 14px">{element["date"]}</p>\n'
html_code += f'<p align="center", style="font-family:\'Segoe UI\', Tahoma, Geneva, Verdana, \
sans-serif; color:rgb(25, 92, 25); font-size: 14px"><a href="{element["link"]}">{element["link"]} \
</a></p>\n'
html_code += f'<p style="font-family:\'Segoe UI\', Tahoma, Geneva, Verdana, sans-serif; \
color:rgb(24, 24, 24); font-size: 18px">{element["text"]}</p>\n'

for href in element["hrefs"]:
html_code += f'<p style="text-align:center"><img src="{href}" height="200"></p>\n'

html_code += '</body>\n</html>'

with open(f'{self.file_name}.html', 'w+', encoding='utf-8') as html_file:
html_file.write(html_code)
33 changes: 33 additions & 0 deletions rss_reader/json_formatter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Module with tools for working with Json"""

import json
import logging


class NewsJsonFormatter:
def __init__(self):
self.data = {}

def __str__(self):
"""Compute json-file for print news to console in json-format"""
logging.info('Compute json-file for print news to console in json-format')

return json.dumps(self.data, ensure_ascii=False, indent=4)

def write_to_file(self):
"""Write json-data to file"""
logging.info('Write json-data to file')

with open('data.json', 'w', encoding='utf-8') as outfile:
json.dump(self.data, outfile, ensure_ascii=False, indent=4)

def format(self, news):
"""Format data to json appereance"""
logging.info('Format data to json appereance')

self.data = {
'feed': []
}

for element in news:
self.data['feed'].append(element)
60 changes: 60 additions & 0 deletions rss_reader/news_cacher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""News cacher module"""

import logging
import json

from datetime import datetime
from os.path import abspath, getsize, exists

from .json_formatter import NewsJsonFormatter


class NewsCacher:
def __init__(self, file_name, source):
self.file_name = file_name
self.source = source
self.date = datetime.now().strftime('%Y%m%d')
self.data = None

def get_cached_news(self, date, limit):
"""Get cached news from json file"""
logging.info("Get cached news from json file")

with open(self.file_name, 'r', encoding='utf-8') as json_file:
self.data = json.load(json_file)

try:
return self.data[self.source][date][:limit]
except (KeyError, TypeError):
raise ValueError

def cache(self, json_object):
"""Cache news to json file"""
logging.info("Cache news to json file")

self.data = {
self.source: {
self.date: []
}
}

if not exists(abspath(self.file_name)):
open(self.file_name, 'w').close()

file_size = getsize(abspath(self.file_name))

if file_size != 0:
with open(self.file_name, 'r', encoding='utf-8') as json_file:
self.data = json.load(json_file)

if self.source not in self.data:
self.data.update({self.source: {}})
if self.date not in self.data[self.source]:
self.data[self.source].update({self.date: []})

for element in json_object:
if element not in self.data[self.source][self.date]:
self.data[self.source][self.date].append(element)

with open(self.file_name, 'w', encoding='utf-8') as outfile:
json.dump(self.data, outfile, ensure_ascii=False, indent=4)
67 changes: 67 additions & 0 deletions rss_reader/pdf_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""PDF converter module"""

import logging
import requests

from fpdf import SYSTEM_TTFONTS, FPDF

SYSTEM_TTFONTS = ''


class PDFConverter:
def __init__(self, data, file_name):
self.data = data
self.file_name = file_name
self.pdf = FPDF(orientation='P', unit='mm', format='A4')

def dump(self):
"""Create and fill PDF-file"""
logging.info("Create and fill PDF-file")

margin = 5

self.pdf.add_page()
self.pdf.set_auto_page_break(True, 10 * margin)

for element in self.data:
self.pdf.add_font('FreeSans', '', 'FreeSans.ttf', uni=True)
self.pdf.set_fill_color(62, 147, 96)

self.pdf.set_text_color(255, 255, 255)
self.pdf.set_font("FreeSans", size=14)
self.pdf.multi_cell(0, 8, txt=element["title"], align="C", fill=1)
self.pdf.set_fill_color(90, 167, 120)
self.pdf.set_font("FreeSans", size=10)
self.pdf.multi_cell(0, 5, txt=element["date"], align="R", fill=1)
self.pdf.set_font("FreeSans", size=10)
self.pdf.multi_cell(0, 5, txt=element["link"], align="R", fill=1)

self.pdf.set_fill_color(229, 229, 229)
self.pdf.set_text_color(0, 0, 0)
self.pdf.set_font("FreeSans", size=12)
self.pdf.multi_cell(0, 6, txt=element["text"], align="J", fill=1)

self.pdf.set_fill_color(242, 242, 242)
self.pdf.set_text_color(0, 0, 0)
self.pdf.set_font("FreeSans", size=10)

self.pdf.ln(margin)

for href in element["hrefs"]:
page_height = 300
image_height = 50

try:
if page_height - self.pdf.get_y() < image_height + margin:
self.pdf.add_page()

if href[-4:] == '.png':
self.pdf.image(href, x=self.pdf.get_x() + image_height + margin, y=self.pdf.get_y(),
h=image_height)
self.pdf.ln(image_height + margin)
except Exception:
logging.error('Cant get an image from url')

self.pdf.multi_cell(0, margin, txt="", align="J")

self.pdf.output(f'{self.file_name}.pdf')
Loading