← Computer Programming I

Variant 1: Log File Processor

Variant Specifications: You are processing application logs to identify and summarize important events. Each log entry is a string with a specific format: YYYY-MM-DD HH:MM:SS [LEVEL] - Message.

Write a function process_logs(logs, min_level) that takes a list of log strings and a minimum severity level. The function should filter for logs that are at or above the min_level and reformat them into a more concise summary.

Severity Hierarchy (from lowest to highest):

  1. INFO
  2. WARNING
  3. ERROR

If min_level is WARNING, you should include both WARNING and ERROR logs. If it is ERROR, you should only include ERROR logs.

Requirements:

  1. The function must accept two arguments: logs (a list of strings) and min_level (a string: "INFO", "WARNING", or "ERROR").
  2. Iterate through each log string in the logs list.
  3. For each log, determine its severity level.
  4. If the log’s severity is at or above min_level, create a summary string.
  5. The summary string format must be: LEVEL (HH:MM): Message.
    • LEVEL is the log’s severity level (INFO, WARNING, or ERROR).
    • HH:MM is the hour and minute from the original timestamp.
    • Message is the original log message text.
  6. The function should return a new list containing only the generated summary strings for the matching logs.
  7. Ignore any empty or malformed lines in the input list.

Testing Inputs:

log_data = [
    "2023-10-01 08:30:00 [INFO] - User 'admin' logged in.",
    "2023-10-01 08:31:15 [INFO] - Service started successfully.",
    "2023-10-01 09:05:22 [WARNING] - Disk space is running low.",
    "",
    "2023-10-01 09:15:00 [ERROR] - Database connection failed.",
    "2023-10-01 09:15:05 [ERROR] - Failed to process user request.",
    "2023-10-01 10:00:45 [INFO] - User 'guest' logged out."
]

# Test Case 1: Process logs with min_level 'WARNING'
warnings_and_errors = process_logs(log_data, "WARNING")
print(warnings_and_errors)

# Test Case 2: Process logs with min_level 'ERROR'
errors_only = process_logs(log_data, "ERROR")
print(errors_only)

# Test Case 3: Process logs with min_level 'INFO'
all_logs = process_logs(log_data, "INFO")
print(all_logs)

Expected Output:

['WARNING (09:05): Disk space is running low.', 'ERROR (09:15): Database connection failed.', 'ERROR (09:15): Failed to process user request.']
['ERROR (09:15): Database connection failed.', 'ERROR (09:15): Failed to process user request.']
["INFO (08:30): User 'admin' logged in.", 'INFO (08:31): Service started successfully.', 'WARNING (09:05): Disk space is running low.', 'ERROR (09:15): Database connection failed.', 'ERROR (09:15): Failed to process user request.', "INFO (10:00): User 'guest' logged out."]

Variant 2: Data Packet Decoder

Variant Specifications: You are writing a decoder for a simple data transmission protocol. Messages are sent as a single string of key-value pairs. Your function needs to validate the message format, extract specific required fields, and report any issues.

Message Format:

  • Key-value pairs are separated by a pipe (|).
  • Each key is separated from its value by a colon (:).
  • The entire message must end with a semicolon (;).
  • Example: ID:A45F|STATUS:OK|PAYLOAD:Data123;

Write a function decode_packet(packet_string, required_fields) that takes the packet string and a list of required field keys.

Requirements:

  1. The function must first check if the packet_string ends with a semicolon. If not, it should immediately return the error string "Error: Invalid packet format.".
  2. If the format is valid, the function should parse the string into its key-value pairs.
  3. It must then check if all keys listed in required_fields are present in the packet.
  4. If one or more required fields are missing, the function should return an error string: "Error: Missing required fields: [FIELD1, FIELD2, ...]", where the list contains the names of the missing fields.
  5. If the format is valid and all required fields are present, the function should return a list of the values corresponding to the required_fields, in the same order.

Testing Inputs:

# Test Case 1: Valid packet with all required fields
packet1 = "ID:A45F|STATUS:OK|PAYLOAD:Data123|CHECKSUM:XYZ;"
required1 = ["ID", "STATUS", "PAYLOAD"]
print(decode_packet(packet1, required1))

