Compare commits

...

12 Commits
master ... main

4 changed files with 1012 additions and 357 deletions

46
data.py
View File

@ -29,7 +29,7 @@ class LinkDataRecord(NamedTuple):
def is_well_formed_line(line: str) -> bool:
"""Checks if current line is valid or not, returns true or false respectively."""
pipe_count = (
4 ## A PROPERLY FORMATED LINE IN linkulator.data HAS EXACTLY FOUR PIPES.
4 # A PROPERLY FORMATED LINE IN linkulator.data HAS EXACTLY FOUR PIPES.
)
return line.count("|") == pipe_count
@ -139,9 +139,9 @@ class LinkData:
"""sort link_data by creation date"""
self.link_data.sort(key=lambda x: x[2], reverse=True)
def add(self, record) -> int:
def add(self, record):
"""Add a record to the data file, and to link_data. Returns a new post
ID, if record is a post, or -1"""
ID, if record is a post, or None"""
if os.path.exists(config.USER.datafile):
append_write = "a" # append if already exists
else:
@ -157,7 +157,7 @@ class LinkData:
)
)
new_post_id = -1
new_post_id = None
if record.category:
if self.link_data:
new_post_id = (
@ -175,6 +175,7 @@ class LinkData:
def generate_category_data(self):
"""generate categories list and category count from sorted link data"""
# TODO: add unread status bool to this query's results
self.categories.clear()
for record in self.link_data:
name = record[4]
@ -238,14 +239,16 @@ class LinkData:
return sorted(search_results, key=lambda x: x[0], reverse=True)
def list_category_details(self, selected_category: str) -> list:
"""returns a sorted list of posts belonging to the specified category"""
def get_links_by_category_name(self, category_name: str) -> list:
"""accepts a category name. returns a sorted list of posts belonging to
the specified category"""
links = []
for record in self.link_data:
category = record[4]
if category == selected_category:
postid = record[0]
if category == category_name:
post_id = record[0]
userid = record[1]
timestamp = record[2]
parent_id = userid + "+" + str(timestamp)
@ -269,7 +272,7 @@ class LinkData:
links.append(
{
"postid": postid,
"post_id": post_id,
"link_timestamp": timestamp,
"link_author": userid,
"reply_count": len(replies),
@ -278,5 +281,28 @@ class LinkData:
"last_modified_timestamp": last_modified_timestamp,
}
)
links.sort(key=lambda x: x["last_modified_timestamp"], reverse=True)
return links
return sorted(links, key=lambda x: x["last_modified_timestamp"], reverse=True)
def get_post(self, post_id) -> dict:
output = {}
for record in self.link_data:
if record[0] == post_id:
output["parent_id"] = "{}+{}".format(record[1], str(record[2]))
output["author"] = record[1]
output["timestamp"] = record[2]
output["category"] = record[4]
output["url"] = record[5]
output["title"] = record[6]
break
if not output["parent_id"]:
raise ValueError("Sorry, no thread found with that ID.")
# TODO: this should return just the required fields
output["replies"] = sorted(
[record for record in self.link_data if record[3] == output["parent_id"]],
key=lambda x: x[2],
)
return output

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ class TestDataHelperFunctions(unittest.TestCase):
self.assertEqual(data.wash_line(line["Test"]), line["Result"])
def test_is_well_formed_line(self):
""" tests the data.is_well_formed_line function"""
"""tests the data.is_well_formed_line function"""
teststrings = [
{"Test": "A line of text", "Result": False},
{"Test": "1 Pipe |", "Result": False},

View File

@ -2,38 +2,237 @@
"""Tests for Linkulator views"""
import unittest
from unittest.mock import patch, call
import linkulator
# TODO: update to support list of strings output
# class TestViewSearchResults(unittest.TestCase):
# """Tests covering the view_search_results function"""
class TestPrintSearchResults(unittest.TestCase):
"""Tests covering the print_search_results function"""
# @patch("builtins.print")
# def test_print_search_results(self, mock_print):
# """tests that the search results are produced correctly"""
# test_keyword = "keyword"
# test_search_results = [
# (66, "keyword", "1576461366.5580268", "", "c", "c", "c"),
# (65, "poster6", "1576461367.5580268", "", "keyword", "c", "c"),
# (64, "poster7", "1576461368.5580268", "", "c", "keyword", "c"),
# (63, "poster8", "1576461369.5580268", "", "c", "c", "keyword"),
# ]
# expected_output = [
# call(
# "\nShowing results for keyword\n\n ID# DATE AUTHOR DESC "
# ),
# call(" 1 2019-12-16 keyword c "),
# call(" 2 2019-12-16 poster6 c "),
# call(" 3 2019-12-16 poster7 c "),
# call(" 4 2019-12-16 poster8 keyword "),
# call(""),
# ]
#
# linkulator.view_search_results(test_keyword, test_search_results)
#
# self.assertEqual(
# mock_print.call_count, 6
# ) # one count for title, 4 for the items and a blank line for formatting
#
# self.assertListEqual(test_view_calls, mock_print.call_args_list)
@patch("builtins.print")
def test_print_search_results(self, mock_print):
"""tests that the search results are printed correctly"""
test_keyword = "keyword"
test_search_results = [
(66, "keyword", "1576461366.5580268", "", "c", "c", "c"),
(65, "poster6", "1576461367.5580268", "", "keyword", "c", "c"),
(64, "poster7", "1576461368.5580268", "", "c", "keyword", "c"),
(63, "poster8", "1576461369.5580268", "", "c", "c", "keyword"),
class TestViewCategories(unittest.TestCase):
def test_view_categories(self):
"""Test general output of view_categories"""
categories = [
{
"name": "category 1",
"count": 1,
"last_updated": "10",
},
{
"name": "category 2",
"count": 2,
"last_updated": "100",
},
{
"name": "long category name that will be truncated because it's a long line, longer than the terminal width. that's for sure.",
"count": 20,
"last_updated": "1000",
},
]
test_print_calls = [
call(
"\nShowing results for keyword\n\n ID# DATE AUTHOR DESC "
),
call(" 1 2019-12-16 keyword c "),
call(" 2 2019-12-16 poster6 c "),
call(" 3 2019-12-16 poster7 c "),
call(" 4 2019-12-16 poster8 keyword "),
call(""),
cols = 80
expected_header = " ID# New Category"
expected_content = [
" 1 * category 1 (1)",
" 2 * category 2 (2)",
" 3 * long category name that will be truncated because it's a long... (20)",
]
linkulator.print_search_results(test_keyword, test_search_results)
actual_header, actual_content = linkulator.view_categories(categories, cols)
self.assertEqual(
mock_print.call_count, 6
) # one count for title, 4 for the items and a blank line for formatting
# confirm expected is equal to actual
self.assertEqual(expected_header, actual_header)
self.assertListEqual(expected_content, actual_content)
self.assertListEqual(test_print_calls, mock_print.call_args_list)
# confirm actual does not exceed cols
header_max_cols = max([len(line) for line in actual_header])
self.assertTrue(header_max_cols <= cols)
content_max_cols = max([len(line) for line in actual_content])
self.assertTrue(content_max_cols <= cols)
def test_empty_categories(self):
"""Test output when no categories data"""
empty_categories = []
cols = 80
expected_header = ""
expected_content = [
"",
"There are no posts yet - enter p to post a new link",
]
actual_header, actual_content = linkulator.view_categories(
empty_categories, cols
)
# confirm expected is equal to actual
self.assertEqual(expected_header, actual_header)
self.assertListEqual(expected_content, actual_content)
# confirm actual does not exceed cols
content_max_cols = max([len(line) for line in actual_content])
self.assertTrue(content_max_cols <= cols)
class TestViewLinks(unittest.TestCase):
def test_view_links(self):
"""Test general output of view_links"""
links = [
{
"post_id": 1,
"link_timestamp": "1627549445.044661",
"link_author": "auth 1",
"reply_count": 0,
"description": "description 1",
"has_new_replies": False,
"last_modified_timestamp": "1627549445.044661",
},
{
"post_id": 2,
"link_timestamp": "1627549445.044661",
"link_author": "author 2 with a long name",
"reply_count": 250,
"description": "a long description for the second post that should wrap i guess",
"has_new_replies": True,
"last_modified_timestamp": "1627549445.044661",
},
]
cols = 80
category_name = "Test Name"
expected_header = " Test Name\n ID# Date Author #Repl Description"
expected_content = [
" 1 2021-07-29 auth 1 [ 0] description 1",
" 2 2021-07-29 author 2 [ 25] a long description for the second post...*",
]
actual_header, actual_content = linkulator.view_links(
links, category_name, cols
)
# confirm expected is equal to actual
self.assertEqual(expected_header, actual_header)
self.assertListEqual(expected_content, actual_content)
# confirm actual does not exceed cols
header_max_cols = max([len(line) for line in actual_header])
self.assertTrue(header_max_cols <= cols)
content_max_cols = max([len(line) for line in actual_content])
self.assertTrue(content_max_cols <= cols)
class TestViewPost(unittest.TestCase):
def test_post_without_reply(self):
"""Test view_post where the post has no reply"""
post = {
"author": "post author 1",
"category": "test category 1",
"timestamp": "100",
"parent_id": "author+timestamp",
"replies": [],
"title": "A cool website",
"url": "http://asdflkjasdf",
}
cols = 80
expected_content = [
" Title : A cool website",
" Link : http://asdflkjasdf",
" Category : test category 1",
" User : post author 1",
" Date : Thu 01 Jan 1970 10:01:40",
"\n No replies yet. Be the first!",
]
actual_content = linkulator.view_post(post, cols)
# confirm expected is equal to actual
self.assertListEqual(actual_content, expected_content)
# confirm actual does not exceed cols
content_max_cols = max([len(line) for line in actual_content])
self.assertTrue(content_max_cols <= cols)
def test_post_with_reply(self):
"""Test view_post where the post has a reply"""
post = {
"author": "author2",
"category": "category 2",
"timestamp": "1000",
"parent_id": "xxxxxxxxx",
"replies": [
[
"",
"reply author 1",
"1001",
"",
"",
"",
"a reply",
],
[
"",
"reply author 2 with a long long long name, a very long name",
"1002",
"",
"",
"",
"a reply with a lot of words in it, too many to read, not going to read this",
],
],
"title": "Website 2",
"url": "asdflkjasdf",
}
cols = 80
expected_content = [
" Title : Website 2",
" Link : asdflkjasdf",
" Category : category 2",
" User : author2",
" Date : Thu 01 Jan 1970 10:16:40",
"\n Replies:\n",
" 1970-01-01 10:16 reply author 1: a reply",
" 1970-01-01 10:16 reply author 2 with a long long long name, a very long name: a reply with a lot of words in it, too many to read, not going to read this",
]
actual_content = linkulator.view_post(post, cols)
# confirm expected is equal to actual
self.assertListEqual(actual_content, expected_content)
# confirm actual does not exceed cols
content_max_cols = max([len(line) for line in actual_content])
self.assertTrue(content_max_cols <= cols)