From 1ff1842c597c696a1de6338103256d4271ca15ab Mon Sep 17 00:00:00 2001 From: Valery Litskevich Date: Tue, 12 Apr 2022 00:01:28 +0500 Subject: [PATCH 1/8] Add homework for session 2 --- Homework-2_Python-Data_Types.md | 74 +++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Homework-2_Python-Data_Types.md diff --git a/Homework-2_Python-Data_Types.md b/Homework-2_Python-Data_Types.md new file mode 100644 index 00000000..69c73afc --- /dev/null +++ b/Homework-2_Python-Data_Types.md @@ -0,0 +1,74 @@ +# Python Practice - Session 2 + +### Task 2.1 +Write a Python program to calculate the length of a string without using the `len` function. + +### Task 2.2 +Write a Python program to count the number of characters (character frequency) in a string (ignore case of letters). +Examples: +``` +Input: 'Oh, it is python' +Output: {',': 1, ' ': 3, 'o': 2, 'h': 2, 'i': 2, 't': 2, 's': 1, 'p': 1, 'y': 1, 'n': 1} +``` + +### Task 2.3 +Write a Python program that accepts a comma separated sequence of words as input and prints the unique words in sorted form. +Examples: +``` +Input: ['red', 'white', 'black', 'red', 'green', 'black'] +Output: ['black', 'green', 'red', 'white', 'red'] +``` + +### Task 2.3 +Create a program that asks the user for a number and then prints out a list of all the [divisors](https://en.wikipedia.org/wiki/Divisor) of that number. +Examples: +``` +Input: 60 +Output: {1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60} +``` + +### Task 2.4 +Write a Python program to sort a dictionary by key. + +### Task 2.5 +Write a Python program to print all unique values of all dictionaries in a list. +Examples: +``` +Input: [{"V":"S001"}, {"V": "S002"}, {"VI": "S001"}, {"VI": "S005"}, {"VII":"S005"}, {"V":"S009"},{"VIII":"S007"}] +Output: {'S005', 'S002', 'S007', 'S001', 'S009'} +``` + +### Task 2.6 +Write a Python program to convert a given tuple of positive integers into an integer. +Examples: +``` +Input: (1, 2, 3, 4) +Output: 1234 +``` + + +### Task 2.7 +Write a program which makes a pretty print of a part of the multiplication table. +Examples: +``` +Input: +a = 2 +b = 4 +c = 3 +d = 7 + +Output: + 3 4 5 6 7 +2 6 8 10 12 14 +3 9 12 15 18 21 +4 12 16 20 24 28 +``` + +### Materials +* [Python Data Types](https://realpython.com/python-data-types/) +* [Python Data Structures](https://realpython.com/python-data-structures/) +* [Conditional Statements](https://realpython.com/python-conditional-statements/) +* [While loop](https://realpython.com/python-while-loop/) +* [For loop](https://realpython.com/python-for-loop/) +* [Operators](http://pythonicway.com/python-operators) + From 8e04a8be35bf04704d7cc89ca5e35140ccf176ef Mon Sep 17 00:00:00 2001 From: Nurmatov Farrukh <84759185+unrealmw@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:17:26 +0500 Subject: [PATCH 2/8] add folder with HW session_2 --- Farrukh Nurmatov/Task 2.1.py | 15 +++++++++++++++ Farrukh Nurmatov/Task 2.2.py | 19 +++++++++++++++++++ Farrukh Nurmatov/Task 2.3.1.py | 10 ++++++++++ Farrukh Nurmatov/Task 2.3.2.py | 18 ++++++++++++++++++ Farrukh Nurmatov/Task 2.4.py | 15 +++++++++++++++ Farrukh Nurmatov/Task 2.5.py | 5 +++++ Farrukh Nurmatov/Task 2.6.py | 13 +++++++++++++ Farrukh Nurmatov/Task 2.7.py | 29 +++++++++++++++++++++++++++++ 8 files changed, 124 insertions(+) create mode 100644 Farrukh Nurmatov/Task 2.1.py create mode 100644 Farrukh Nurmatov/Task 2.2.py create mode 100644 Farrukh Nurmatov/Task 2.3.1.py create mode 100644 Farrukh Nurmatov/Task 2.3.2.py create mode 100644 Farrukh Nurmatov/Task 2.4.py create mode 100644 Farrukh Nurmatov/Task 2.5.py create mode 100644 Farrukh Nurmatov/Task 2.6.py create mode 100644 Farrukh Nurmatov/Task 2.7.py diff --git a/Farrukh Nurmatov/Task 2.1.py b/Farrukh Nurmatov/Task 2.1.py new file mode 100644 index 00000000..cb9913e9 --- /dev/null +++ b/Farrukh Nurmatov/Task 2.1.py @@ -0,0 +1,15 @@ +"""Write a Python program to calculate the length of a string without using the `len` function.""" + + +def my_count(countable_str: str): + + """Function takes string and counts characters, moving through the string in 'for' loop and increasing counter.""" + + c = 0 + for char in countable_str: + c += 1 + return c + +if __name__ == '__main__': + input_string = input("Input:") + print(f"Output: {my_count(input_string)}") diff --git a/Farrukh Nurmatov/Task 2.2.py b/Farrukh Nurmatov/Task 2.2.py new file mode 100644 index 00000000..4af5d9fe --- /dev/null +++ b/Farrukh Nurmatov/Task 2.2.py @@ -0,0 +1,19 @@ +"""Write a Python program to count the number of characters + (character frequency) in a string (ignore case of letters).""" + + + +def freq_counter(some_str: str): + + """Function takes string in input and returns dictionary of letters and letters frequency in string, + ignoring case of the letters""" + + some_str = some_str.lower() # set all letters in string to lowercase + counter_dict = dict() + for char in set(list(some_str)): # loop that moves across letters set + counter_dict[char] = input_str.count(char) # add letter: letter_frequency to dictionary + return counter_dict + +if __name__ == '__main__': + input_str = input("Input your string:") + print(freq_counter(input_str)) diff --git a/Farrukh Nurmatov/Task 2.3.1.py b/Farrukh Nurmatov/Task 2.3.1.py new file mode 100644 index 00000000..42df0787 --- /dev/null +++ b/Farrukh Nurmatov/Task 2.3.1.py @@ -0,0 +1,10 @@ +"""Write a Python program that accepts a comma separated sequence of + words as input and prints the unique words in sorted form. + + Input: ['red', 'white', 'black', 'red', 'green', 'black'] + Output: ['black', 'green', 'red', 'white', 'red']""" + +input_seq = ['red', 'white', 'black', 'red', 'green', 'black'] +unique_words = list(set(input_seq)) + +print("Output:", sorted(unique_words), sep=" ") \ No newline at end of file diff --git a/Farrukh Nurmatov/Task 2.3.2.py b/Farrukh Nurmatov/Task 2.3.2.py new file mode 100644 index 00000000..c32bcb65 --- /dev/null +++ b/Farrukh Nurmatov/Task 2.3.2.py @@ -0,0 +1,18 @@ +"""Create a program that asks the user for a number and then prints out a list of all the + [divisors](https://en.wikipedia.org/wiki/Divisor) of that number. + Examples: + Input: 60 + Output: [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60]""" + + +def divisors_lst(num: int): + + """Function takes integer number to input and return list of divisors.""" + + divisors = [i for i in range(1, num + 1) if num % i == 0] + return divisors + + +if __name__ == '__main__': + number = int(input("Input:")) + print("Output:", divisors_lst(number), sep=" ") \ No newline at end of file diff --git a/Farrukh Nurmatov/Task 2.4.py b/Farrukh Nurmatov/Task 2.4.py new file mode 100644 index 00000000..9aed2d85 --- /dev/null +++ b/Farrukh Nurmatov/Task 2.4.py @@ -0,0 +1,15 @@ +"""Write a Python program to sort a dictionary by key.""" + +example_dict = {1: "stas", 3: "misha", 4: "fara", 5: "dima", 2: "serega"} # initial dict +dict_keys = list(example_dict.keys()) # create list of keys +dict_keys.sort() # sorting list of keys + +sorted_dict = dict() # create new dict + +# add key: value pair to dict, taking key from sorted list and getting value from initial dict by key +for key in dict_keys: + sorted_dict[key] = example_dict.get(key) + + +print(example_dict) # initial dict +print(sorted_dict) # sorted dict \ No newline at end of file diff --git a/Farrukh Nurmatov/Task 2.5.py b/Farrukh Nurmatov/Task 2.5.py new file mode 100644 index 00000000..a8a994a2 --- /dev/null +++ b/Farrukh Nurmatov/Task 2.5.py @@ -0,0 +1,5 @@ +a = (1, 2, 3, 4) +s = "" +for i in a: + s += str(i) +print(int(s)) diff --git a/Farrukh Nurmatov/Task 2.6.py b/Farrukh Nurmatov/Task 2.6.py new file mode 100644 index 00000000..b5a965eb --- /dev/null +++ b/Farrukh Nurmatov/Task 2.6.py @@ -0,0 +1,13 @@ +"""Write a Python program to print all unique values of all dictionaries in a list.""" + + +lst = [{"V": "S001"}, {"V": "S002"}, {"VI": "S001"}, {"VI": "S005"}, + {"VII": "S005"}, {"V": "S009"}, {"VIII": "S007"}] # example list of dicts + +unique_values = set() # empty set for unique values + +# add all values from dicts to make them unique +for i in lst: + unique_values.update(set(i.values())) + +print(unique_values) diff --git a/Farrukh Nurmatov/Task 2.7.py b/Farrukh Nurmatov/Task 2.7.py new file mode 100644 index 00000000..9245aef4 --- /dev/null +++ b/Farrukh Nurmatov/Task 2.7.py @@ -0,0 +1,29 @@ +"""Write a program which makes a pretty print of a part of the multiplication table.""" + +# input parameters +a = 2 +b = 4 +c = 3 +d = 7 + + +# calculating multiplication table +matrix = [] +for i in range(a, b + 1): + lst = [] + for j in range(c, d + 1): + lst.append(i * j) + matrix.append(lst) + +# calculating parameters for .ljust() function +max_num_len = len(str(matrix[-1][-1])) # length of the one cell in the table +max_str_len = len("0 " + " ".join(str(i).ljust(len(str(i))) for i in matrix[-1])) # length of the one row in the table + +"""pretty printing""" +# top row of multipliers printing +print(" ".join(str(i).ljust(max_num_len) for i in range(c, d + 1)).rjust(max_str_len)) + +for i in range(a, b + 1): + print(i, end=" ") # colon multiplier printing + print(" ".join(str(i).ljust(max_num_len) for i in matrix[i - 2]).ljust(max_str_len)) # print row of the table + From d4735ecc92a3f626b15eeb6675c9f6e463b09461 Mon Sep 17 00:00:00 2001 From: Nurmatov Farrukh <84759185+unrealmw@users.noreply.github.com> Date: Mon, 18 Apr 2022 10:53:51 +0500 Subject: [PATCH 3/8] Delete Farrukh Nurmatov directory --- Farrukh Nurmatov/Task 2.1.py | 15 --------------- Farrukh Nurmatov/Task 2.2.py | 19 ------------------- Farrukh Nurmatov/Task 2.3.1.py | 10 ---------- Farrukh Nurmatov/Task 2.3.2.py | 18 ------------------ Farrukh Nurmatov/Task 2.4.py | 15 --------------- Farrukh Nurmatov/Task 2.5.py | 5 ----- Farrukh Nurmatov/Task 2.6.py | 13 ------------- Farrukh Nurmatov/Task 2.7.py | 29 ----------------------------- 8 files changed, 124 deletions(-) delete mode 100644 Farrukh Nurmatov/Task 2.1.py delete mode 100644 Farrukh Nurmatov/Task 2.2.py delete mode 100644 Farrukh Nurmatov/Task 2.3.1.py delete mode 100644 Farrukh Nurmatov/Task 2.3.2.py delete mode 100644 Farrukh Nurmatov/Task 2.4.py delete mode 100644 Farrukh Nurmatov/Task 2.5.py delete mode 100644 Farrukh Nurmatov/Task 2.6.py delete mode 100644 Farrukh Nurmatov/Task 2.7.py diff --git a/Farrukh Nurmatov/Task 2.1.py b/Farrukh Nurmatov/Task 2.1.py deleted file mode 100644 index cb9913e9..00000000 --- a/Farrukh Nurmatov/Task 2.1.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Write a Python program to calculate the length of a string without using the `len` function.""" - - -def my_count(countable_str: str): - - """Function takes string and counts characters, moving through the string in 'for' loop and increasing counter.""" - - c = 0 - for char in countable_str: - c += 1 - return c - -if __name__ == '__main__': - input_string = input("Input:") - print(f"Output: {my_count(input_string)}") diff --git a/Farrukh Nurmatov/Task 2.2.py b/Farrukh Nurmatov/Task 2.2.py deleted file mode 100644 index 4af5d9fe..00000000 --- a/Farrukh Nurmatov/Task 2.2.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Write a Python program to count the number of characters - (character frequency) in a string (ignore case of letters).""" - - - -def freq_counter(some_str: str): - - """Function takes string in input and returns dictionary of letters and letters frequency in string, - ignoring case of the letters""" - - some_str = some_str.lower() # set all letters in string to lowercase - counter_dict = dict() - for char in set(list(some_str)): # loop that moves across letters set - counter_dict[char] = input_str.count(char) # add letter: letter_frequency to dictionary - return counter_dict - -if __name__ == '__main__': - input_str = input("Input your string:") - print(freq_counter(input_str)) diff --git a/Farrukh Nurmatov/Task 2.3.1.py b/Farrukh Nurmatov/Task 2.3.1.py deleted file mode 100644 index 42df0787..00000000 --- a/Farrukh Nurmatov/Task 2.3.1.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Write a Python program that accepts a comma separated sequence of - words as input and prints the unique words in sorted form. - - Input: ['red', 'white', 'black', 'red', 'green', 'black'] - Output: ['black', 'green', 'red', 'white', 'red']""" - -input_seq = ['red', 'white', 'black', 'red', 'green', 'black'] -unique_words = list(set(input_seq)) - -print("Output:", sorted(unique_words), sep=" ") \ No newline at end of file diff --git a/Farrukh Nurmatov/Task 2.3.2.py b/Farrukh Nurmatov/Task 2.3.2.py deleted file mode 100644 index c32bcb65..00000000 --- a/Farrukh Nurmatov/Task 2.3.2.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Create a program that asks the user for a number and then prints out a list of all the - [divisors](https://en.wikipedia.org/wiki/Divisor) of that number. - Examples: - Input: 60 - Output: [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60]""" - - -def divisors_lst(num: int): - - """Function takes integer number to input and return list of divisors.""" - - divisors = [i for i in range(1, num + 1) if num % i == 0] - return divisors - - -if __name__ == '__main__': - number = int(input("Input:")) - print("Output:", divisors_lst(number), sep=" ") \ No newline at end of file diff --git a/Farrukh Nurmatov/Task 2.4.py b/Farrukh Nurmatov/Task 2.4.py deleted file mode 100644 index 9aed2d85..00000000 --- a/Farrukh Nurmatov/Task 2.4.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Write a Python program to sort a dictionary by key.""" - -example_dict = {1: "stas", 3: "misha", 4: "fara", 5: "dima", 2: "serega"} # initial dict -dict_keys = list(example_dict.keys()) # create list of keys -dict_keys.sort() # sorting list of keys - -sorted_dict = dict() # create new dict - -# add key: value pair to dict, taking key from sorted list and getting value from initial dict by key -for key in dict_keys: - sorted_dict[key] = example_dict.get(key) - - -print(example_dict) # initial dict -print(sorted_dict) # sorted dict \ No newline at end of file diff --git a/Farrukh Nurmatov/Task 2.5.py b/Farrukh Nurmatov/Task 2.5.py deleted file mode 100644 index a8a994a2..00000000 --- a/Farrukh Nurmatov/Task 2.5.py +++ /dev/null @@ -1,5 +0,0 @@ -a = (1, 2, 3, 4) -s = "" -for i in a: - s += str(i) -print(int(s)) diff --git a/Farrukh Nurmatov/Task 2.6.py b/Farrukh Nurmatov/Task 2.6.py deleted file mode 100644 index b5a965eb..00000000 --- a/Farrukh Nurmatov/Task 2.6.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Write a Python program to print all unique values of all dictionaries in a list.""" - - -lst = [{"V": "S001"}, {"V": "S002"}, {"VI": "S001"}, {"VI": "S005"}, - {"VII": "S005"}, {"V": "S009"}, {"VIII": "S007"}] # example list of dicts - -unique_values = set() # empty set for unique values - -# add all values from dicts to make them unique -for i in lst: - unique_values.update(set(i.values())) - -print(unique_values) diff --git a/Farrukh Nurmatov/Task 2.7.py b/Farrukh Nurmatov/Task 2.7.py deleted file mode 100644 index 9245aef4..00000000 --- a/Farrukh Nurmatov/Task 2.7.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Write a program which makes a pretty print of a part of the multiplication table.""" - -# input parameters -a = 2 -b = 4 -c = 3 -d = 7 - - -# calculating multiplication table -matrix = [] -for i in range(a, b + 1): - lst = [] - for j in range(c, d + 1): - lst.append(i * j) - matrix.append(lst) - -# calculating parameters for .ljust() function -max_num_len = len(str(matrix[-1][-1])) # length of the one cell in the table -max_str_len = len("0 " + " ".join(str(i).ljust(len(str(i))) for i in matrix[-1])) # length of the one row in the table - -"""pretty printing""" -# top row of multipliers printing -print(" ".join(str(i).ljust(max_num_len) for i in range(c, d + 1)).rjust(max_str_len)) - -for i in range(a, b + 1): - print(i, end=" ") # colon multiplier printing - print(" ".join(str(i).ljust(max_num_len) for i in matrix[i - 2]).ljust(max_str_len)) # print row of the table - From c5520f32f8f0a4a3ed82fadff11d8f70bae4b7f3 Mon Sep 17 00:00:00 2001 From: Farrukh Nurmatov Date: Thu, 23 Jun 2022 20:05:03 +0500 Subject: [PATCH 4/8] first iteration finished --- Homework-2_Python-Data_Types.md | 74 ------------- README.md | 90 +++++++++++----- requirements.txt | Bin 0 -> 336 bytes rss_reader.py | 181 ++++++++++++++++++++++++++++++++ tests.py | 58 ++++++++++ 5 files changed, 302 insertions(+), 101 deletions(-) delete mode 100644 Homework-2_Python-Data_Types.md create mode 100644 requirements.txt create mode 100644 rss_reader.py create mode 100644 tests.py diff --git a/Homework-2_Python-Data_Types.md b/Homework-2_Python-Data_Types.md deleted file mode 100644 index 69c73afc..00000000 --- a/Homework-2_Python-Data_Types.md +++ /dev/null @@ -1,74 +0,0 @@ -# Python Practice - Session 2 - -### Task 2.1 -Write a Python program to calculate the length of a string without using the `len` function. - -### Task 2.2 -Write a Python program to count the number of characters (character frequency) in a string (ignore case of letters). -Examples: -``` -Input: 'Oh, it is python' -Output: {',': 1, ' ': 3, 'o': 2, 'h': 2, 'i': 2, 't': 2, 's': 1, 'p': 1, 'y': 1, 'n': 1} -``` - -### Task 2.3 -Write a Python program that accepts a comma separated sequence of words as input and prints the unique words in sorted form. -Examples: -``` -Input: ['red', 'white', 'black', 'red', 'green', 'black'] -Output: ['black', 'green', 'red', 'white', 'red'] -``` - -### Task 2.3 -Create a program that asks the user for a number and then prints out a list of all the [divisors](https://en.wikipedia.org/wiki/Divisor) of that number. -Examples: -``` -Input: 60 -Output: {1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60} -``` - -### Task 2.4 -Write a Python program to sort a dictionary by key. - -### Task 2.5 -Write a Python program to print all unique values of all dictionaries in a list. -Examples: -``` -Input: [{"V":"S001"}, {"V": "S002"}, {"VI": "S001"}, {"VI": "S005"}, {"VII":"S005"}, {"V":"S009"},{"VIII":"S007"}] -Output: {'S005', 'S002', 'S007', 'S001', 'S009'} -``` - -### Task 2.6 -Write a Python program to convert a given tuple of positive integers into an integer. -Examples: -``` -Input: (1, 2, 3, 4) -Output: 1234 -``` - - -### Task 2.7 -Write a program which makes a pretty print of a part of the multiplication table. -Examples: -``` -Input: -a = 2 -b = 4 -c = 3 -d = 7 - -Output: - 3 4 5 6 7 -2 6 8 10 12 14 -3 9 12 15 18 21 -4 12 16 20 24 28 -``` - -### Materials -* [Python Data Types](https://realpython.com/python-data-types/) -* [Python Data Structures](https://realpython.com/python-data-structures/) -* [Conditional Statements](https://realpython.com/python-conditional-statements/) -* [While loop](https://realpython.com/python-while-loop/) -* [For loop](https://realpython.com/python-for-loop/) -* [Operators](http://pythonicway.com/python-operators) - diff --git a/README.md b/README.md index c86d1e65..b43edc85 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,64 @@ -# How to create a PR with a homework task - -1. Create fork from the following repo: https://github.com/E-P-T/Homework. (Docs: https://docs.github.com/en/get-started/quickstart/fork-a-repo ) -2. Clone your forked repo in your local folder. -3. Create separate branches for each session.Example(`session_2`, `session_3` and so on) -4. Create folder with you First and Last name in you forked repo in the created session. -5. Add your task into created folder -6. Push finished session task in the appropriate branch in accordance with written above. - You should get the structure that looks something like that - -``` - Branch: Session_2 - DzmitryKolb - |___Task1.py - |___Task2.py - Branch: Session_3 - DzmitryKolb - |___Task1.py - |___Task2.py -``` - -7. When you finish your work on task you should create Pull request to the appropriate branch of the main repo https://github.com/E-P-T/Homework (Docs: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). -Please use the following instructions to prepare good description of the pull request: - - Pull request header should be: `Session - `. - Example: `Session 2 - Dzmitry Kolb` - - Pull request body: You should write here what tasks were implemented. - Example: `Finished: Task 1.2, Task 1.3, Task 1.6` +Welcome to rss_reader.py readme file! +Installization: +- copy your programm to your folder +- open your folder in terminal +- than install requerments: + py -m pip install -r requerments.txt +- after that you you can adress to script typing command in terminal: +....\you folder\py rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source + + +usage: rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source + +This program gets information from RSS-channel and returns in user friendly format. + +positional arguments: + source RSS link for your information + +options: + -h, --help show this help message and exit + -v, --version Print program version and exit. + --verbose Outputs verbose status messages. + --json Print result as JSON in stdout. + --limit LIMIT Limit news topics if this parameter provided. + + +source is a positional argument that you should always input to your program. + +If --limit parameter takes only integer numbers which provide program to return that number of news. +If that parameter not provided, program print all news from RSS-channel. + +If --json parameter not provided, program print to console news in format below. + +Feed:[title of the rss site where you get news] + +Title: [fact [1] title] +Date: [date of that fact[1]] +Link: [link to the fact[1]] +Description: [fact's [1] short summary] +...... +Title: [fact [limit] title] +Date: [date of that fact[limit]] +Link: [link to the fact[limit]] +Description: [fact's [limit] short summary] + + +Parameter --json print json string in format that described below. This parameter also takes effect from --limit parameter. + +"[title of the rss site where you get news]": [ + { + "Title": "[date of that fact[1]]", + "Link": "[link to the fact[1]]", + "Date": "[date of that fact[1]]", + "Description": "[fact's [1] short summary]" + }, + ..........., + { + "Title": "[date of that fact[limit]]", + "Link": "[link to the fact[limit]]", + "Date": "[date of that fact[limit]]", + "Description": "[fact's [limit] short summary]" + } + ] +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..bb334892b90f51b25b2301ee9041c90900a0c4c9 GIT binary patch literal 336 zcmX|7TMmLS5S(ulk0Q~6Mm{_Y#TrSfVgX}3ygEA-vss$#&TIL+Jpvj`7}4RxH)6nz zZ;uKGR)qo^mVK94z;mBBYtF3myroY1sdQpGZXK(@PN=Q;Eza~yR0%|{aHU(qg|o;6 zFR7TX@v^!TufvhpM#?33rKS;2a-*h}v=+;9CMw-|N1)Tlo;4lzf6RR8K<9u5dq$aq ZCFq&X`1!_(s!=&T{S9mX#p%9UdA@umF?#?2 literal 0 HcmV?d00001 diff --git a/rss_reader.py b/rss_reader.py new file mode 100644 index 00000000..a663abdd --- /dev/null +++ b/rss_reader.py @@ -0,0 +1,181 @@ +import requests +from lxml import etree +import xml.etree.ElementTree as elementTree +from bs4 import BeautifulSoup +import json +import logging as log +import argparse + + +def get_content(url: str): + """Get request from the url and depending on a flag returns corresponding xml etree or html soup""" + log.info(f"Try in to retrieve information from {url}") + try: + r = requests.get(url) + log.info(f"Connection to {url} established!") + if is_xml(r.content): + root = etree.fromstring(r.content) + log.info("XML content downloaded and parsed.") + return root[0] + else: + raise AttributeError() + except requests.exceptions.RequestException as exc: + print(f"Error Occurred: {exc}") + except AttributeError: + print("Site that you entered is not RSS, because it doesn't contain XML!") + + +def is_xml(value): + """Check value containing XML content""" + try: + elementTree.fromstring(value) + except elementTree.ParseError: + log.info("Received data isn't XML.") + return False + else: + log.info("Received data is XML content.") + return True + + +def get_feed(root): + """Return feed string""" + feed = root.find("title").text + return feed + + +def get_news_db(root, num=None): + """Get all "item" elements and return list or slice of the list from root if num specified.""" + + news_db = root.findall("item") + + if num is not None: + news_slice = news_db[:num] + log.info("Retrieved user defined number of news.") + return news_slice + else: + log.info("Retrieved all amount of news.") + return news_db + + +def inf_generator(news_db): + """Get information from the "item" list and yield it as a list + next format [title, link, date, description]""" + log.info("Retrieving information from item branches.") + for bunch in news_db: + title = bunch.find("title").text + link = link_handler(bunch.find("link")) + date = bunch.find("pubDate").text + description = description_handler(bunch.find("description")) + inf_lst = [title, link, date, description] + log.info("Branch retrieved successful!") + yield inf_lst + + +def link_handler(inf): + """Handles absence of link in RSS.""" + if inf is None: + log.info("RSS doesn't provide news link!") + return "Link field doesn't provided!" + else: + log.info("Link retrieved successful.") + link = inf.text + return link + + +def description_handler(inf): + """Handles absence of link in RSS and gets HTML from the string.""" + if inf is None: + log.info("RSS doesn't provide description!") + return "There is no description!" + else: + description = inf.text + log.info("Description retrieved successful.") + if "<" in description: + soup = BeautifulSoup(description, "html.parser") + data = soup.text + return data + else: + return description + + +def printer(feed, news_db): + """Printing in the console news in next format: + Feed:[title of the rss site where you get news] + + Title: [fact's title] + Date: [date of that fact] + Link: [link to the fact] + Description: [fact's short summary] + """"" + log.info("Printing information to console!") + print(f"Feed: {feed}", "\n") + gen = inf_generator(news_db) + for inf in gen: + print(f"Title: {inf[0]}") + print(f"Date: {inf[2]}") + print(f"Link: {inf[1]}") + print(f"Description: {inf[3]}", end="\n") + print('\n') + + +def news_to_json(feed, news_db): + """Collect information form the site into dict "news feed" and return it as a json string""" + log.info("Collection information to json format.") + news_feed = {feed: []} + gen = inf_generator(news_db) + for inf in gen: + fact_dict = {"Title": inf[0], + "Link": inf[1], + "Date": inf[2], + "Description": inf[3], + } + news_feed[feed].append(fact_dict) + log.info("Collecting JSON completed successful!") + return json.dumps(news_feed, ensure_ascii=False, indent=4) + + +def output_form(main_title, news_data, form): + """Printing return the output to console if form specified (console/json)""" + if form not in ["console", "json"]: + log.error("Entered unavailable format!") + raise AttributeError("Choose between console or json!") + elif form == "console": + printer(main_title, news_data) + elif form == "json": + news_json = news_to_json(main_title, news_data) + print(news_json) + + +def main(): + parser = argparse.ArgumentParser(description="This program gets information from RSS-channel " + "and returns in user friendly format.") + parser.add_argument("source", type=str, help="RSS link for your information") + parser.add_argument("-v", "--version", action="version", version="Version 1.0.0", + help="Print program version and exit.") + parser.add_argument("--verbose", action="store_true", help="Outputs verbose status messages.") + parser.add_argument("--json", action="store_true", help="Print result as JSON in stdout.") + parser.add_argument("--limit", type=int, help="Limit news topics if this parameter provided.") + + args = parser.parse_args() + rss_url = args.source + verbose = args.verbose + print_json = args.json + limit = args.limit + + if verbose: + log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) + log.info("Verbose output.") + else: + log.basicConfig(format="%(levelname)s: %(message)s") + + xml_content = get_content(rss_url) + feed = get_feed(xml_content) + news_db = get_news_db(xml_content, limit) + if print_json: + output_form(feed, news_db, "json") + else: + output_form(feed, news_db, "console") + + +if __name__ == "__main__": + main() diff --git a/tests.py b/tests.py new file mode 100644 index 00000000..873eac67 --- /dev/null +++ b/tests.py @@ -0,0 +1,58 @@ +import unittest +from unittest.mock import patch +from rss_reader.rss_reader import * + + +class MyTest(unittest.TestCase): + def test_is_xml(self): + r = requests.get("https://news.yahoo.com/rss/") + xml_content = r.content + self.assertFalse(is_xml(xml_content)) + self.assertTrue(is_xml(xml_content)) + + def test_is_xml_2(self): + s = requests.get("https://mail.ru") + c = s.content + self.assertTrue(is_xml(c)) + self.assertFalse(is_xml(c)) + + def test_get_content(self): + r = requests.get("https://news.yahoo.com/rss/") + xml_content = r.content + root = etree.fromstring(xml_content) + self.assertEqual(type(get_content("https://news.yahoo.com/rss/")), type(root[0])) + + @patch('rss_reader.get_content') + def test_for_get_content(self, mock_is_xml): + mock_is_xml.return_value = False + with self.assertRaises(Exception): + get_content("https://news.yahoo.com/rss/") + mock_is_xml.assert_called_once() + + def test_link_handler(self): + link_xml = "https://news.yahoo.com/gop-commission-refuses-certify-mexico-004011875.html" + root = etree.fromstring(link_xml) + link = "https://news.yahoo.com/gop-commission-refuses-certify-mexico-004011875.html" + self.assertEqual(link_handler(None), "Link field doesn't provided!") + self.assertEqual(link_handler(root), link) + + def test_description_handler(self): + description = '' \ + '

