Objective
Develop a Python program that can store up to 10000 records of expenses and incomes to create a small household accounting system. For each expense (or income), the following information should be saved:
• Date (8 characters: YYYYMMDD format)
• Description of the expense or income
• Category
• Amount (positive for income, negative for expenses)
The program should allow the user to perform the following operations:
1 - Add a new expense (the date should "seem correct": day 01 to 31, month 01 to 12, year between 1000 and 3000). The description must not be empty. No need to validate the other data.
2 - Show all expenses of a certain category (e.g., "education") between two specific dates (e.g., between "20110101" and "20111231"). Display the number, date (format DD/MM/YYYY), description, category in parentheses, and amount to two decimal places, all in the same line, separated by hyphens. At the end, show the total amount of the displayed data.
3 - Search for expenses containing a certain text (in the description or category, case insensitive). Display the number, date, and description (truncated to 50 characters if longer). Notify the user if none are found.
4 - Modify a record (prompt the user for the record number, display the previous value of each field, and allow the user to press Enter to leave any field unchanged). Warn (but do not re-prompt) if the user enters an incorrect record number. No need to validate any data.
5 - Delete records, starting from the number entered by the user. Warn (but do not re-prompt) if an incorrect number is entered. Display the record to be deleted and prompt for confirmation before deletion.
6 - Sort data alphabetically by date and (if dates match) by description.
7 - Normalize descriptions: remove trailing spaces and duplicate spaces. If a description is all uppercase, convert it to lowercase (except for the first letter, which remains uppercase).
Q - Quit the application (as we do not store the information, the data will be lost).
Example Python Exercise
Show Python Code
import re
from datetime import datetime
# List to store records
records = []
# Loop for user interaction
while True:
# Show menu
print("\nMenu:")
print("1 - Add a new expense")
print("2 - Show all expenses of a category between two dates")
print("3 - Search for expenses containing a certain text")
print("4 - Modify a record")
print("5 - Delete records")
print("6 - Sort data")
print("7 - Normalize descriptions")
print("Q - Quit")
choice = input("Choose an option: ").strip().lower()
if choice == '1': # Add a new expense
date = input("Enter date (YYYYMMDD): ")
while True:
try:
datetime.strptime(date, "%Y%m%d")
break
except ValueError:
print("Invalid date format. Please enter a valid date (YYYYMMDD).")
date = input("Enter date (YYYYMMDD): ")
description = input("Enter description: ").strip()
while not description:
print("Description cannot be empty.")
description = input("Enter description: ").strip()
category = input("Enter category: ").strip()
amount = input("Enter amount (positive for income, negative for expense): ")
while not amount.replace('.', '', 1).isdigit() or float(amount) == 0:
print("Invalid amount. Enter a valid number (positive for income, negative for expense).")
amount = input("Enter amount (positive for income, negative for expense): ")
records.append({
'date': date,
'description': description,
'category': category,
'amount': float(amount)
})
elif choice == '2': # Show all expenses of a category between two dates
category = input("Enter category: ").strip()
start_date = input("Enter start date (YYYYMMDD): ")
end_date = input("Enter end date (YYYYMMDD): ")
while True:
try:
datetime.strptime(start_date, "%Y%m%d")
datetime.strptime(end_date, "%Y%m%d")
break
except ValueError:
print("Invalid date format. Please enter valid dates (YYYYMMDD).")
start_date = input("Enter start date (YYYYMMDD): ")
end_date = input("Enter end date (YYYYMMDD): ")
total_amount = 0.0
count = 0
for i, record in enumerate(records):
if record['category'].lower() == category.lower() and start_date <= record['date'] <= end_date:
print(f"{i+1} - {record['date'][6:8]}/{record['date'][4:6]}/{record['date'][:4]} - {record['description']} - ({record['category']}) - {record['amount']:.2f}")
total_amount += record['amount']
count += 1
if count > 0:
print(f"Total: {total_amount:.2f}")
else:
print("No records found.")
elif choice == '3': # Search for expenses containing a certain text
search_text = input("Enter the text to search for: ").strip().lower()
count = 0
for i, record in enumerate(records):
if search_text in record['description'].lower() or search_text in record['category'].lower():
print(f"{i+1} - {record['date'][6:8]}/{record['date'][4:6]}/{record['date'][:4]} - {record['description'][:50]}...")
count += 1
if count == 0:
print("No records found.")
elif choice == '4': # Modify a record
record_number = input("Enter record number to modify: ")
while not record_number.isdigit() or int(record_number) < 1 or int(record_number) > len(records):
print("Invalid record number.")
record_number = input("Enter record number to modify: ")
record_index = int(record_number) - 1
record = records[record_index]
print(f"Current record: {record['date']} - {record['description']} - {record['category']} - {record['amount']:.2f}")
date = input(f"Enter new date (current: {record['date']}): ").strip()
if date:
record['date'] = date
description = input(f"Enter new description (current: {record['description']}): ").strip()
if description:
record['description'] = description
category = input(f"Enter new category (current: {record['category']}): ").strip()
if category:
record['category'] = category
amount = input(f"Enter new amount (current: {record['amount']:.2f}): ").strip()
if amount:
record['amount'] = float(amount)
elif choice == '5': # Delete records
start_record = input("Enter starting record number to delete: ")
end_record = input("Enter ending record number to delete: ")
while not start_record.isdigit() or not end_record.isdigit() or int(start_record) > int(end_record) or int(start_record) < 1 or int(end_record) > len(records):
print("Invalid record numbers.")
start_record = input("Enter starting record number to delete: ")
end_record = input("Enter ending record number to delete: ")
for i in range(int(start_record) - 1, int(end_record)):
print(f"Deleting record: {records[i]}")
confirm = input("Are you sure? (y/n): ").strip().lower()
if confirm == 'y':
del records[i]
break
elif choice == '6': # Sort data
records.sort(key=lambda x: (x['date'], x['description'].lower()))
print("Records sorted.")
elif choice == '7': # Normalize descriptions
for record in records:
record['description'] = re.sub(' +', ' ', record['description']).strip()
if record['description'].isupper():
record['description'] = record['description'].capitalize()
print("Descriptions normalized.")
elif choice == 'q': # Quit
break
else:
print("Invalid choice. Please try again.")
Output
Case 1:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: 1
Enter date (YYYYMMDD): 20231225
Enter description: Christmas gifts
Enter category: Gifts
Enter amount (positive for income, negative for expense): -200
Case 2:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: 2
Enter category: Gifts
Enter start date (YYYYMMDD): 20231201
Enter end date (YYYYMM31): 20231231
1 - 25/12/2023 - Christmas gifts - (Gifts) - -200.00
Total: -200.00
Case 3:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: 3
Enter the text to search for: gifts
1 - 25/12/2023 - Christmas gifts...
Case 4:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: 4
Enter record number to modify: 1
Current record: 20231225 - Christmas gifts - Gifts - -200.00
Enter new date (current: 20231225):
Enter new description (current: Christmas gifts):
Enter new category (current: Gifts):
Enter new amount (current: -200.00):
Case 5:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: 5
Enter starting record number to delete: 1
Enter ending record number to delete: 1
Deleting record: {'date': '20231225', 'description': 'Christmas gifts', 'category': 'Gifts', 'amount': -200.0}
Are you sure? (y/n): y
Case 6:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: 6
Records sorted.
Case 7:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: 7
Descriptions normalized.
Case 8:
Menu:
1 - Add a new expense
2 - Show all expenses of a category between two dates
3 - Search for expenses containing a certain text
4 - Modify a record
5 - Delete records
6 - Sort data
7 - Normalize descriptions
Q - Quit
Choose an option: q
Share this Python Exercise