def filter(expenses, parameters): """ Function that handles the 'filter' functionality :param expenses: The expense list to filter :param parameters: The parameters for filtering """ rules = [ [str], # filter <category> [str, Operator.Operator, int] # filter <category> < / > / = <value> ] all_categories = get_all_categories(expenses) if Parser.parameters_are_valid(parameters, rules[0]) is True: category_to_keep = parameters[0] for category in all_categories: if category != category_to_keep: remove(expenses, [category]) elif Parser.parameters_are_valid(parameters, rules[1]) is True: category_to_keep = parameters[0] operator = parameters[1] value = int(parameters[2]) for category in all_categories: if category != category_to_keep: remove(expenses, [category]) for expense in expenses[:]: if Operator.operator_map[operator](Expense.get_amount(expense), value) is False: expenses.remove(expense)
def menu_sort(expenses): """ Launch the menu that corresponds to the 'sort' command :param expenses: The list of expenses to sort """ options = ['Day', 'Category'] criterion = Parser.choose(options, "Sort by:") if criterion is 'Day': valid_days = Functions.get_all_days( expenses) # get all the unique entries for days day = Parser.get_input_of_type(int, "Please choose a day:") if day not in valid_days: print("There are no entries for day {}".format(str(day))) return Functions.sort_by(expenses, [day]) elif criterion is 'Category': valid_categories = Functions.get_all_categories( expenses) # get all the unique entries for categories category = Parser.choose(valid_categories, "Please pick a category:") Functions.sort_by(expenses, [category])
def menu_list(expenses): """ Launch the menu that corresponds to the 'list' command :param expenses: The list of expenses to print out """ printer = Functions.list_elements categories = ['All categories'] + Functions.get_all_categories(expenses) category = Parser.choose(categories, "Please choose a category:") if category is 'All categories': printer(expenses, []) return constraining_options = ['No constraints', 'Place a constraint'] constraint_choice = Parser.choose(constraining_options, "Please choose a constraint:") if constraint_choice is 'No constraints': printer(expenses, [category]) return operators = ['<', '>', '='] operator = Parser.choose(operators, "Please choose an operator: ") comparison_element = Parser.get_input_of_type( int, "Please choose a number to compare to: ") printer(expenses, [category, operator, comparison_element])
def menu_add(expenses): """ Launch the menu that corresponds to the 'add' command :param expenses: The list of expenses to add elements to """ add = Functions.add # Reference the same function used in the command-based menu print("You have selected the 'add' option.") amount = Parser.get_input_of_type(int, "Please give an amount") category = Parser.get_input_of_type(str, "Please give a category") add(expenses, [amount, category])
def menu_insert(expenses): """ Launch the menu that corresponds to the 'insert' command :param expenses: The list of expenses to insert new expenses into """ add = Functions.insert # Reference the same function used in the command-based menu print("You have selected the 'add' option.") while True: day = Parser.get_input_of_type(int, "Please give a valid day (1-30)") if day in range(1, 31): break amount = Parser.get_input_of_type(int, "Please give an amount") category = Parser.get_input_of_type(str, "Please give a category") add(expenses, [day, amount, category])
def max_day(expenses, parameters): """ Function that gets the day with the biggest expenses :param expenses: :param parameters: :return: """ rule = [] if Parser.parameters_are_valid(parameters, rule) is False: print("Usage: maxday") return max_day = -1 sum_exp_per_day = {} for expense in expenses: day = Expense.get_day(expense) amount = Expense.get_amount(expense) if day not in sum_exp_per_day.keys(): sum_exp_per_day[day] = amount if max_day is -1: max_day = day if sum_exp_per_day[max_day] < amount: max_day = day else: sum_exp_per_day[day] += amount if sum_exp_per_day[max_day] < sum_exp_per_day[day]: max_day = day print(max_day)
def max_per_day(expenses, parameters): """ Function that returns a list of expenses that have the day equal to a given day :param expenses: The list to look through :param day: The target day :return: The list of corresponding days """ usage = "max <day>" rule = [int] if Parser.parameters_are_valid(parameters, rule) is False: print(usage) return day = int(parameters[0]) if day not in range(1, 31): print(usage) return max_expense = 0 for expense in expenses: if Expense.get_day(expense) == day: amount = Expense.get_amount(expense) if amount > max_expense: max_expense = amount print(max_expense)
def sum_category(expenses, parameters): """ Function that handles the 'sum' functionality Usage: sum <category> :param expenses: The expense list :param parameters: The list of parameters used for summing :return: The sum corresponding to the elements described by the parameters """ rule = [str] usage = "sum <category>" if Parser.parameters_are_valid(parameters, rule): target_category = parameters[0] if target_category not in get_all_categories(expenses): print("Category {} not found.".format(target_category)) return total = 0 for expense in expenses: if Expense.get_type(expense) == target_category: total += Expense.get_amount(expense) print(total) else: print(usage)
def menu_max(expenses): """ Launch the menu that corresponds to the 'maxday' command :param expenses: The list of expenses to look through """ options = [ "Get the day with the most expenses", "Get the maximum expense in a day" ] user_choice = Parser.choose(options, "Please choose what max you want:") if user_choice is options[0]: Functions.max_day(expenses, []) elif user_choice is options[1]: day = Parser.get_input_of_type(int, "Please pick a day:") Functions.max_per_day(expenses, [day])
def menu_sum(expenses): """ Launch the menu that corresponds to the 'sum' command :param expenses: The list of expenses to sum """ categories = Functions.get_all_categories(expenses) category = Parser.choose(categories, "Please choose a category:") Functions.sum_category(expenses, [category])
def get_menu_type(): """ Function that gets called first, to decide what type of menu the user desires :return: 1 or 2, corresponding to a command-based interface, or a menu-based one respectively """ print("Please choose an interface type.\n" "1. Command-based\n" "2. Menu-based") while True: choice = Parser.get_input_of_type(int, "Choice: ") if choice in [1, 2]: return choice
def sort_by(expenses, parameters): """ Sort the expenses in a given day in ascending order :param expenses: The expense list to look through :param parameters: The parameters for sorting """ usage = "sort <day>\nsort <category>" rules = [[int], [str]] if Parser.parameters_are_valid(parameters, rules[0]) is True: # sort <day> all_day_entries = get_all_days(expenses) target_day = int(parameters[0]) if target_day not in all_day_entries: print("No entry for day {}".format(parameters[0])) return expenses_in_target_day = get_all_expenses(expenses, day=target_day) criterion = lambda e: Expense.get_amount(e) expenses_in_target_day.sort(key=criterion) list_elements(expenses_in_target_day, []) elif Parser.parameters_are_valid(parameters, rules[1]) is True: # sort <category> all_category_entries = get_all_categories(expenses) target_category = parameters[0] if target_category not in all_category_entries: print("No entry for category {}".format(target_category)) return expenses_in_target_category = get_all_expenses( expenses, category=target_category) criterion = lambda e: Expense.get_amount(e) expenses_in_target_category.sort(key=criterion) list_elements(expenses_in_target_category, []) else: # invalid arguments print(usage) return
def menu_filter(expenses): """ Launch the menu that corresponds to the 'filter' command :param expenses: The list of expenses to filter """ categories = Functions.get_all_categories(expenses) category = Parser.choose(categories, "Please choose a category:") restrictions = ['No restrictions', 'Add restriction'] restriction = Parser.choose(restrictions, "Please choose any further restriction:") if restriction is 'No restrictions': Functions.filter(expenses, [category]) elif restriction is 'Add restriction': operators = ['<', '>', '='] operator = Parser.choose(operators, "Please pick an operator:") value = Parser.get_input_of_type(int, "Please pick a value to compare to:") Functions.filter(expenses, [category, operator, value])
def list_elements(expenses, parameters): ''' Function that handles listing expenses Possible parameters: list list <category> list <category> [ < | = | > ] <value> :param expenses: Expense list to be listed out :param parameters: Parameters used by the listing feature ''' rules = [[], [str], [str, Operator.Operator, int]] rule_used = -1 for rule_index, rule in enumerate(rules): if Parser.parameters_are_valid(parameters, rule): rule_used = rule_index break if rule_used == -1: print('Usage:\n', 'list\n', 'list <category>\n' 'list <category> [ < | = | > ] <value>') else: print_row(to_print=None) print_row(to_print='Header') print_row(to_print=None) if rule_used == 0: # First case for expense in expenses: print_row(to_print=expense) elif rule_used == 1: # Second case for expense in expenses: if Expense.get_type(expense) == parameters[0]: print_row(to_print=expense) elif rule_used == 2: # Third case for expense in expenses: type_match = Expense.get_type(expense) == parameters[0] operator_rule_followed = Operator.operator_map[parameters[1]]( Expense.get_amount(expense), int(parameters[2])) if operator_rule_followed and type_match: print_row(to_print=expense) print_row(to_print=None)
def remove(expenses, parameters): ''' Function that handles the remove feature in the menu Possible parameter structure: remove <day> remove <start day> to <end day> remove <category> :param expenses: Expense list to change :param parameters: Parameters for the remove feature :return: ''' rules = [[int], [int, 'to', int], [str]] rule_used = -1 for rule_index in range(len( rules)): # Figure out which of the three rules the command follows if Parser.parameters_are_valid( parameters, rules[rule_index]): # Found the corresponding set of rules rule_used = rule_index break if rule_used == -1: # No rule matched up with the given parameters print("Usage:\n" "remove <day>\n" "remove <start day> to <end day>\n" "remove <category>") if rule_used == 0: # First case for expense in expenses[:]: if Expense.get_day(expense) == int(parameters[0]): expenses.remove(expense) elif rule_used == 1: # Second case for expense in expenses[:]: if int(parameters[0]) <= Expense.get_day(expense) <= int( parameters[2]): expenses.remove(expense) elif rule_used == 2: # Third case for expense in expenses[:]: if Expense.get_type(expense) == parameters[0]: expenses.remove(expense)
def insert(expenses, parameters): ''' Function that inserts a new expense into the list, but this time all three details of an expense have to be specified :param expenses: The list of expenses :param parameters: The list of parameters ''' # Rule for checking parameter validity rule = [int, int, str] # Parameters have to be in day/amount/category format if not Parser.parameters_are_valid(parameters, rule) or int( parameters[0]) not in range(1, 31): print("Invalid parameters for inserting function.") print("Usage: insert <day> <sum> <category>") return expense = Expense.create(int(parameters[0]), int(parameters[1]), parameters[2]) expenses.append(expense)
def menu_interface(): """ The main menu-based user interface """ os.system('clear') expenses = Expense.initialize_list() expenses_history = [copy.deepcopy(expenses)] menu_options = [ "Add an element to the list", "Insert an element to the list", "List elements", "Sort elements", "Filter out certain elements", "Get maximum", "Sum elements", "Undo", "Exit" ] functions = { menu_options[0]: menu_add, menu_options[1]: menu_insert, menu_options[2]: menu_list, menu_options[3]: menu_sort, menu_options[4]: menu_filter, menu_options[5]: menu_max, menu_options[6]: menu_sum, menu_options[7]: None, menu_options[8]: menu_exit } while True: function_to_call = functions[Parser.choose( menu_options, "Please choose one of the following options:")] os.system('clear') if function_to_call is None: expenses = Functions.undo(expenses_history) continue function_to_call(expenses) if expenses != expenses_history[-1]: expenses_history.append(copy.deepcopy(expenses))
def add(expenses, parameters): ''' Function that handles adding expenses to the expenses list, checking for the day of the month automatically :param expenses: The list to modify :param parameters: The list of parameters the user inputted for the 'add' command The parameters are in the following format: add <sum> <category> ''' # Rule for checking parameter validity rule = [int, str] # Parameters have to be an int and a str in this exact order if not Parser.parameters_are_valid(parameters, rule): print("Invalid parameters for adding function.") print("Usage: add <sum> <category>") return expense = Expense.create(int(datetime.now().day), int(parameters[0]), parameters[1]) expenses.append(expense)