В МЧС рассказали об аварии, которая произошла 14 июня ' \ + 'около 17:30 на трассе М1 (Брест — Минск — граница России), вблизи деревни Рябиновка Дзержинского района. Mazda 626 столкнулась с микроавтобусом Mercedes Sprinter.' \ + '

Читать далее…

]]>
' + root = etree.fromstring(description) + clear_descr = " В МЧС рассказали об аварии, которая произошла 14 июня около 17:30 на трассе М1 (Брест — Минск — граница России), " \ + "вблизи деревни Рябиновка Дзержинского района. Mazda 626 столкнулась с микроавтобусом Mercedes Sprinter.Читать далее… " + self.assertEqual(description_handler(root), clear_descr) + self.assertEqual(description_handler(None), "There is no description!") + description_2 = "Graphic footage of the attack in China set off a heated debate that showed both " \ + "the growing awareness of women’s rights and how divisive feminism still remains." + clear_descr_2 = "Graphic footage of the attack in China set off a heated debate that showed both " \ + "the growing awareness of women’s rights and how divisive feminism still remains." + root_2 = etree.fromstring(description_2) + self.assertEqual(description_handler(root_2), clear_descr_2) + + +if __name__ == '__main__': + unittest.main(argv=[''], exit=False) \ No newline at end of file From c20b1c9d0a078532b8df78a5c99c23e7da59d7e5 Mon Sep 17 00:00:00 2001 From: Farrukh Nurmatov Date: Thu, 23 Jun 2022 20:13:33 +0500 Subject: [PATCH 5/8] iteration 2 complieted program released as a package --- README.md | 19 ++++++++++++-- rss_reader/__init__.py | 2 ++ rss_reader/__main__.py | 5 ++++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 206 bytes .../__pycache__/rss_reader.cpython-310.pyc | Bin 0 -> 5882 bytes rss_reader/__pycache__/tests.cpython-310.pyc | Bin 0 -> 3847 bytes rss_reader.py => rss_reader/rss_reader.py | 2 +- tests.py => rss_reader/tests.py | 0 setup.py | 24 ++++++++++++++++++ 9 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 rss_reader/__init__.py create mode 100644 rss_reader/__main__.py create mode 100644 rss_reader/__pycache__/__init__.cpython-310.pyc create mode 100644 rss_reader/__pycache__/rss_reader.cpython-310.pyc create mode 100644 rss_reader/__pycache__/tests.cpython-310.pyc rename rss_reader.py => rss_reader/rss_reader.py (99%) rename tests.py => rss_reader/tests.py (100%) create mode 100644 setup.py diff --git a/README.md b/README.md index b43edc85..71549526 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,27 @@ Welcome to rss_reader.py readme file! -Installization: + + +Installization. +First option: - copy your programm to your folder - open your folder in terminal - than install requerments: py -m pip install -r requerments.txt - after that you you can adress to script typing command in terminal: -....\you folder\py rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source +....\you folder\rss_reader\py rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source + +Second option: +- copy your package to your folder +- open your folder in terminal +- than install package: + py pip install setup.py + or + py pip install . +- after that you you can adress to script typing command in terminal: +....\you folder\rss_reader\py rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source +....\you folder\py rss_reader [-h] [-v] [--verbose] [--json] [--limit LIMIT] source +....\any folder\rss_reader [-h] [-v] [--verbose] [--json] [--limit LIMIT] source usage: rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source diff --git a/rss_reader/__init__.py b/rss_reader/__init__.py new file mode 100644 index 00000000..a8bf2082 --- /dev/null +++ b/rss_reader/__init__.py @@ -0,0 +1,2 @@ +from .rss_reader import * +from .tests import * \ No newline at end of file diff --git a/rss_reader/__main__.py b/rss_reader/__main__.py new file mode 100644 index 00000000..369e5c9b --- /dev/null +++ b/rss_reader/__main__.py @@ -0,0 +1,5 @@ +from rss_reader import main + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/rss_reader/__pycache__/__init__.cpython-310.pyc b/rss_reader/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b8adab8b0a58e686aea744d1c67b91d7278d8a5 GIT binary patch literal 206 zcmd1j<>g`kf;O%-N%}zgF^Gc@H>j5*!RX Fi~xfOG>iZM literal 0 HcmV?d00001 diff --git a/rss_reader/__pycache__/rss_reader.cpython-310.pyc b/rss_reader/__pycache__/rss_reader.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a103a187ff4c18739560c135afaea9741e996ee GIT binary patch literal 5882 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 JE7)j9iN#UuQzKuA8u$CcVno@d8ja_@-JN(o zw(q^Q6@yJNoz;pi4@0Tiv4dZV#$Uim)ucE015N>cYFzWQS0yDJgmP!3WP*^nH zG`P(NZW(+)7}xEsLVZ9Kd2!j2-@tf~4>nC+;)i}g66V#cRmy*dPGg~BrUPwPdo6U# zZ+kSCe+n49il$x%5k}qQM&05jw{98r0xxhIZ&>Cj7w10~X;ubdlUkd}Lsi?Smsh16CeXr!i0hp^`C3I5KIN8#z`-@SC<+7F>Y zU2}QpN7vq3^IEP9-;(il;c0aZ<{c?qF66Zp5v}CMn%t?j*U};CIX&Bhl!FpT*({oe z%~7*E+RIoq$MmJylLL@MHo8ap*$7=fsLD>IC`q#<&GHbDVIo6Bs0=FwIf7Ses?_wd z$a>@$O^)PDJ5$6bUjXSIf8H}^>KWe%)gl@pt1f^XL{=BJfh&+MWT+PPAUC)0)5Y}y zw_Zji-N`B)7#9{KsghJVmC}K5(gC5R5NWv~<4|eoN6kL8Z@kxWgVfe*ZIKQE`#ypd zI}2Ij+cV@5kkpoOtPci%Gz&f_N1>}eLS>Vg-Iwxt$Z>XG+(*BfoA)fJ=s(maZCQ#g zqr*}34i&knk(k@ZOhY3TTU7YwwtdGijUVCDxAEI5&?a!zm^9R>RjkwR$7tCnQCPT~ z<*PD~4ojgsGICbjpd;$Tz2g3X>nowsLvP&hL|glDBu~P4eYCggcy15~?!=KNDpr=L z`cN2q%X?*dFiXd?EHz^3$EXg+{#MELT*qbyg+4woMBAc#CkT)@?bA?-<}KnXnDT>NgUW9nsIv^8%5|Vg)EFq z(dZyC;~sVezp*wRiW|Nck55cYPfSkEo;zD@=`fh9Wg5HR`w!~up87t#WDQ>gobgzw z=oQV{GG6hy;HOcoFChu?1QAMXI!rD&EjQwUkbA{kFJ+V1r{pQ(v1xb^t792fd1(gT z3@95Q+2&F6xY<3C2P)6}3`(@AJVohk3^+FJFG6V_l^#m_sBwD(hsef2CS?QL;vSAg z;5hK~^Di!R{)%s9jtk{UKdadJxzq1ldi}M<*WO`s?fE&E&CmH^lPT$49P0zKw_R7` z2r&p*U0TZyh?;J7!lekCV{Q7J$ERoc?D^@L8GcT9vy*({{Ot6s@RrU@HMm#3-WJU< z<_7xW*chwL&(&Ns*lW)xzhFId=O@peIaeEF+Lib| zE{-`%fg3H4%_kowA18N6Bir3f?(V+7`+o8$xtn~7_UGgwOLmfb4E_69+e{uN55L2( zzO#E1jreb~|4pNF!KQRZ?Y$wA7Bm|ACoh^;U~lo(TBS~fq1eFTOtw}GWk;9zFsPZ+?(D!bx#xyxpzW*B*u#qbC& zZBdAr`8lRp@)=Y-g4_<}DC4@fy9LK~lF!%`Aw7X=VOQHY4m7aeL+v@p+>Va(1u``; zHC3CKsZE}(h2+J!(s8gHbplt7tMSlZma)5{+Y7lUo}O}py<6%~ZY?|L>O)>2TMA}x_!Xf#!{2}=TvI~`RT5_Do z4Ta;RWyc9)-U+Bb>^O8U?X5^sFJC0`RU&jJl#IyNiF}g?{oBY9x|qxU^;*$l$tann zVyRdxmhm1Q%vQ-dl?5ysmn&9k;{aMoZQN;erBp9)!~HK*az6 literal 0 HcmV?d00001 diff --git a/rss_reader.py b/rss_reader/rss_reader.py similarity index 99% rename from rss_reader.py rename to rss_reader/rss_reader.py index a663abdd..9c74c01e 100644 --- a/rss_reader.py +++ b/rss_reader/rss_reader.py @@ -150,7 +150,7 @@ def main(): parser = argparse.ArgumentParser(description="This program gets information from RSS-channel " "and returns in user friendly format.") parser.add_argument("source", type=str, help="RSS link for your information") - parser.add_argument("-v", "--version", action="version", version="Version 1.0.0", + parser.add_argument("-v", "--version", action="version", version="Version 1.2.0", help="Print program version and exit.") parser.add_argument("--verbose", action="store_true", help="Outputs verbose status messages.") parser.add_argument("--json", action="store_true", help="Print result as JSON in stdout.") diff --git a/tests.py b/rss_reader/tests.py similarity index 100% rename from tests.py rename to rss_reader/tests.py diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..b9f777b4 --- /dev/null +++ b/setup.py @@ -0,0 +1,24 @@ +from setuptools import setup, find_packages + +setup( + name="rss_reader", + version="1.2.0", + description="Simple RSS-reader for console use.", + author="Farrukh Nurmatov", + packages=find_packages(), + install_requires=["beautifulsoup4>=4.11.1", + "bs4>=0.0.1", + "certifi>=2022.6.15", + "charset-normalizer>=2.0.12", + "idna>=3.3", + "lxml>=4.9.0", + "requests>=2.28.0", + "soupsieve>=2.3.2.post1", + "urllib3>=1.26.9" + ], + python_requires=">=3.9", + entry_points={ + 'console_scripts': + ['rss_reader = ' + 'rss_reader.rss_reader:main', ]} + ) From afd3984e36cf99e4523e8d01de17379c887ceee5 Mon Sep 17 00:00:00 2001 From: Farrukh Nurmatov Date: Tue, 28 Jun 2022 17:46:04 +0500 Subject: [PATCH 6/8] complieted iteration 3 --- README.md | 103 ++++++++----- requirements.txt | Bin 336 -> 410 bytes rss_reader/__init__.py | 3 +- .../database_handler.cpython-310.pyc | Bin 0 -> 5425 bytes .../__pycache__/rss_reader.cpython-310.pyc | Bin 5882 -> 6264 bytes rss_reader/database_handler.py | 139 ++++++++++++++++++ rss_reader/dbh_tests.py | 7 + rss_reader/requirements.txt | Bin 0 -> 538 bytes rss_reader/rss_reader.py | 110 ++++++++++++-- rss_reader/tests.py | 57 ++++++- setup.py | 5 +- 11 files changed, 369 insertions(+), 55 deletions(-) create mode 100644 rss_reader/__pycache__/database_handler.cpython-310.pyc create mode 100644 rss_reader/database_handler.py create mode 100644 rss_reader/dbh_tests.py create mode 100644 rss_reader/requirements.txt diff --git a/README.md b/README.md index 71549526..6a1a7d00 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,79 @@ -Welcome to rss_reader.py readme file! + _ __ ___ ___ _ __ ___ __ _ __| | ___ _ __ +| '__|/ __|/ __| | '__| / _ \ / _` | / _` | / _ \| '__| +| | \__ \\__ \ | | | __/| (_| || (_| || __/| | +|_| |___/|___/ _____ |_| \___| \__,_| \__,_| \___||_| + |_____| +Welcome to rss_reader.py readme file! -Installization. -First option: -- copy your programm to your folder -- open your folder in terminal -- than install requerments: - py -m pip install -r requerments.txt -- after that you you can adress to script typing command in terminal: -....\you folder\rss_reader\py rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source +usage: rss_reader.py [-h] [--date DATE] [-v] [--verbose] [--json] + [--limit LIMIT] + [source] + +This program gets information from RSS-channel and returns in user friendly +format. + +positional arguments: + source RSS link for your information + +options: + -h, --help show this help message and exit + --date DATE Get news form the database by date. + -v, --version Print program version and exit. + --verbose Outputs verbose status messages. + --json Print result as JSON in stdout. + --limit LIMIT Limit news topics if this parameter provided. + + +source is a positional argument that you should input to your program, when you have to get information from the RSS-channel. -Second option: -- copy your package to your folder -- open your folder in terminal -- than install package: - py pip install setup.py - or - py pip install . -- after that you you can adress to script typing command in terminal: -....\you folder\rss_reader\py rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source -....\you folder\py rss_reader [-h] [-v] [--verbose] [--json] [--limit LIMIT] source -....\any folder\rss_reader [-h] [-v] [--verbose] [--json] [--limit LIMIT] source +If --limit parameter takes only integer numbers which provide program to return that number of news. +If that parameter not provided, program print all news from RSS-channel. +If --json parameter not provided, program print to console news in format below. -usage: rss_reader.py [-h] [-v] [--verbose] [--json] [--limit LIMIT] source +Feed:[title of the rss site where you get news] + +Title: [fact [1] title] +Date: [date of that fact[1]] +Link: [link to the fact[1]] +Description: [fact's [1] short summary] +...... +Title: [fact [limit] title] +Date: [date of that fact[limit]] +Link: [link to the fact[limit]] +Description: [fact's [limit] short summary] -This program gets information from RSS-channel and returns in user friendly format. -positional arguments: - source RSS link for your information +Parameter --json print json string in format that described below. This parameter also takes effect from --limit parameter. -options: - -h, --help show this help message and exit - -v, --version Print program version and exit. - --verbose Outputs verbose status messages. - --json Print result as JSON in stdout. - --limit LIMIT Limit news topics if this parameter provided. +"[title of the rss site where you get news]": [ + { + "Title": "[date of that fact[1]]", + "Link": "[link to the fact[1]]", + "Date": "[date of that fact[1]]", + "Description": "[fact's [1] short summary]" + }, + ..........., + { + "Title": "[date of that fact[limit]]", + "Link": "[link to the fact[limit]]", + "Date": "[date of that fact[limit]]", + "Description": "[fact's [limit] short summary]" + } + ] +} +--date - parameter to get information from the database. This parameter should take a date in `%Y%m%d` format. +For example: `--date 20191020` -source is a positional argument that you should always input to your program. +--date with source parameter returns data from database according to date and source link. -If --limit parameter takes only integer numbers which provide program to return that number of news. -If that parameter not provided, program print all news from RSS-channel. +Just --date parameter returns to console news in format described below: -If --json parameter not provided, program print to console news in format below. -Feed:[title of the rss site where you get news] +News on date [date]! Title: [fact [1] title] Date: [date of that fact[1]] @@ -59,9 +86,9 @@ Link: [link to the fact[limit]] Description: [fact's [limit] short summary] -Parameter --json print json string in format that described below. This parameter also takes effect from --limit parameter. +With --json parameter this parameter returns JSON string: -"[title of the rss site where you get news]": [ +"[date]": [ { "Title": "[date of that fact[1]]", "Link": "[link to the fact[1]]", @@ -76,4 +103,4 @@ Parameter --json print json string in format that described below. This paramete "Description": "[fact's [limit] short summary]" } ] -} +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index bb334892b90f51b25b2301ee9041c90900a0c4c9..1293898a715af24358b779058e079bfd2d691ebc 100644 GIT binary patch delta 72 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 literal 0 HcmV?d00001 diff --git a/rss_reader/__pycache__/rss_reader.cpython-310.pyc b/rss_reader/__pycache__/rss_reader.cpython-310.pyc index 7a103a187ff4c18739560c135afaea9741e996ee..20c647e90b3f15d98143be6f6a3fe7107587def0 100644 GIT binary patch delta 938 zcmY*XO-vI(6rQ);Znr1(Xp{Y>_pn?e`rV62n*|hG`6w4N;i)hn^ z7&P%@LM9py9w5fMq-Q-#qVeF3M9&^MipJELEhe&?_suuo``I^d_E+lXh_}+w;X?G8 zPQNQ`ckg&(@Np+uhTff5lL;-V#h##CMDNz(_c&b8Bijg&23gCLzWt*WAzY@(kCBOX zL4!R2R7W%DPLh-zG0C*oVVBeRJ=lf@Zwk$q4iA8Xyb%ZN9xZR`=T!ULehL1<4?NJnHi-1mi9P=$B z!?lT)n8w#RE8bSOWlxl?|E1F*f>jETfR<4fDYk`LM9vQ-WGx{V67q6Fp3TVf89AGf zYqfy9oRZg4vUcmAnp5mJLL1JB?5JjGO+OT?CSJFgYcXHm(63@#*}|5*!eoVS2dgDx z1rM@Z=lTZLRaPM9#=5zI4Qqq_)Y*mrV`nJbpjM4h5P%1Xj#L7!#kD*<58NLQ$AhOO zffadbz+~3mK?=@qu-Ke*T?bIeGxwlmIVu+eY?4vVm{RJ8q~i*#6uM{&a67aeDlrs-preC<}mkjG1LwQRe`l{ zPoM85jDBbq-t@CgnC(9*7>-e$xhCQjM?VlisKO<0Smvw4t4g84Hr*Av!T_o;1Q7Ox zqHsCWgMAo?RdEOnUr+2x-pV(G1L*lYE_o5DD~|?#4M3qU$pZaCZiF-lfd(oNkbYo_ z7G#rtR6oP^=yl*CK$=dplswOB?$F~w6Xxj$d1_P+PC!pF(ULI9CNvq0eKn_rY1#?T zfJ5(xO;{Se2*)9i;TaZ(EPWTb1*}Lkrz~>dP^|NU1)Rv`ko zOy5S|L!Lf}ZKPKC#yo?PrJ&5_vFsqW5d%E1#9^6!kCn`OjJh!fMCA4eGfJ3I4W!?= zI;-Pt%>mx^ng}eVlj}IL9fkfGc!+x)@$#bnM@AaJTI`n5R#sPvusbW*sRj zoeDB-ELE`Pmi_3u_DyW!1r{x<9jv#ycEfGAaD&SF33y0P>TB^$ejKsw>y>)Twk;@G zNqR>wCV9oOzuB(U>aCjR49_zO*XbwSnB&il$2joc_~(-|eiPiJhLM8|-7=1b10+JG L*wDzF3ew~c#(Rw> diff --git a/rss_reader/database_handler.py b/rss_reader/database_handler.py new file mode 100644 index 00000000..36969962 --- /dev/null +++ b/rss_reader/database_handler.py @@ -0,0 +1,139 @@ +import sqlite3 +import time +import os +import logging as log +import json + +data_base = "news_data.db" + +class DataBaseHandler: + + """Class that handles with sqlite3 database. With that class you can: + adress to your DB through context manager, create table, input data to table, + get data from table by date, souce, check emtyness of the table.""" + + def __init__(self, base_path): + log.info("Initialization of object complieted.") + self.path = base_path + + def __enter__(self): + log.info("Connection to database esteblished.") + self.conn = sqlite3.connect(self.path) + return self.conn + + def __exit__(self, exc_type, exc_val, exc_tb): + log.info("Connection closed") + if exc_type is None: + self.conn.close() + + def create_table(self): + + """Metod that help us to create table in our database if it doesn't exist. + Table contains next fields: date TEXT, + source TEXT, + title TEXT, + url TEXT, + full_date TEXT, + description TEXT""" + + with DataBaseHandler(self.path) as conn: + cursor = conn.cursor() + cursor.execute("""CREATE TABLE IF NOT EXISTS news_data ( + date TEXT, + source TEXT, + title TEXT, + url TEXT, + full_date TEXT, + description TEXT)""") + conn.commit() + log.info("Now news_data table exists in out database.") + + + def emptiness_checker(self): + + """This metod check our database and return boolean result. + If it is empty - return True, else return - False""" + + log.info("Cheking table for emptiness...") + with DataBaseHandler(self.path) as conn: + cursor = conn.cursor() + cursor.execute("""SELECT COUNT(*) FROM news_data""") + result = cursor.fetchone() + if result[0] == 0: + log.info("Table is empty!") + return True + else: + log.info("There is data in the table!") + return False + + def add_data(self, source, *args): + + """This method add information to news_data table, + also this method creates information to date field.""" + + converted_date = time.strptime(args[2], "%a, %d %b %Y %H:%M:%S %z") + date = time.strftime("%Y%m%d", converted_date) + with DataBaseHandler(self.path) as conn: + cursor = conn.cursor() + cursor.execute("""INSERT INTO news_data (date, source, title, url, full_date, description) + VALUES (?, ?, ?, ?, ?, ?)""", (date, source, args[0], args[1], args[2], args[3])) + conn.commit() + log.info("Data added to the news_data table.") + + def retrieve_data(self, date, source=None, num=None): + + """If num parameter specified, takes numered quantity of data, + from database especially by date, and by source if it specified.""" + + with DataBaseHandler(self.path) as conn: + cursor = conn.cursor() + if source is None: + cursor.execute("""SELECT title, url, full_date, description FROM news_data WHERE date=?""", (date,)) + else: + cursor.execute("""SELECT title, url, full_date, description + FROM news_data WHERE date=? AND source=?""", (date, source)) + conn.commit() + data = cursor.fetchall() + if len(data) == 0: + log.info("There is no such data in the table.") + raise ValueError + if num is None: + self.data = data + log.info("Provided amount of data retrieved from the table.") + else: + self.data = data[:num] + log.info("Provided amount of data retrieved from the table.") + return self.data + + def data_to_json(self, date): + + """Returns retrieved data from database in json format""" + + log.info("Collecting data in json!") + self.json_data = {date:[]} + for i in self.data: + fact_dict = {"Title": i[0], + "Link": i[1], + "Date": i[2], + "Description": i[3], + } + self.json_data[date].append(fact_dict) + return json.dumps(self.json_data, ensure_ascii=False, indent=4) + + + def data_to_print(self, date): + + """Prints retrieved data from database to console.""" + log.info("Printing data from database.") + print(f"News on {date}!\n") + for i in self.data: + print(f"Title: {i[0]}") + print(f"Date: {i[2]}") + print(f"Link: {i[1]}") + print(f"Description: {i[3]}", end="\n") + print('\n') + + + + + diff --git a/rss_reader/dbh_tests.py b/rss_reader/dbh_tests.py new file mode 100644 index 00000000..565ce9fe --- /dev/null +++ b/rss_reader/dbh_tests.py @@ -0,0 +1,7 @@ +import unittest +from database_handler import * + + +class MyTest(unittest.TestCase): + + diff --git a/rss_reader/requirements.txt b/rss_reader/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..ee51d552e3d1c95c9749bcbe0516d5f633bc7b3c GIT binary patch literal 538 zcmYL`QBK1^3`FM}i92us+OjQ(A|WAh0e<*KNt&ubn-F$WR5(2F>?J}gXE*kEJhp$n zYqhF$)TB;(?x?Ic?ok`vb2eJ&ieuk%ov9{XA}f4m#oxe_{#ZKC3`7HGp&LhCdw!z> z^%5*C=t~V$OX~4O6nr_0)wR6B9z3tp4s7MjYhr8nMxDrw?lq-b&oZ5(KH)#WK2T*x z<^*@?v*4}5Emrsw(Rbo4$|h{q5FE4eYNk5FX4nc&>-a(s*dFbX|2r-HJo@Vyv|%zn z!}p*D-cM+o=g{+)Q27e7@ybij$a>-auVX!B@=va;?8D|-l+8OcM5_;M)^D@2PDg53 WXJuPQxT8=4.9.0", "requests>=2.28.0", "soupsieve>=2.3.2.post1", - "urllib3>=1.26.9" + "urllib3>=1.26.9", + "python-dateutil>=2.8.2" ], python_requires=">=3.9", entry_points={ From d87d327129902b6181493c7d92353f634ffb3d4d Mon Sep 17 00:00:00 2001 From: Farrukh Nurmatov Date: Thu, 30 Jun 2022 17:14:49 +0500 Subject: [PATCH 7/8] finished iteration 4 added news conversion to html and epub --- README.md | 84 ++++++----- requirements.txt | Bin 410 -> 544 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 206 -> 0 bytes .../database_handler.cpython-310.pyc | Bin 5425 -> 0 bytes .../__pycache__/rss_reader.cpython-310.pyc | Bin 6264 -> 0 bytes rss_reader/__pycache__/tests.cpython-310.pyc | Bin 3847 -> 0 bytes rss_reader/database_handler.py | 39 ++++- rss_reader/dbh_tests.py | 16 ++ rss_reader/requirements.txt | Bin 538 -> 0 bytes rss_reader/rss_reader.py | 139 +++++++++++++++++- rss_reader/tests.py | 29 ++-- setup.py | 11 +- 12 files changed, 260 insertions(+), 58 deletions(-) delete mode 100644 rss_reader/__pycache__/__init__.cpython-310.pyc delete mode 100644 rss_reader/__pycache__/database_handler.cpython-310.pyc delete mode 100644 rss_reader/__pycache__/rss_reader.cpython-310.pyc delete mode 100644 rss_reader/__pycache__/tests.cpython-310.pyc delete mode 100644 rss_reader/requirements.txt diff --git a/README.md b/README.md index 6a1a7d00..ed2490b3 100644 --- a/README.md +++ b/README.md @@ -7,23 +7,23 @@ Welcome to rss_reader.py readme file! -usage: rss_reader.py [-h] [--date DATE] [-v] [--verbose] [--json] - [--limit LIMIT] - [source] - -This program gets information from RSS-channel and returns in user friendly -format. - -positional arguments: - source RSS link for your information - -options: - -h, --help show this help message and exit - --date DATE Get news form the database by date. - -v, --version Print program version and exit. - --verbose Outputs verbose status messages. - --json Print result as JSON in stdout. - --limit LIMIT Limit news topics if this parameter provided. +usage: rss_reader.py [-h] [--date DATE] [-v] [--verbose] [--to-html] [--to-epub] [--json] [--limit LIMIT] [source] + +This program gets information from RSS-channel and returns in user friendly format. + +positional arguments: + source RSS link for your information + +options: + -h, --help show this help message and exit + --date DATE Get news form the database by date. + -v, --version Print program version and exit. + --verbose Outputs verbose status messages. + --to-html Return HTML file to C:\rss_reader\html_files + --to-epub Return HTML file to C:\rss_reader\epub_files + --json Print result as JSON in stdout. + --limit LIMIT Limit news topics if this parameter provided. + source is a positional argument that you should input to your program, when you have to get information from the RSS-channel. @@ -31,22 +31,8 @@ source is a positional argument that you should input to your program, when you If --limit parameter takes only integer numbers which provide program to return that number of news. If that parameter not provided, program print all news from RSS-channel. -If --json parameter not provided, program print to console news in format below. -Feed:[title of the rss site where you get news] - -Title: [fact [1] title] -Date: [date of that fact[1]] -Link: [link to the fact[1]] -Description: [fact's [1] short summary] -...... -Title: [fact [limit] title] -Date: [date of that fact[limit]] -Link: [link to the fact[limit]] -Description: [fact's [limit] short summary] - - -Parameter --json print json string in format that described below. This parameter also takes effect from --limit parameter. +--json is a parameter that print json string in format that described below. This parameter also takes effect from --limit parameter. "[title of the rss site where you get news]": [ { @@ -65,13 +51,31 @@ Parameter --json print json string in format that described below. This paramete ] } ---date - parameter to get information from the database. This parameter should take a date in `%Y%m%d` format. -For example: `--date 20191020` +If --json parameter not provided, program print to console news in format below. ---date with source parameter returns data from database according to date and source link. +Feed:[title of the rss site where you get news] + +Title: [fact [1] title] +Date: [date of that fact[1]] +Link: [link to the fact[1]] +Description: [fact's [1] short summary] +...... +Title: [fact [limit] title] +Date: [date of that fact[limit]] +Link: [link to the fact[limit]] +Description: [fact's [limit] short summary] -Just --date parameter returns to console news in format described below: +--to-html is parameter that saves information to C:\rss_reader\html_files folder with given name in date time format `%d%m%y%H%M`. +--to-epub is parameter that saves information to C:\rss_reader\epub_files folder with given name in date time format `%d%m%y%H%M`. + +--to-epub and --to-html also works with other parameters. + +--date is a parameter to get information from the database. This parameter should take a date in `%Y%m%d` format. +For example: `--date 20191020` +With --to-epub or --to-html parameter program saves html or epub files to corresponding folders with `%Y%m%d` nameformat. +--date with source parameter returns data from database according to date and source link. +Just --date parameter returns to console news in format described below: News on date [date]! @@ -86,7 +90,7 @@ Link: [link to the fact[limit]] Description: [fact's [limit] short summary] -With --json parameter this parameter returns JSON string: +With --json parameter returns JSON string: "[date]": [ { @@ -103,4 +107,8 @@ With --json parameter this parameter returns JSON string: "Description": "[fact's [limit] short summary]" } ] -} \ No newline at end of file +} + +Running unittests for rss_reader: +py -m unittest rss_reader/dbh_tests.py +py -m unittest rss_reader/tests.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 1293898a715af24358b779058e079bfd2d691ebc..e848f78c969ee03e19c88f00db1e6d07036fd005 100644 GIT binary patch delta 160 zcmbQmyntoGs$f@!B!+w-%x3Um$ON)&fzW_KkHL_^98B^uZ~?_r81fhr!E(kxQDX*> zs25N?k0A>vZUmPx1j-ss+%0eK3luH_8d}Owzz_^%r!l006&ZmI1sM-AA^>Pa4iM%u Ol!Ik0VX_mi9t8k~jT;02 delta 17 ZcmZ3$GK+b_s)^^sCNnWgO-^7u002AE22}t6 diff --git a/rss_reader/__pycache__/__init__.cpython-310.pyc b/rss_reader/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 2b8adab8b0a58e686aea744d1c67b91d7278d8a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 zcmd1j<>g`kf;O%-N%}zgF^Gc@H>j5*!RX Fi~xfOG>iZM diff --git a/rss_reader/__pycache__/database_handler.cpython-310.pyc b/rss_reader/__pycache__/database_handler.cpython-310.pyc deleted file mode 100644 index a9402ccd454748560040b1c75136d2a81af30380..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5425 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 diff --git a/rss_reader/__pycache__/rss_reader.cpython-310.pyc b/rss_reader/__pycache__/rss_reader.cpython-310.pyc deleted file mode 100644 index 20c647e90b3f15d98143be6f6a3fe7107587def0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6264 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 diff --git a/rss_reader/database_handler.py b/rss_reader/database_handler.py index 36969962..bc0012e3 100644 --- a/rss_reader/database_handler.py +++ b/rss_reader/database_handler.py @@ -118,12 +118,13 @@ def data_to_json(self, date): "Description": i[3], } self.json_data[date].append(fact_dict) + log.info("Collecting data to json complieted!") return json.dumps(self.json_data, ensure_ascii=False, indent=4) - def data_to_print(self, date): """Prints retrieved data from database to console.""" + log.info("Printing data from database.") print(f"News on {date}!\n") for i in self.data: @@ -133,6 +134,42 @@ def data_to_print(self, date): print(f"Description: {i[3]}", end="\n") print('\n') + def data_to_html(self, date): + + """Metod that costract HTML text from data retrieved from database. + Takes input date as a title and header.""" + + header = f""" + + + + {date} + + """ + + end = """ + """ + + log.info("Stating construction HTML file!") + self.html_text = header + + h1 = f"""

News on: {date}

""" + self.html_text += h1 + for inf in self.data: + p = f"""

+ Title: {inf[0]}
+ Date: {inf[2]}
+ Link: clickable link
+ Description: {inf[3]}
+


+

""" + self.html_text += p + self.html_text += end + log.info("Construction HTML file complieted!") + return self.html_text + + + diff --git a/rss_reader/dbh_tests.py b/rss_reader/dbh_tests.py index 565ce9fe..39234bc3 100644 --- a/rss_reader/dbh_tests.py +++ b/rss_reader/dbh_tests.py @@ -1,7 +1,23 @@ import unittest from database_handler import * +from unittest.mock import patch, Mock class MyTest(unittest.TestCase): + @patch("database_handler.DataBaseHandler") + def test_create_table(self, mock_DataBase): + m = mock_DataBase + m.create_table() + m.create_table.assert_called() + @patch("database_handler.DataBaseHandler") + def test_emptiness_checker(self, mock_DataBase): + m = mock_DataBase + m.emptiness_checker() + m.emptiness_checker.assert_called() + + + +if __name__ == '__main__': + unittest.main(argv=[''], exit=False) \ No newline at end of file diff --git a/rss_reader/requirements.txt b/rss_reader/requirements.txt deleted file mode 100644 index ee51d552e3d1c95c9749bcbe0516d5f633bc7b3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 zcmYL`QBK1^3`FM}i92us+OjQ(A|WAh0e<*KNt&ubn-F$WR5(2F>?J}gXE*kEJhp$n zYqhF$)TB;(?x?Ic?ok`vb2eJ&ieuk%ov9{XA}f4m#oxe_{#ZKC3`7HGp&LhCdw!z> z^%5*C=t~V$OX~4O6nr_0)wR6B9z3tp4s7MjYhr8nMxDrw?lq-b&oZ5(KH)#WK2T*x z<^*@?v*4}5Emrsw(Rbo4$|h{q5FE4eYNk5FX4nc&>-a(s*dFbX|2r-HJo@Vyv|%zn z!}p*D-cM+o=g{+)Q27e7@ybij$a>-auVX!B@=va;?8D|-l+8OcM5_;M)^D@2PDg53 WXJuPQxT8 + + + + {head} + + """ + + end = """ + """ + + html_text = header + + h1 = f"""

