3
\$\begingroup\$

I am making a simple calendar app as part of my course in CodeCademy.
Basic functions of the app should be: view the calendar, add events to the calendar, update events in the calendar, delete events from the calendar.

While trying to work on updating and finding items in the calendar I found myself struggling by creating functions to identify which item(event) should be actually updated or deleted and this got me thinking that by simply adding a simple id number to the event name and date would help in identifying items in the calendar greatly.

What I'd like to achieve is having some sort of data structure (that's not so complicated) to get 3 pieces of data and be able to find the item by each of the following: unique id, date, event name.
Right now I am using dictionary as calendar = {"YYYY/MM/DD" : "Event name"}

I was looking for some answers to this challenge and found arrays using numpy, but not really sure if that's necessary (and looks complicated), so I am asking you. If that's too ignorant approach then I am sincerely sorry and I promise to ask better next time, but it's my first week of learning python so there's a lot to learn.

In case any of you wanted to see the code and have any tips for me, I'd be glad. (I am aware I'd need to rewrite everything to take the new data type into consideration)

"""Command Line Calendar
Functionalities:
 - View the calendar
 - Add an event to the calendar
 - Update an existing event
 - Delete an existing event
 The program should behave in the following way:

Prompt the user to view, add, update, or delete an event on the calendar
Depending on the user's input: view, add, update, or delete an event on the calendar
The program should never terminate unless the user decides to exit
"""
from time import sleep, strftime
from calendar import monthrange

def welcome():
  print("Welcome to Command Line Calendar!")
  name = str(input("What's your name?"))
  print("Hello, ", name, ". It's nice to meet you!", sep="")
  sleep(1.5)
  return


def print_menu():
    # Prints menu options in cmd line.
    print(strftime("%A, %m-%d-%Y %H:%M:%S"))
    print("Menu: ")
    sleep(0.5)
    print("1 - View the calendar")
    sleep(0.5)
    print("2 - Add an event to the calendar")
    sleep(0.5)
    print("3 - Update an existing event")
    sleep(0.5)
    print("4 - Delete an existing event")
    sleep(0.5)
    print("0 - Exit the calendar")
    sleep(0.7)
    return


def get_user_input():
    # Gets user_input. Blocks wrong user_input. Only integers 0-4.
    while True:
        try:
            user_input = int(input("Choose your option: "))
            if user_input > 4 or user_input < 0:
                raise ValueError
            break
        except ValueError:
            print("Should be an integer 0-4.")

    return user_input


def choose_function(user_input, cal):
    # Processes user_input and forwards to proper function.
    if user_input == 0:
        exit_calendar()
    elif user_input == 1:
        view_calendar(cal)
    elif user_input == 2:
        add_event(cal)
    elif user_input == 3:
        update_event(cal)
    elif user_input == 4:
        delete_event(cal)
    return


def exit_calendar():
    # Prints a message and exits the program using exit().
    print("Thank you for using Command Line Calendar!")
    print("Exiting...")
    exit()
    return


def view_calendar(cal):
  if len(cal) == 0:
    print("There are no entries to show.")
    print()
    sleep(1)
  else:
    cal_keys_sorted = sorted(cal)
    for k in cal_keys_sorted:
      print(k, "-", cal[k])
    print()


def add_event(cal):
  print("We need 2 piece of data: date and event name.")
  date_str = get_date_as_string()
  event_name = str(input("Event name: "))
  cal[date_str] = event_name
  print()
  print("Calendar item:", date_str, "-", cal[date_str]) # Prints event and date added.
  print()
  print("Item successfully added to the calendar!")
  sleep(1)
  return


def update_event(cal):
    return


def delete_event(cal):
  view_calendar(cal)
  print("We need to find an item you want to delete.")
  find_calendar_item(cal)

    return

def create_calendar():
  calendar = {}
  return calendar

def get_date_as_string():
  # This function takes user input and makes sure it's in proper format: MM/DD/YYYY.
  while True:
    try:
      y = int(input("Year formatted YYYY: "))
      if str(y) < strftime("%Y"):
        raise ValueError
      break
    except ValueError:
      print("Try again with suggested format. Year must be", strftime("%Y"), "or further.")

  while True:  
    try:
      mo = str(input("Month formatted MM: "))
      if len(mo) > 2 or len(mo) < 2:
        raise ValueError
      if int(mo) > 12 or int(mo) < 1:
        raise ValueError
      if str(y) == strftime("%Y"):
        if str(mo) < strftime("%m"):
          raise ValueError
      break
    except ValueError:
      print("Try again with the format suggested. Month should be within 1-12 range, but not in the past.")

  while True:
    try:
      d = str(input("Day formatted DD: "))
      if len(d) > 2 or len(d) < 2:
        raise ValueError
      if int(d) > monthrange(y,int(mo))[1] or int(d) < 1:
        raise ValueError
      if str(y) == strftime("%Y") and \
         str(mo) == strftime("%m") and \
         str(d) < strftime("%d"):
           raise ValueError
      break
    except ValueError:
      print("Try again with the format suggested. Also number of days has to be in range: 01 -", monthrange(y,int(mo))[1])

  date = str(y) + "/" + str(mo) + "/" + str(d)

  return date

