The task: user-generated news feed:
User select what data type he wants to add
Provide record type required data
Record is published on text file in special format
Types of data:
News – text and city as input. Date is calculated during publishing.
Private ad – text and expiration date as input. Day left is calculated during publishing.
Your unique one with unique publish rules.
Expected result:
News -------------------------
Something happened
London, 03/01/2021 13.45
------------------------------
News -------------------------
Something other happened
Minsk, 24/01/2021 20.33
------------------------------
Private Ad ------------------
I want to sell a bike
Actual until: 01/03/2021, 21 days left
------------------------------
Joke of the day ------------
Did you hear about the claustrophobic astronaut?
He just needed a little space
Funny meter – three of ten
------------------------------
My code:
from datetime import datetime, date
from sys import exit
class Article:
def __init__(self, title, text, line_width):
self.title = title
self.text = text
self.line_width = line_width
@staticmethod
def publish_article(formatted_text):
with open("all_news.txt", "a") as file:
file.write(formatted_text)
class News(Article):
def __init__(self, title, text, city, date, line_width):
Article.__init__(self, title, text, line_width)
self.city = city
self.date = date
def format_text(self):
return f"{self.title}{(self.line_width - len(self.title)) * '-'}\n"\
f"{self.text} \n"\
f"{self.city}, {self.date.strftime('%d/%m/%Y %H:%M:%S')} \n"\
f"{'-'*self.line_width}\n\n\n"
class Ad(Article):
def __init__(self, title, text, end_date, line_width):
Article.__init__(self, title, text, line_width)
self.end_date = end_date
def format_text(self):
day, month, year = map(int, self.end_date.split('/'))
days_left = (date(year, month, day) - date.today()).days
return f"{self.title}{(self.line_width - len(self.title)) * '-'}\n"\
f"{self.text} \n"\
f"Actual until: {date(year, month, day).strftime('%d/%m/%Y')}, {days_left} days left \n"\
f"{'-'*self.line_width}\n\n\n"
class PersonalNews(Article):
def __init__(self, title, text, line_width):
Article.__init__(self, title, text, line_width)
def format_text(self):
return f"{self.title}{(self.line_width - len(self.title)) * '-'}\n"\
f"{self.text} \n"\
f"{'-'*self.line_width}\n\n\n"
while True:
user_input = input('Enter a number: '
'1 - Publish news;\n'
'2 - Publish ad; \n'
'3 - Publish personal news\n'
'q - Exit\n')
if user_input == "1":
new_news = News("News",
input('Print your text\n'),
input('Print city for the news\n'), datetime.now(), 30)
new_news.publish_article(new_news.format_text())
elif user_input == "2":
new_news = Ad("Private Ad",
input("Input your text\n"),
input('Print endDate of ad in format DD/MM/YEAR\n'), 30)
new_news.publish_article(new_news.format_text())
elif user_input == "3":
new_news = PersonalNews(input("Input your title\n"),
input("Input your text\n"), 30)
new_news.publish_article(new_news.format_text())
elif user_input == "q":
exit(0)
else:
print("Incorrect input. Please enter a number (1, 2, 3) or 'q' for exit")
1 Answer 1
Shadowing
Your date
parameter to the constructor of News
is poorly-named, because it shadows the built-in date
that you've imported from datetime
.
Super
Your call to Article.__init__()
should use super()
instead.
Data classes
Article
can just be a @dataclass
with its explicit __init__
removed.
Static methods
publish_article
doesn't make sense as a static method. In your invocations, you're always calling format_text()
on a child instance, then passing that to a static method on the parent. Instead:
- Define
format_text(self) -> str: raise NotImplementedError()
onArticle
to declare it abstract - Change
publish_article(self)
to simplyfile.write(self.format_text())
Backslash continuation
Change this:
return f"{self.title}{(self.line_width - len(self.title)) * '-'}\n"\
f"{self.text} \n"\
f"{self.city}, {self.date.strftime('%d/%m/%Y %H:%M:%S')} \n"\
f"{'-'*self.line_width}\n\n\n"
to drop the backslashes and use parens instead:
return (
f"{self.title}{(self.line_width - len(self.title)) * '-'}\n"
f"{self.text} \n"
f"{self.city}, {self.date.strftime('%d/%m/%Y %H:%M:%S')} \n"
f"{'-'*self.line_width}\n\n\n"
)
Date parsing
This is evil:
day, month, year = map(int, self.end_date.split('/'))
Instead, you should be using an actual parsing method out datetime
to get you a date
instance; then referring to its members. Since you're looking for DD/MM/YEAR
, this will be:
end_date = datetime.strptime(self.end_date, '%d/%m/%Y').date()
# Use end_date.day, end_date.month, end_date.year
That said, if you're at all able, drop that date format like a sack of rotten potatoes. YYYY-mm-dd is sortable and unambiguous.