Feed: {nw_title}

""" + html_text += h1 + gen = inf_generator(db) + for inf in gen: + p = f"""

+ Title: {inf[0]}
+ Link: clickable link
+ Date: {inf[2]}
+ Description: {inf[3]}
+


+

""" + html_text += p + html_text += end + log.info("HTML text creation complieted!") + return html_text + +def write_html_file(path, html_text, html_name=None): + + """Write HTML text to file, than saves file to the path, + also this programm automatically gives name to files.""" + + log.info("Starting writing HTML to the file!") + if html_name is None: + html_name = datetime.now().strftime("%d%m%y%H%M") + ".html" + log.info("Created name for HTML file!") + if not html_name.endswith(".html"): + html_name = html_name + ".html" + log.info("Added .html extention to file name!") + full_path = os.path.join(path, html_name) + with open(full_path, "w", encoding="utf-8") as file: + file.write(html_text) + log.info("HTML file created!") + print(f"HTML file saved to to path: {full_path}") + + +def write_epub_file(path, html_text, epub_name=None): + + """Write ePub file, than saves file to the path, + also this programm automatically gives name to files.""" + + log.info("Starting writing HTML to the ePub file!") + if epub_name is None: + epub_name = datetime.now().strftime("%d%m%y%H%M") + ".epub" + log.info("Created name for ePub file!") + if not epub_name.endswith(".epub"): + epub_name = epub_name + ".epub" + log.info("Added .epub extention to file name!") + file_path = os.path.join(path, epub_name) + book = epub.EpubBook() + book.add_author('Nurmatov Farrukh') # you found easter egg + c1 = epub.EpubHtml(title='News', file_name='chap_01.xhtml') + c1.content = html_text + book.add_item(c1) + book.add_item(epub.EpubNcx()) + book.add_item(epub.EpubNav()) + style = 'BODY {color: white;}' + nav_css = epub.EpubItem(uid="style_nav", file_name="style/nav.css", media_type="text/css", content=style) + book.add_item(nav_css) + book.spine = ['nav', c1] + epub.write_epub(file_path, book, {}) + log.info("ePub file created!") + print(f"Epub file saved to: {file_path}") def main(): @@ -204,9 +293,13 @@ def main(): "and returns in user friendly format.") parser.add_argument("source", type=str, nargs="?", help="RSS link for your information", default=None) parser.add_argument("--date", type=str, help="Get news form the database by date.") - parser.add_argument("-v", "--version", action="version", version="Version 1.3.0", + parser.add_argument("-v", "--version", action="version", version="Version 1.4.0", help="Print program version and exit.") parser.add_argument("--verbose", action="store_true", help="Outputs verbose status messages.") + parser.add_argument("--to-html", action="store_true", help="Return HTML file to C:\\rss_reader\\html_files", + dest="to_html") + parser.add_argument("--to-epub", action="store_true", help="Return HTML file to C:\\rss_reader\\epub_files", + dest="to_epub") parser.add_argument("--json", action="store_true", help="Print result as JSON in stdout.") parser.add_argument("--limit", type=int, help="Limit news topics if this parameter provided.") @@ -214,12 +307,16 @@ def main(): rss_url = args.source date = args.date verbose = args.verbose + html = args.to_html + epub = args.to_epub print_json = args.json limit = args.limit parent_dir = "C:/" root_dir = "rss_reader" db_dir = "data_base" + html_dir = "html_files" + epub_dir = "epub_files" if verbose: log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG) @@ -231,6 +328,10 @@ def main(): create_path(sys_path) db_path = os.path.join(sys_path, db_dir) create_path(db_path) + html_path = os.path.join(sys_path, html_dir) + create_path(html_path) + epub_path = os.path.join(sys_path, epub_dir) + create_path(epub_path) db = os.path.join(db_path, handler.data_base) base = handler.DataBaseHandler(db) base.create_table() @@ -240,8 +341,21 @@ def main(): if base.emptiness_checker(): raise AttributeError base_inf = base.retrieve_data(date, rss_url, limit) - if print_json: + html_data = base.data_to_html(date) + if print_json and html: print(base.data_to_json(date)) + write_html_file(html_path, html_data, date) + elif print_json and epub: + print(base.data_to_json(date)) + write_epub_file(epub_path, html_data, date) + elif print_json: + print(base.data_to_json(date)) + elif html: + write_html_file(html_path, html_data, date) + base.data_to_print(date) + elif epub: + write_epub_file(epub_path, html_data, date) + base.data_to_print(date) else: base.data_to_print(date) else: @@ -249,12 +363,25 @@ def main(): feed = get_feed(xml_content) news_db = get_news_db(xml_content, limit) sql_gen = inf_generator(news_db) + html_inf = html_adder(feed, news_db) for inf in sql_gen: base.add_data(rss_url, *inf) - if print_json: + if print_json and html: + output_form(feed, news_db, "json") + write_html_file(html_path, html_inf) + elif print_json and epub: + output_form(feed, news_db, "json") + write_epub_file(epub_path, html_inf) + elif print_json: output_form(feed, news_db, "json") + elif html: + write_html_file(html_path, html_inf) + output_form(feed, news_db, "console") + elif epub: + write_epub_file(epub_path, html_inf) + output_form(feed, news_db, "console") else: output_form(feed, news_db, "console") except ValueError: diff --git a/rss_reader/tests.py b/rss_reader/tests.py index f96d932f..b1e62e1b 100644 --- a/rss_reader/tests.py +++ b/rss_reader/tests.py @@ -1,5 +1,5 @@ import unittest -from unittest.mock import patch, Mock +from unittest.mock import patch, Mock, MagicMock import rss_reader from rss_reader import * import lxml @@ -85,25 +85,34 @@ def test_description_handler(self): self.assertEqual(description_handler(inf_2), "Test information") @patch("os.path.exists") - def test_create_path(self, mock_exist): - mock_exist.return_value = True + @patch("os.mkdir") + def test_create_path(self, mock_exist, mock_mkdir): + mock_exist.return_value = False path = os.path.join("C:/", "rss_reader") create_path(path) - mock_exist.assert_called_once_with(path) + # mock_exist.assert_called_once_with(path) + mock_mkdir.assert_called() @patch("rss_reader.printer") @patch("rss_reader.news_to_json") - def test_uotput_form(self, mock_printer, mock_news_to_json): + def test_output_form(self, mock_printer, mock_news_to_json): title = Mock() news = Mock() - self.assertEqual(uotput_form(title, news, "main"), "Choose between console or json!") - uotput_form(title, news, "json") + with self.assertRaises(AttributeError) as context: + output_form(title, news, "main") + self.assertTrue("Choose between console or json!" in str(context.exception)) + output_form(title, news, "json") mock_printer.assert_called_with(title, news) - uotput_form(title, news, "console") + output_form(title, news, "console") mock_news_to_json.assert_called_with(title, news) - - + @patch("rss_reader.inf_generator") + def test_html_adder(self, mock_generator): + title = Mock() + data = Mock() + mock_generator.return_value = [["a", "b", "s", "d"], + ["t", "h", "s", "d"]] + self.assertIsInstance(html_adder(title, data), str) diff --git a/setup.py b/setup.py index 127ace9f..eff4cd4f 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="rss_reader", - version="1.3.0", + version="1.4.0", description="Simple RSS-reader for console use.", author="Farrukh Nurmatov", packages=find_packages(), @@ -10,12 +10,17 @@ "bs4>=0.0.1", "certifi>=2022.6.15", "charset-normalizer>=2.0.12", + "EbookLib>=0.17.1", "idna>=3.3", + "Jinja2>=3.1.2", "lxml>=4.9.0", + "MarkupSafe>=2.1.1", + "Pillow>=9.1.1", + "python-dateutil>=2.8.2", "requests>=2.28.0", + "six>=1.16.0", "soupsieve>=2.3.2.post1", - "urllib3>=1.26.9", - "python-dateutil>=2.8.2" + "urllib3>=1.26.9" ], python_requires=">=3.9", entry_points={ From c88b46a9be1d65bb6afb0a246aff684ed5074259 Mon Sep 17 00:00:00 2001 From: Farrukh Nurmatov Date: Thu, 30 Jun 2022 17:29:11 +0500 Subject: [PATCH 8/8] system info added in README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ed2490b3..67964c8b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Welcome to rss_reader.py readme file! +Tested on Windows 10! + usage: rss_reader.py [-h] [--date DATE] [-v] [--verbose] [--to-html] [--to-epub] [--json] [--limit LIMIT] [source] This program gets information from RSS-channel and returns in user friendly format.