def find_calendar_item(cal):
  print("1 - Find calendar item by date")
  print("2 - Find calendar item by event name")
  # Checks if input is an integer 1-2
  while True:
    try: 
      user_input = int(input("Choose your option: "))
      if (user_input > 2) or (user_input < 1):
        raise ValueError
      break
    except ValueError: 
      print("Choose 1 to find by date or 2 to find by event name.")
    if user_input == 1:
      find_item_by_date(cal)
    if user_input == 2:
      item_name = find_item_by_name(cal)


def find_item_by_name(cal):
  temp_cal = {}
  while True:
    user_input = str(input("What name do you want to find? "))
    # Adding substrings found to temp_cal
    i = 1
    for k in cal:
      if user_input.lower() in str(cal[k]).lower():
        temp_cal[i] = cal[k]
        i += 1
    # If no items found
    if len(list(temp_cal.keys())) == 0:
      print("No items with", user_input, "found. Try again!")
      print()
    else: 
      break

  for k1 in temp_cal:
    print(k1, '-', temp_cal[k1])
  print("0 - It's not on the list")
  while True:
    try:
      user_input2 = int(input("Is the item you are looking for any of these?"))
      if user_input2 < 0 or user_input2 > len(list(temp_cal.keys())):
        raise ValueError
      break
    except ValueError:
      print("Choose from 0-", len(list(temp_cal.keys())))
  if user_input2 == 0:
    print("Okay, let's try another name.")
    print()
    find_item_by_name(cal)
    return
  for key in cal:
    if temp_cal[user_input2] == cal[key]:
      return key

def find_item_by_date(cal):
  return

def calendar_app():
  welcome()
  cal = create_calendar()
  while True:
    print_menu()
    user_input = get_user_input()
    choose_function(user_input, cal)
  return

#calendar_app()

print(strftime("%A, %m - %d - %Y"))

cal = {
  "2018/03/12" : "Work",
  "2018/12/24" : "Christmas",
  "2019/03/03" : "Manicure",
}
view_calendar(cal)
print()
print()
print()
find_item_by_name(cal)
\$\endgroup\$
  • 1
    \$\begingroup\$ Please ensure that your code is correctly indented. Your deleve_event() function has an incorrectly indented return, for example. The easiest way to post code is to paste it into the question editor, highlight it, and press Ctrl-K to mark it as a code block. \$\endgroup\$ – 200_success Mar 13 '18 at 19:06
1
\$\begingroup\$

From a quick review, I can suggest the following:

  • input() returns a string by default, you don't need to wrap it with str(
  • print functions can use f-strings (if you're using Python 3.4 or newer). for instance, your line becomes the following line:

    print("Hello, ", name, ". It's nice to meet you!", sep="")
    print(f"Hello, {name}. It's nice to meet you!")
    
  • You don't need a return following the exit() - it should be like (remember to import sys):

    def exit_calendar():
        print("Thank you for using Command Line Calendar!\nExiting...")
        sys.exit()
    
  • One thing new programmers always do is to write comments. Uncle Bob teaches us that if you have to write a comment - then your code is wrong*. For instance this comment:

        # Prints a message and exits the program using exit().
    

    is redundant, because you've named your function exit_calendar. It's pretty self-explanatory. There's no need to repeat in a comment what your code has already stated.

  • print() -> Use "\n" in your print statements.

  • return at the end of functions. You only need those if you're returning something, otherwise it will "return" to the caller anyway.

def find_calendar_item(cal):
  print("1 - Find calendar item by date")
  print("2 - Find calendar item by event name")
  # Checks if input is an integer 1-2
  while True:
    try: 
      user_input = int(input("Choose your option: "))
      if (user_input > 2) or (user_input < 1):
        raise ValueError
      break
    except ValueError: 
      print("Choose 1 to find by date or 2 to find by event name.")
    if user_input == 1:
      find_item_by_date(cal)
    if user_input == 2:
      item_name = find_item_by_name(cal)

I can see the following problems with the above block:

Redundant comment, redundant brackets, using a while loop and throwing an exception to control program flow, converting the input into an integer (you're not doing any math, so there's actually no need to convert it into an integer) and assignment with an unused variable (item_name = find_item...).

I'd do something like this instead:

def find_calendar_item(cal):
    print("1 - Find calendar item by date")
    print("2 - Find calendar item by event name")
    print("Any other key returns to previous function.")
    user_input = input("Choose your option: ")
    if user_input in ("1", "2"):
        find_item_by_date(cal) if user_input == "1" else find_item_by_name(cal)

Admittedly I actually wouldn't do the last line, purely because it's not clear coding, but it demonstrates the ternary and that you can utilise functions in such a fashion.
The while loop is removed, the exception is removed, and the input isn't converted. An extra line is printed explaining what happens if any other key is pressed (you should trust your users will know if they hit the right key - don't over engineer it - plus also as they have to hit [ENTER] to complete the input, they have a chance to fix it, if they do actually press something other than intended).

Hope this helps, Good Luck!

* code is wrong: Not wrong per-say - but a failure to express the instructions in an easy to comprehend fashion. If I cannot read your code and understand what you're trying to do - then you've wasted time writing the code (obviously there are always exceptions to the rule and there are times when a comment is necessary).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.