TODO list:
DONE - 1) Implement sys instead of print and std error instead of error
DONE - 2) Check if the input data is correct (data_20 (DONE))
DONE - 3) Handle errors when you have invalid date and that the date would be lower than today or in the future
DONE - 4) Create how to send email
DONE - 5) Invalid error when there is no valid name
DONE - 6) Invalid  when email is wrong
DONE - 7) Write tests
DONE - 8) Update README.md
DONE - 9) Handle standard error, std output, so it is written in a terminal
DONE - 10) How to load env variable from .env
DONE - 11) Check that it tries sending three times - emails can always be sent successfully in 3 retries,
DONE - 12) Make Cron work
DONE - 13) Add additional tests
DONE - 14) Check for logging
DONE - 15) Update data.csv before sending
DONE - 16) Fix what the input asks for if you input string
DONE - 17) Add doc strings to bdayreminder.py
DONE - 18) Add doc strings to tests.py
DONE - 19) Update README.md more
DONE - 20) Check if the input data is correct (data_50 (DONE), data_50 (DONE))
DONE - 21) Some functions seem redundant. E.g. is_not_empty_name() - it could simply be replaced by one statement. Also, the name is_not_empty_name() has negation in the name, making it harder to read, e.g. if is_not_empty_name() vs. if not is_not_empty_name() (subjective)
DONE - 22) Avoid redundant comments, e.g. Importing Regex on obvious statements.
DONE - 23) Exception handling - e.g. in convert_birthday_file() if e.g. file fails to open (e.g. does not exist), the message "Wrong input data file" would be printed, but the method would continue and None would be returned at the end, leading to a TypeError: 'NoneType' object is not iterable in validate_data_and_send_emails. Result - we have an error message the input data file is wrong and some unrelated traceback, hiding the original problem that the file does not exist - it is very difficult to debug such problems.
DONE - 24) Naming - e.g. is_birthdate_in_7_days(), the name implies that the function would check if the person's birthday is in 7 days - however, the method simply returns a string of the date in 7 days. The name misleads the reader. Similar with convert_birthday_file() - the name implies some conversion/data parsing, but it simply opens the file and returns a csv.reader (also, the file is not closed in convert_birthday_file()).
DONE - 25) Function structure/return types - e.g. try_parsing_date will return either datetime object or dict as first item, which makes it difficult to work with its results - need to check type. It is also not clear why it must return dict in given format.
DONE - 26) Update tests