Exerciseschevron_rightChapter 3chevron_right3.7
fitness_center

Exercise 3.7

Drilling Fluid Inventory

Level 2
Chapter 3: Data Structures
descriptionProblem

A drilling operation tracks its mud chemicals as a dictionary of dictionaries: each additive name maps to a record of how much is on location, the sack weight, the reorder level, and what the additive does. The starter gives you an 8-item inventory.

Write four functions:

  1. total_weight_lb(inv): total weight on location:

quantity_sacks × unit_weight_lb, summed over every additive.

  1. below_reorder(inv): the names of additives whose quantity_sacks

has dropped below their reorder_sacks level (time to order more).

  1. add_shipment(inv, material, sacks): a delivery arrives: increase that

additive's quantity_sacks and return the new quantity.

  1. use_material(inv, material, sacks): the rig consumes material:

decrease the quantity and return the new amount. If there isn't enough on location, raise ValueError instead of going negative. You can't pump barite you don't have.

The last one is the real lesson: a function that silently lets stock go negative will quietly corrupt every report downstream. Failing loudly is the correct engineering behaviour.

lightbulbHints (0/3)

Stuck? Reveal hints one at a time — they progress from nudge to near-solution.

codeYour solution
main.py
visibilityReveal reference solutionexpand_more

Try solving it yourself first — the hints walk you through it. The solution below is one valid approach; yours may differ and still be correct.

inventory = {
    "Barite":         {"quantity_sacks": 1200, "unit_weight_lb": 100, "reorder_sacks": 400, "purpose": "Weighting agent"},
    "Bentonite":      {"quantity_sacks":  250, "unit_weight_lb": 100, "reorder_sacks": 300, "purpose": "Viscosifier"},
    "CMC":            {"quantity_sacks":   80, "unit_weight_lb":  55, "reorder_sacks": 100, "purpose": "Fluid loss control"},
    "Caustic Soda":   {"quantity_sacks":   60, "unit_weight_lb":  55, "reorder_sacks":  50, "purpose": "pH control"},
    "Soda Ash":       {"quantity_sacks":   90, "unit_weight_lb":  50, "reorder_sacks":  40, "purpose": "Calcium removal"},
    "KCl":            {"quantity_sacks":  700, "unit_weight_lb": 110, "reorder_sacks": 300, "purpose": "Shale inhibition"},
    "Lignosulfonate": {"quantity_sacks":  120, "unit_weight_lb":  55, "reorder_sacks":  80, "purpose": "Thinner"},
    "Lime":           {"quantity_sacks":  200, "unit_weight_lb":  50, "reorder_sacks":  60, "purpose": "Alkalinity"},
}


def total_weight_lb(inv):
    return sum(item["quantity_sacks"] * item["unit_weight_lb"] for item in inv.values())


def below_reorder(inv):
    return [name for name, item in inv.items() if item["quantity_sacks"] < item["reorder_sacks"]]


def add_shipment(inv, material, sacks):
    inv[material]["quantity_sacks"] += sacks
    return inv[material]["quantity_sacks"]


def use_material(inv, material, sacks):
    available = inv[material]["quantity_sacks"]
    if sacks > available:
        raise ValueError(f"Cannot use {sacks} sacks of {material}: only {available} on location")
    inv[material]["quantity_sacks"] -= sacks
    return inv[material]["quantity_sacks"]


# Formatted inventory report.
print(f"{'Additive':<16}{'Sacks':>8}{'Weight (lb)':>14}")
for name, item in inventory.items():
    weight = item["quantity_sacks"] * item["unit_weight_lb"]
    flag = "  ← reorder" if item["quantity_sacks"] < item["reorder_sacks"] else ""
    print(f"{name:<16}{item['quantity_sacks']:>8,}{weight:>14,}{flag}")
print(f"{'TOTAL':<16}{'':>8}{total_weight_lb(inventory):>14,}")

lockCopying code is a Full Access feature.