# Test Case 2: Valid packet but missing a required field
packet2 = "ID:B98C|STATUS:PENDING;"
required2 = ["ID", "STATUS", "PAYLOAD"]
print(decode_packet(packet2, required2))

# Test Case 3: Packet with invalid format (missing semicolon)
packet3 = "ID:C12D|STATUS:ERROR"
required3 = ["ID"]
print(decode_packet(packet3, required3))

# Test Case 4: Valid packet, different order of fields
packet4 = "PAYLOAD:MoreData|STATUS:OK|ID:D45E;"
required4 = ["ID", "STATUS", "PAYLOAD"]
print(decode_packet(packet4, required4))

Expected Output:

['A45F', 'OK', 'Data123']
Error: Missing required fields: ['PAYLOAD']
Error: Invalid packet format.
['D45E', 'OK', 'MoreData']

Variant 3: Text Invoice Calculator

Variant Specifications: You need to write a function that can parse a multi-line string representing a simple invoice and calculate the final cost. The invoice contains item lines, a tax rate, and an optional discount.

Invoice String Format:

  • Each line represents either an item, tax, or a discount.
  • Item lines have the format: "Item Name @ Quantity x $Price"
  • The tax line has the format: "TAX: Percentage%"
  • The discount line has the format: "DISCOUNT: $Amount"

Write a function calculate_total(invoice_text) that parses this string and returns the final cost.

Calculation Logic:

  1. Subtotal: The sum of Quantity * Price for all item lines.
  2. Discount Application: The discount is a flat amount subtracted from the subtotal.
  3. Tax Application: The tax percentage is applied to the subtotal after the discount has been subtracted.
  4. Grand Total: (Subtotal - Discount) * (1 + Tax Rate)

Requirements:

  1. The function must accept one argument, invoice_text, which is a single multi-line string.
  2. Hint: The input is a single string containing newlines. Use .split('\n') to break it into a list of individual lines so you can process them one by one.
  3. It should parse each line to identify items, tax, and discount.
  4. It must correctly perform the calculations described above.
  5. The function should return the final grand total, formatted as a string with a leading dollar sign and exactly two decimal places. For example, "$123.45".

Testing Inputs:

# Test Case 1: Standard invoice with items, tax, and discount
invoice1 = """Laptop @ 1 x $1250.00
Mouse @ 2 x $25.50
TAX: 8%
DISCOUNT: $50.00"""
print(calculate_total(invoice1))

# Test Case 2: Invoice with no discount
invoice2 = """Book @ 3 x $15.00
Pen @ 10 x $1.50
TAX: 5%"""
print(calculate_total(invoice2))

# Test Case 3: Invoice with zero tax and a discount
invoice3 = """Monitor @ 1 x $300.00
Keyboard @ 1 x $75.00
DISCOUNT: $25.00
TAX: 0%"""
print(calculate_total(invoice3))

Expected Output:

$1351.08
$63.00
$350.00

Variant 4: Weather Alert Filter

Variant Specifications: You are processing a feed of incoming weather reports to identify dangerous conditions. Each report is a string with a specific format: [SEVERITY] Location - MM/DD HH:MM - Description.

Write a function filter_alerts(alerts, min_severity) that takes a list of alert strings and a minimum severity threshold. The function should filter for alerts that are at or above the min_severity and reformat them.

Severity Hierarchy (from lowest to highest):

  1. ADVISORY
  2. WATCH
  3. WARNING

If min_severity is WATCH, you should include both WATCH and WARNING alerts.

Requirements:

  1. The function accepts: alerts (list of strings) and min_severity (string: "ADVISORY", "WATCH", or "WARNING").
  2. Iterate through each string.
  3. Determine the severity level.
  4. If the severity is at or above min_severity, create a summary string.
  5. The summary string format must be: SEVERITY (Location): Description.
    • SEVERITY is the level (ADVISORY, WATCH, or WARNING).
    • Location is the city/region name from the log.
    • Description is the weather details.
    • Note: The timestamp is removed in the summary.
  6. Return a list of the summary strings.
  7. Ignore empty or malformed lines.

Testing Inputs:

