Skip to main content

CLI and library for calculating FDM 3D print job costs

Project description

fdm-price-calc

A CLI tool and Python library for calculating FDM 3D print job costs. Implements the same pricing model as a standard product pricing worksheet: filament cost × efficiency factor + machine time cost + labor = total landed cost, with suggested sell prices at 50 / 60 / 70 % margin.

Installation

pip install fdm-price-calc

Using uv:

# install as a CLI tool
uv tool install fdm-price-calc

# or as a library inside a project
uv add fdm-price-calc

CLI

fdmpricecalc [FILE] [PRINTER] [FILAMENT] [options]
fdmpricecalc new-printer | new-filament | list

FILE is a Supported sliced file . PRINTER and FILAMENT are the names of saved presets (see Presets). All three positional arguments are optional — Missing values are filled in interactively.

Preset auto-detection from file

If the file parcer suppports it, the CLI reads the embedded slicer metadata and tries to match your saved presets automatically before prompting:

  • Printer — reads the printer_model field (e.g., "Bambu Lab P1S") and scores saved presets by word overlap. The best match is suggested, and you can accept or pick a different one.
  • Filament — matches each slot's material type (e.g., PLA) against saved filament presets. Works for both single-filament and multi-filament jobs.

With -y, the match is used silently; if nothing matches, the run errors with a helpful message instead of hanging on a prompt.

# Full run from a sliced file with saved presets
fdmpricecalc job.3mf my_printer pla_basic -n "Widget v2" -a Alice

# Omit PRINTER and FILAMENT — auto-detected from the file
fdmpricecalc job.3mf -n "Widget v2" -a Alice

# Fully scripted with auto-detection — no prompts at all
fdmpricecalc job.3mf -n "Widget" -a Alice -l 5 -q 2 -y

# No file — enter print stats manually via prompts
fdmpricecalc --no-file my_printer pla_basic

# No file — fully scripted, zero prompts
fdmpricecalc --no-file my_printer pla_basic -w 224.93 -t "5h6m12s" \
  --material PLA --color "#FF8800" -n "Pages" -a me -l 10 -q 2 -y

# Override a preset value for this run only
fdmpricecalc job.3mf my_printer pla_basic --efficiency 1.2 --filament-cost 35

# Save result to JSON — minimal by default (no plate data, presets by name)
fdmpricecalc job.3mf bambu_p1s bambu_pla_basic -n "Widget" -o result.json

# Include raw per-plate slicer data
fdmpricecalc job.3mf bambu_p1s bambu_pla_basic -n "Widget" -o result.json -f

# Embed full preset data so the file is self-contained
fdmpricecalc job.3mf bambu_p1s bambu_pla_basic -n "Widget" -o result.json -s

If an unsupported file type is given, the tool falls back to manual entry automatically.

The default JSON output is minimal — just the inputs you provided (printer, filament, labor, quantity, extra costs) and the calculated costs. The raw per-plate slicer data is omitted since it can always be re-read from the source file. Add -f to include it, or -s to embed full preset data for archiving.

Multi-filament

For .3mf files with multiple filament slots, the CLI will automatically try to match each slot to a saved preset by material type (e.g., a slot reporting PLA will match any preset whose material field is PLA). You can also map slots manually:

# Map slot 1 to orange PLA, slot 2 to white PLA; all other slots fall back to pla_basic
fdmpricecalc job.3mf my_printer pla_basic \
  --filament-map 1:pla_orange \
  --filament-map 2:pla_white

# Override cost per kg for all PLA slots in this run only (doesn't change saved presets)
fdmpricecalc job.3mf my_printer pla_basic --material-cost PLA:18

# Also override the efficiency/markup factor for this material
fdmpricecalc job.3mf my_printer pla_basic --material-cost PLA:18:1.8

All flags

  -y, --non-interactive   Never prompt — use provided args and defaults.
                          If PRINTER or FILAMENT are omitted, attempts to
                          auto-detect from file metadata before erroring.
                          Defaults: labor=0, qty=1, no extra costs.
                          Multi-filament slots are auto-matched to saved presets.

job metadata:
  -n, --name NAME       Job name
  -a, --author AUTHOR   Job author
  -o, --output FILE     Save results to JSON file
  -f, --full            Include raw per-plate slicer data in the JSON
                        (plates + totals; omitted by default as they can be
                        re-read from the source file)
  -s, --self-contained  Embed full preset data in the JSON instead of
                        referencing by name (useful for archiving jobs)
  -l, --labor MIN       Post-processing labor time in minutes
  --labor-rate $/HR     Labor hourly rate (overrides preset value for this run)
  -q, --qty N           Quantity to produce
  --extra NAME:COST[:QTY]
                        Extra per-unit cost (hardware, inserts, etc.).
                        Repeatable: --extra 'Magnet:0.50:2' --extra 'Insert:0.08'