weather_data = [
    "[ADVISORY] Austin - 11/05 08:00 - Dense fog reported.",
    "[ADVISORY] Dallas - 11/05 08:15 - Light rain expected.",
    "[WATCH] Houston - 11/05 12:00 - Conditions favorable for tornadoes.",
    "",
    "[WARNING] Galveston - 11/05 14:30 - Hurricane making landfall.",
    "[WARNING] Miami - 11/05 15:00 - Flash flooding imminent.",
    "[ADVISORY] Seattle - 11/05 16:00 - Light drizzle."
]

# Test Case 1: Process with min_severity 'WATCH'
watches_and_warnings = filter_alerts(weather_data, "WATCH")
print(watches_and_warnings)

# Test Case 2: Process with min_severity 'WARNING'
warnings_only = filter_alerts(weather_data, "WARNING")
print(warnings_only)

Expected Output:

['WATCH (Houston): Conditions favorable for tornadoes.', 'WARNING (Galveston): Hurricane making landfall.', 'WARNING (Miami): Flash flooding imminent.']
['WARNING (Galveston): Hurricane making landfall.', 'WARNING (Miami): Flash flooding imminent.']

Variant 5: Game Save Parser

Variant Specifications: You are writing a loader for a video game save system. Save data is stored as a single string of parameters. You need to validate the format, check for required attributes, and extract values.