manual print stats (use with --no-file or as file fallback):
  -m, --no-file         Skip file parsing; enter stats via args or prompts
  -w, --weight G        Total filament weight in grams
  -t, --time DURATION   Total print time (90, 1h30m, 1:30, 5h6m12s, …)
  --material TYPE       Filament material (PLA, PETG, …)
  --color HEX           Filament color hex (e.g. #FF8800)

filament preset overrides:
  --filament-cost $/KG  Override filament cost per kg (default/single preset)
  --filament-map ID:PRESET
                        Map a filament slot ID to a preset (repeatable)
  --material-cost MATERIAL:$/KG[:MARKUP]
                        Override cost per kg (and optionally the efficiency/markup
                        factor) for all slots of a given material type
                        (e.g., PLA:18 or PLA:18:1.8). Repeatable.
  --efficiency X        Material efficiency factor (default 1.5)

printer preset overrides:
  --print-rate $/HR     Override machine cost rate ($/hr)
  --printer-cost $      Printer purchase cost
  --upfront $           Additional upfront cost
  --maintenance $/YR    Estimated annual maintenance
  --life YRS            Estimated printer life in years
  --uptime PCT          Estimated uptime 0–100
  --power W             Printer power consumption in watts
  --electricity $/KWH   Electricity cost per kWh
  --buffer X            Printer cost buffer factor

Other commands

fdmpricecalc new-printer           # interactive printer, preset wizard
fdmpricecalc new-filament          # interactive filament preset wizard
fdmpricecalc list                  # list all saved presets
fdmpricecalc list printers
fdmpricecalc list filaments

Presets

The package ships with built-in presets for common Bambu and Prusa printers and filaments. They are available immediately after installation with no setup required.

Bundled printers: bambu_a1_mini, bambu_a1, bambu_p1p, bambu_p1s, bambu_p2s, bambu_x1c, bambu_x1e, bambu_h2d, bambu_h2s, bambu_h2c, prusa_mini_plus, prusa_mk4s, prusa_xl, prusa_core_one

Bundled filaments: bambu_pla_basic, bambu_petg_basic, bambu_abs, bambu_tpu, prusament_pla, prusament_petg, prusament_asa

User presets are stored as plain JSON files and always take priority over bundled presets with the same name:

~/.local/share/fdmpricecalc/
    printers/    ← one .json per printer preset
    filaments/   ← one .json per filament preset

To customize a bundled preset, save it under the same name and your version will be used from then on:

fdmpricecalc new-printer   # wizard saves to your user dir
# or edit the JSON directly at the path shown by fdmpricecalc --help

Run fdmpricecalc list to see all available presets (bundled + user). Run fdmpricecalc --help to see the exact user preset paths on your system.

Printer preset fields (printers/my_printer.json):

Field Default Description
name Display name
printer_cost 800 Purchase cost ($)
additional_upfront_cost 0 Upgrades at purchase ($)
annual_maintenance 80 Estimated yearly upkeep ($)
estimated_life_years 5 Expected printer lifespan
estimated_uptime_pct 0.5 Fraction of hours the printer runs
power_watts 160 Average power draw (W)
electricity_cost_per_kwh 0.14 Electricity rate ($/kWh)
printer_cost_buffer_factor 1.3 Multiplier for unexpected costs
material_efficiency_factor 1.5 Filament waste multiplier
labor_hourly_rate 15 Post-processing labor rate ($/hr)
print_time_rate_override null Skip auto-calc and use a fixed $/hr

Filament preset fields (filaments/my_filament.json):

Field Default Description
name Display name
material PLA Material type string
cost_per_kg 20 Cost per kilogram ($)

Pricing model

filament_cost  = (cost_per_kg / 1000) × weight_g × efficiency_factor × qty
machine_cost   = print_time_rate × print_time_hr × qty
labor_cost    = (labor_min / 60) × labor_hourly_rate × qty
extra_cost     = Σ (unit_cost × item_qty) × qty   ← hardware, inserts, etc.

total_landed   = filament_cost + machine_cost + labor_cost + extra_cost

sell_price     = total_landed / (1 − margin)
                 e.g. 50% margin → total_landed × 2
                      60% margin → total_landed × 2.5
                      70% margin → total_landed × 3.33

print_time_rate (auto) = (capital_cost_per_hr + electrical_cost_per_hr)
                         × buffer_factor

Supported file formats

Format Notes
.gcode.3mf Bambu Studio sliced projects; reads slice_info.config for print data and project_settings.config for printer model and filament types
.json Json file produced by the --output option
.gcode PrusaSlicer text G-code; reads embedded config comments
.bgcode PrusaSlicer binary G-code; reads PRINTER_METADATA and PRINT_METADATA blocks

Python library

All pricing inputs are set directly on the JobData object before calling calculate(). Parsers populate the file data (plates, weight, print time); Your code attaches the pricing config.

import fdm_price_calc as fdm

# Parse a sliced .3mf
job = fdm.parse("job.3mf")

# Or build job data manually
job = fdm.manual_job(
    weight_g=224.93,
    print_time_s=fdm.parse_duration("5h6m12s"),
    material="PLA",
)

# Load saved presets (or create inline)
printer  = fdm.PrinterPreset.load("bambu_p1s")
filament = fdm.FilamentPreset.load("bambu_pla_basic")

# Attach pricing config to the job
job.printer       = printer
job.filament      = filament
job.labor_minutes = 10
job.quantity      = 3
job.extra_costs   = [
    fdm.ExtraCost(name="Magnet",    unit_cost=0.50, qty=2),
    fdm.ExtraCost(name="M3 insert", unit_cost=0.08, qty=4),
]

result = fdm.calculate(job)

print(f"Landed cost:  ${result.total_landed_cost:.2f}")
print(f"50% margin:   ${result.price_at_margin['50%']:.2f}")
print(f"60% margin:   ${result.price_at_margin['60%']:.2f}")

# Save presets for later
printer.save()
filament.save()

Multi-filament (per-slot pricing)

For .3mf files where each slot may use a different filament:

job = fdm.parse("multi_color.gcode.3mf")

orange = fdm.FilamentPreset(name="Orange PLA", cost_per_kg=22)
white  = fdm.FilamentPreset(name="White PLA",  cost_per_kg=25)
pla    = fdm.FilamentPreset.load("pla_basic")

job.printer          = printer
job.filament         = {1: orange, 2: white}  # dict[slot_id, preset]
job.default_filament = pla                     # fallback for unmapped slots
job.labor_minutes    = 5
job.quantity         = 1

result = fdm.calculate(job)
# result.filament_breakdown — list of (label, cost) per slot

Public API

Symbol Type Description
parse(path) function Parse a supported file → JobData
manual_job(weight_g, print_time_s, ...) function Build JobData without a file
calculate(job) function Returns CostBreakdown; all inputs read from job
parse_duration(s) function "1h30m" → seconds (int)
JobData dataclass Job data + pricing config (see fields below)
PlateData dataclass Per-plate print time, weight, filaments
FilamentUsage dataclass Per-filament usage within a plate
ExtraCost dataclass Extra per-unit cost item (name, unit_cost, qty)
PrinterPreset dataclass Printer config with cost calculations
FilamentPreset dataclass Filament name, material, $/kg
CostBreakdown dataclass Cost components + margin prices
BaseParser ABC Base class for adding new file formats
list_printers() function Names of saved printer presets
list_filaments() function Names of saved filament presets
data_dir() function Root preset storage path
printers_dir() function Printer preset directory
filaments_dir() function Filament preset directory

JobData pricing fields (set by caller, not by parsers):

Field Type Default Description
printer_hint str | None None Printer model string from file metadata; used by CLI for auto-matching (read-only for library users)
printer PrinterPreset | None None Required before calculate()
filament FilamentPreset | dict[int, FilamentPreset] | None None Single preset or per-slot mapping
default_filament FilamentPreset | None None Fallback for unmapped slots (dict mode)
labor_minutes float 0.0 Post-processing labor time
quantity int 1 Number of copies
extra_costs list[ExtraCost] [] Extra per-unit cost items (hardware, inserts, etc.)
name str "" Job display name
author str "" Job author

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fdm_price_calc-1.0.0.tar.gz (26.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fdm_price_calc-1.0.0-py3-none-any.whl (39.4 kB view details)

Uploaded Python 3

File details

Details for the file fdm_price_calc-1.0.0.tar.gz.

File metadata

  • Download URL: fdm_price_calc-1.0.0.tar.gz
  • Upload date:
  • Size: 26.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fdm_price_calc-1.0.0.tar.gz
Algorithm Hash digest
SHA256 914dac9eabb591da2f2a2e16d83eb7610d4579d4a66631cdcaab2c650f507bb1
MD5 618c0b373f515027ba6b351e3246b89d
BLAKE2b-256 fb2c988ec158ec2b6cbe18ba550322686013d604282b67739e4ce0c847dc8eb0

See more details on using hashes here.

Provenance

The following attestation bundles were made for fdm_price_calc-1.0.0.tar.gz:

Publisher: publish.yml on Gunderson3d/fdmpricecalc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fdm_price_calc-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: fdm_price_calc-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 39.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fdm_price_calc-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 127d3596accca6a652b5d4070bbe837b8b9e6221d17576c535e2416407f7f968
MD5 92631f4882af3a5da200a0bc322a1e63
BLAKE2b-256 de8895bad9bc880af8244c1af39b7b776d489337e9808ff89c076d133ccc969d

See more details on using hashes here.

Provenance

The following attestation bundles were made for fdm_price_calc-1.0.0-py3-none-any.whl:

Publisher: publish.yml on Gunderson3d/fdmpricecalc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page