Save String Format:

  • Parameters are separated by an ampersand (&).
  • Keys and values are separated by an equals sign (=).
  • The string must end with a hash symbol (#).
  • Example: PLAYER=Hero&LEVEL=5&GOLD=100#

Write a function parse_save_data(save_string, required_keys) that takes the save string and a list of keys that must exist.

Requirements:

  1. Check if save_string ends with #. If not, return "Error: Corrupt save file.".
  2. Parse the valid string into key-value pairs.
  3. Check if all keys in required_keys exist in the data.
  4. If required keys are missing, return "Error: Missing save data: [KEY1, KEY2, ...]".
  5. If valid and complete, return a list of values corresponding to the required_keys, in order.

Testing Inputs:

# Test Case 1: Valid save with all fields
save1 = "PLAYER=Kratos&HP=100&XP=4500&MAP=Sparta#"
req1 = ["PLAYER", "HP", "MAP"]
print(parse_save_data(save1, req1))

# Test Case 2: Valid save but missing a key
save2 = "PLAYER=Mario&COINS=50#"
req2 = ["PLAYER", "HP", "COINS"]
print(parse_save_data(save2, req2))

# Test Case 3: Invalid format (missing hash)
save3 = "PLAYER=Link&HEARTS=3"
req3 = ["PLAYER"]
print(parse_save_data(save3, req3))

# Test Case 4: Different order
save4 = "SCORE=99&NAME=PacMan&LIVES=3#"
req4 = ["NAME", "LIVES", "SCORE"]
print(parse_save_data(save4, req4))

Expected Output:

['Kratos', '100', 'Sparta']
Error: Missing save data: ['HP']
Error: Corrupt save file.
['PacMan', '3', '99']

Variant 6: Restaurant Bill Splitter

Variant Specifications: Write a function that parses a multi-line string representing a restaurant bill to calculate the final amount to be charged to a credit card. The bill includes food items, a tip percentage, and a voucher deduction.

Bill String Format:

  • Food lines format: "Dish # Quantity @ $Price" (Note the separators used).
  • Tip line format: "TIP: Percentage%"
  • Voucher line format: "VOUCHER: $Amount"

Write a function calculate_bill(bill_text) to return the final cost.

Calculation Logic:

  1. Subtotal: Sum of Quantity * Price for all food lines.
  2. Voucher Deduction: Subtract the voucher amount from the subtotal.
  3. Tip Application: The tip percentage is applied to the amount after the voucher is subtracted.
  4. Grand Total: (Subtotal - Voucher) * (1 + Tip Rate)

Requirements:

  1. Accept one argument bill_text (multi-line string).
  2. Use .split('\n') to process lines.
  3. Parse lines to identify food, tip, and vouchers.
  4. Perform the math as described.
  5. Return the total as a string formatted with a dollar sign and two decimals (e.g., "$45.00").

Testing Inputs:

# Test Case 1: Standard bill
bill1 = """Burger # 2 @ $12.50
Fries # 2 @ $4.00
TIP: 15%
VOUCHER: $5.00"""
print(calculate_bill(bill1))

# Test Case 2: No voucher
bill2 = """Steak # 1 @ $30.00
Wine # 1 @ $10.00
TIP: 20%"""
print(calculate_bill(bill2))

# Test Case 3: Voucher, no tip
bill3 = """Pizza # 1 @ $18.00
Soda # 2 @ $2.50
VOUCHER: $3.00
TIP: 0%"""
print(calculate_bill(bill3))

Expected Output:

$32.20
$48.00
$20.00

Variant 7: Security Risk Auditor

Variant Specifications: You are analyzing data from a building’s electronic door system to flag security risks. Each entry is a formatted string: [RISK-LEVEL] HH:MM:SS | Zone | Event Details.

Write a function audit_access(entries, min_risk) that takes a list of entry strings and a minimum risk threshold. The function filters for entries at or above the min_risk and reformats them for a report.

Risk Hierarchy (from lowest to highest):

  1. LOW
  2. MED
  3. HIGH

If min_risk is MED, you should include both MED and HIGH entries.

Requirements:

  1. The function accepts: entries (list of strings) and min_risk (string: "LOW", "MED", or "HIGH").
  2. Iterate through each string.
  3. Determine the risk level.
  4. If the risk is at or above min_risk, create a summary string.
  5. The summary string format must be: RISK-LEVEL (HH:MM): Zone - Event Details.
    • RISK-LEVEL is the level found in the brackets.
    • HH:MM is the hour and minute from the timestamp.
    • Zone and Event Details are the remaining parts of the original string.
  6. Return a list of the summary strings.
  7. Ignore empty or malformed lines.

Testing Inputs:

access_log = [
    "[LOW] 07:55:00 | Lobby | Employee badge scan.",
    "[MED] 08:05:12 | Server Room | Door held open too long.",
    "[LOW] 08:10:00 | Cafeteria | Vendor delivery.",
    "",
    "[HIGH] 19:30:45 | Executive Wing | Unidentified keycard used.",
    "[HIGH] 19:35:00 | Vault | Motion sensor triggered.",
    "[MED] 20:00:00 | Parking Garage | Gate malfunction."
]

# Test Case 1: Process with min_risk 'MED'
medium_and_high = audit_access(access_log, "MED")
print(medium_and_high)

# Test Case 2: Process with min_risk 'HIGH'
high_only = audit_access(access_log, "HIGH")
print(high_only)

Expected Output:

['MED (08:05): Server Room - Door held open too long.', 'HIGH (19:30): Executive Wing - Unidentified keycard used.', 'HIGH (19:35): Vault - Motion sensor triggered.', 'MED (20:00): Parking Garage - Gate malfunction.']
['HIGH (19:30): Executive Wing - Unidentified keycard used.', 'HIGH (19:35): Vault - Motion sensor triggered.']

Variant 8: IoT Config Parser

Variant Specifications: You are writing a firmware function to parse configuration strings sent to IoT devices. The configuration is a single string of settings. You need to validate the format, check for required settings, and extract their values.

Config String Format:

  • Settings are separated by a double-dash (--).
  • Keys and values are separated by a double-colon (::).
  • The string must end with a right angle bracket (>).
  • Example: MODE::AUTO--TEMP::72--FAN::ON>

Write a function parse_config(config_string, required_settings) that takes the configuration string and a list of required setting keys.

Requirements:

  1. Check if config_string ends with >. If not, return "Error: Incomplete configuration.".
  2. Parse the valid string into key-value pairs.
  3. Check if all keys in required_settings exist in the data.
  4. If required keys are missing, return "Error: Missing settings: [KEY1, KEY2, ...]".
  5. If valid and complete, return a list of values corresponding to the required_settings, in order.

Testing Inputs:

# Test Case 1: Valid config
conf1 = "SSID::GuestNet--PASS::Secret123--IP::DHCP>"
req1 = ["SSID", "PASS", "IP"]
print(parse_config(conf1, req1))

# Test Case 2: Valid config but missing a setting
conf2 = "SSID::HomeWifi--CHANNEL::6>"
req2 = ["SSID", "PASS"]
print(parse_config(conf2, req2))

# Test Case 3: Invalid format (missing end bracket)
conf3 = "SSID::Office--PASS::Admin"
req3 = ["SSID"]
print(parse_config(conf3, req3))

# Test Case 4: Different order
conf4 = "TIMEOUT::30--PORT::8080--HOST::Localhost>"
req4 = ["HOST", "PORT", "TIMEOUT"]
print(parse_config(conf4, req4))

Expected Output:

['GuestNet', 'Secret123', 'DHCP']
Error: Missing settings: ['PASS']
Error: Incomplete configuration.
['Localhost', '8080', '30']

Variant 9: Construction Bid Calculator

Variant Specifications: Write a function that parses a multi-line string representing a construction bid to calculate the final project cost. The bid includes labor/materials, a mandatory safety fee percentage, and a client deposit deduction.

Bid String Format:

  • Work lines format: "Task -> Hours hrs at $Rate/hr" (Note the separators).
  • Fee line format: "FEE: Percentage%"
  • Deposit line format: "DEPOSIT: $Amount"

Write a function calculate_bid(bid_text) to return the final cost.

Calculation Logic:

  1. Subtotal: Sum of Hours * Rate for all work lines.
  2. Deposit Deduction: Subtract the deposit amount from the subtotal (the client has already paid this).
  3. Fee Application: The fee percentage is applied to the amount after the deposit is subtracted (e.g., overhead/margin).
  4. Grand Total: (Subtotal - Deposit) * (1 + Fee Rate)

Requirements:

  1. Accept one argument bid_text (multi-line string).
  2. Use .split('\n') to process lines.
  3. Parse lines to identify tasks, fees, and deposits.
  4. Perform the math as described.
  5. Return the total as a string formatted with a dollar sign and two decimals (e.g., "$1,200.00"—though commas are optional, the decimal precision is key).

Testing Inputs:

# Test Case 1: Standard bid
bid1 = """Framing -> 10 hrs at $50.00/hr
Wiring -> 5 hrs at $80.00/hr
FEE: 10%
DEPOSIT: $100.00"""
print(calculate_bid(bid1))

# Test Case 2: No deposit
bid2 = """Plumbing -> 2 hrs at $100.00/hr
Cleanup -> 1 hrs at $20.00/hr
FEE: 5%"""
print(calculate_bid(bid2))

# Test Case 3: Deposit, no fee
bid3 = """Painting -> 4 hrs at $25.00/hr
Sanding -> 2 hrs at $15.00/hr
DEPOSIT: $30.00
FEE: 0%"""
print(calculate_bid(bid3))

Expected Output:

$880.00
$231.00
$100.00

Variant 10: Stock Market Signal Filter

Variant Specifications: You are building a tool for a trading desk to filter incoming stock signals. Each signal is a string formatted as: [STRENGTH] Ticker | HH:MM:SS | Analyst Note.

Write a function filter_signals(signals, min_strength) that takes a list of signal strings and a minimum strength threshold. The function filters for signals at or above the min_strength and reformats them.

Strength Hierarchy (from lowest to highest):

  1. HOLD
  2. BUY
  3. STRONG_BUY

If min_strength is BUY, you should include both BUY and STRONG_BUY signals.

Requirements:

  1. The function accepts: signals (list of strings) and min_strength (string: "HOLD", "BUY", or "STRONG_BUY").
  2. Iterate through each string.
  3. Determine the strength level.
  4. If the strength is at or above min_strength, create a summary string.
  5. The summary string format must be: STRENGTH (Ticker): Analyst Note.
    • STRENGTH is the level from the brackets.
    • Ticker is the stock symbol (e.g., AAPL).
    • Analyst Note is the text at the end.
    • Note: The timestamp is removed in the summary.
  6. Return a list of the summary strings.
  7. Ignore empty or malformed lines.

Testing Inputs:

market_data = [
    "[HOLD] AAPL | 09:30:00 | Awaiting earnings report.",
    "[BUY] TSLA | 09:35:15 | Breaking resistance levels.",
    "[HOLD] GOOGL | 10:00:00 | Market undecided.",
    "",
    "[STRONG_BUY] NVDA | 10:15:00 | AI demand surging.",
    "[STRONG_BUY] AMD | 10:20:00 | Competitor weakness.",
    "[BUY] MSFT | 11:00:45 | Cloud revenue up."
]

# Test Case 1: Process with min_strength 'BUY'
actionable_signals = filter_signals(market_data, "BUY")
print(actionable_signals)

# Test Case 2: Process with min_strength 'STRONG_BUY'
urgent_signals = filter_signals(market_data, "STRONG_BUY")
print(urgent_signals)

Expected Output:

['BUY (TSLA): Breaking resistance levels.', 'STRONG_BUY (NVDA): AI demand surging.', 'STRONG_BUY (AMD): Competitor weakness.', 'BUY (MSFT): Cloud revenue up.']
['STRONG_BUY (NVDA): AI demand surging.', 'STRONG_BUY (AMD): Competitor weakness.']

Variant 11: Warehouse Scanner Parser

Variant Specifications: You are writing software for a handheld warehouse scanner. Scanning a QR code produces a raw text string. You need to validate the format, ensure required product details are present, and extract values.

Tag String Format:

  • Data points are separated by a forward slash (/).
  • Labels and values are separated by a hash symbol (#).
  • The string must end with an asterisk (*).
  • Example: SKU#12345/BIN#A1/QTY#50*

Write a function parse_tag(tag_string, required_labels) that takes the tag string and a list of required labels.

Requirements:

  1. Check if tag_string ends with *. If not, return "Error: Read error.".
  2. Parse the valid string into label-value pairs.
  3. Check if all labels in required_labels exist in the data.
  4. If required labels are missing, return "Error: Missing labels: [LABEL1, LABEL2, ...]".
  5. If valid and complete, return a list of values corresponding to the required_labels, in order.

Testing Inputs:

# Test Case 1: Valid scan
scan1 = "SKU#TX-99/LOC#Row3/BATCH#B7*"
req1 = ["SKU", "LOC", "BATCH"]
print(parse_tag(scan1, req1))

# Test Case 2: Valid scan but missing batch info
scan2 = "SKU#RX-11/LOC#Row1*"
req2 = ["SKU", "LOC", "BATCH"]
print(parse_tag(scan2, req2))

# Test Case 3: Invalid format (missing asterisk)
scan3 = "SKU#ZZ-00/LOC#Dock"
req3 = ["SKU"]
print(parse_tag(scan3, req3))

# Test Case 4: Different order
scan4 = "EXP#2024/SKU#MILK/LOC#Fridge*"
req4 = ["SKU", "LOC", "EXP"]
print(parse_tag(scan4, req4))

Expected Output:

['TX-99', 'Row3', 'B7']
Error: Missing labels: ['BATCH']
Error: Read error.
['MILK', 'Fridge', '2024']

Variant 12: Freelance Quote Calculator

Variant Specifications: Write a function that parses a multi-line string representing a freelance project quote to calculate the final fee. The quote includes billable tasks, a rush fee percentage (surcharge), and a “goodwill” discount (credit).

Quote String Format:

  • Task lines format: "Service : Hours hrs x $Rate/hr" (Note the separators).
  • Surcharge line format: "SURCHARGE: Percentage%"
  • Credit line format: "CREDIT: $Amount"

Write a function calculate_quote(quote_text) to return the final cost.

Calculation Logic:

  1. Subtotal: Sum of Hours * Rate for all task lines.
  2. Credit Deduction: Subtract the credit amount from the subtotal.
  3. Surcharge Application: The surcharge percentage is applied to the amount after the credit is subtracted (e.g., a rush fee applied to the remaining balance).
  4. Grand Total: (Subtotal - Credit) * (1 + Surcharge Rate)

Requirements:

  1. Accept one argument quote_text (multi-line string).
  2. Use .split('\n') to process lines.
  3. Parse lines to identify services, surcharges, and credits.
  4. Perform the math as described.
  5. Return the total as a string formatted with a dollar sign and two decimals (e.g., "$550.75").

Testing Inputs:

# Test Case 1: Standard quote
quote1 = """Design : 10 hrs x $50.00/hr
Coding : 20 hrs x $60.00/hr
SURCHARGE: 20%
CREDIT: $100.00"""
print(calculate_quote(quote1))

# Test Case 2: No credit
quote2 = """Consulting : 5 hrs x $100.00/hr
Reporting : 2 hrs x $50.00/hr
SURCHARGE: 5%"""
print(calculate_quote(quote2))

# Test Case 3: Credit, no surcharge
quote3 = """Testing : 10 hrs x $30.00/hr
Docs : 5 hrs x $20.00/hr
CREDIT: $50.00
SURCHARGE: 0%"""
print(calculate_quote(quote3))

Expected Output:

$1920.00
$630.00
$350.00


This is a simplified alternative to the standard Week 8 assignment. If you choose to submit one of these options instead of the standard assignment, the maximum grade you can achieve is 60%.

Variant A: Class Roster Formatter (easy)

Scenario: You have a list of student names from a signup sheet, but the data is messy. Some students used lowercase, some used mixed case, and there are extra spaces everywhere. You need to standardize them into a specific format for the class database.

Task: Write a function format_roster(names) that takes a list of messy strings and returns a list of formatted names.

Requirements:

  1. Iterate through the list of names.
  2. For each name, remove any leading or trailing whitespace.
  3. The name must be converted to ALL CAPS (Uppercase).
  4. If a name entry is an empty string (after stripping whitespace), it should be ignored and not added to the final list.
  5. Return the cleaned list.

Testing Inputs:

student_list = [
    "  john doe ",
    "JANE SMITH",
    "   ",
    "alice wonderland",
    "bOb rOsS  "
]

# Run the function
cleaned_roster = format_roster(student_list)
print(cleaned_roster)

Expected Output:

['JOHN DOE', 'JANE SMITH', 'ALICE WONDERLAND', 'BOB ROSS']

Variant B: Inventory Code Standardizer (easy)

Scenario: You are migrating an old inventory system to a new database. The old system allowed product codes to be entered with dashes, spaces, and mixed capitalization. The new system requires codes to be strictly Uppercase and contain no dashes or spaces.

Task: Write a function fix_inventory_codes(codes) that takes a list of messy code strings and returns a list of standardized codes.

Requirements:

  1. Iterate through the list of codes.
  2. For each code, remove all dashes (-) and all spaces ( ). Hint: You can use .replace() multiple times.
  3. Convert the cleaned code to all Uppercase.
  4. If the resulting code is less than 3 characters long, it is considered invalid and should be skipped (do not add it to the final list).
  5. Return the list of valid, standardized codes.

Testing Inputs:

raw_codes = [
    "abc-123",
    "  X-99 ",
    "prod 456",
    "a-1",       # Too short after cleaning (becomes "A1")
    "super-item"
]

# Run the function
fixed_codes = fix_inventory_codes(raw_codes)
print(fixed_codes)

Expected Output:

['ABC123', 'X99', 'PROD456', 'SUPERITEM']

Variant C: File Name Cleaner (easy)

Scenario: Users are uploading files to your server, but the file names contain uppercase letters and spaces, which causes issues for your web server. You need to convert them to “safe” file names.

Task: Write a function sanitize_filenames(files) that takes a list of raw filenames and returns a list of web-safe filenames.

Requirements:

  1. Iterate through the list of filenames.
  2. Remove any leading or trailing whitespace.
  3. Convert the entire filename to lowercase.
  4. Replace all remaining spaces in the middle of the name with underscores (_).
  5. Check if the file is a text file (contains ".txt"). If the filename does not contain ".txt", skip it.
  6. Return the list of cleaned, valid filenames.

Testing Inputs:

uploads = [
    "  My Resume.pdf ",  # Not a .txt file, skip
    "Vacation Photo.JPG", # Not a .txt file, skip
    "PROJECT specs.txt",
    "   ",
    "notes.txt"
]

# Run the function
safe_files = sanitize_filenames(uploads)
print(safe_files)

Expected Output:

['project_specs.txt', 'notes.txt']