Skip to content

Instantly share code, notes, and snippets.

@4wrxb
Last active January 24, 2021 23:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 4wrxb/64c1a5e852bb2a88a06a00b3b659640a to your computer and use it in GitHub Desktop.
Save 4wrxb/64c1a5e852bb2a88a06a00b3b659640a to your computer and use it in GitHub Desktop.
Python 3 script to convert presets from a Prusa Config bundle into CSV for side-by-side comparison
#!/usr/bin/env python3
# Convert a PrusaSlicer_config_bundle.ini file to a set of CSVs where options can be compared across presets
# Author: Will O (https://github.com/4wrxb)
# Email: 0100wrxb@gmail.com
# Date: 2021-01-24
# Copyright (C) 2021 Will O
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
import sys
import csv
from configparser import ConfigParser
########################################################################################################################
# CONFIGURATION #
########################################################################################################################
# General
#--------
# CONTROL DEBUG OUTPUT
enable_debug = 0
# Bypass warnings on the "preset" section (this is just active state)
supress_presets_warning = 1
# Disable outputting a CSV for a given type if it only has one preset
suppress_single_entry_csv = 1
# Supported Preset (section) Types
#---------------------------------
# Each preset in Prusa maps to a section. Each configured types will be parsed in a seperate .csv file.
# Seciton name should be "<type>:*" in the .ini to be recognized otherwise script below will need edited
section_types = ["print", "filament", "printer"]
# WARNING: this is not checked, make sure entries are unique etc. or there will be unexpected behavior
########################################################################################################################
# END CONFIGURATION #
########################################################################################################################
# My logging functions
def fail(message, retcode=1):
print("FATAL : " + message)
exit(retcode)
def error(message):
print("ERROR : " + message)
def warn(message):
print("WARNING : " + message)
def debug(message):
if enable_debug == 1:
print("DEBUG : " + message)
def info(message):
print("INFO : " + message)
# Read in the INI
with open(sys.argv[1], "r") as ini_f:
# Disable interpolation, % are used as raw values by PrusaSlicer
ini = ConfigParser(interpolation=None)
ini.read_file(ini_f)
# Categorize each section, collect row names and options
#-------------------------------------------------------
# Collect the columns and rows in each CSV (section type)
# These will be two dictionaries of sets where the key is the section type
# The entries will be a dictionary of dictionaries (section then option) that need translated on output
# Columns (fields to the DictWriter function) translate to the section in the ini
sec_lists = {x: set() for x in section_types}
# Options within each section translate to a row in the CSV (we take a superset)
opt_lists = {x: set() for x in section_types}
# Option entries themselves will need transposed laters
entries = {}
for sec in ini.sections():
for sectype in section_types:
if sec.startswith(sectype + ":"):
sec_lists[sectype].add(sec)
for key,value in ini.items(sec):
opt_lists[sectype].add(key)
continue
if sec in set.union(*sec_lists.values()):
# If the section was recognized than store all options
entries[sec] = {}
for key,value in ini.items(sec):
entries[sec][key] = value
elif supress_presets_warning == 1 and sec == "presets":
debug("Not warning that " + sec + " is unrecognized due to supression option.")
continue
else:
# If the section wasn't recognized print a warning
warn("Dropping section " + sec + " because its type is not supported.")
# Output a CSV file for each section type
#----------------------------------------
for sectype in section_types:
# Make sure there was a matching section of this type
# Note that the sectype key is defined on initialization, but the value (set) may be empty
if not sec_lists[sectype]:
debug("No sections matched type " + sectype + " a csv for this type will not be written.")
continue
if len(sec_lists[sectype]) == 1:
debug("Not writing single-column csv for " + sectype + " due to supression option.")
continue
# Prepare the section and option lists for the sectype
sections = sorted(sec_lists[sectype])
options = sorted(opt_lists[sectype])
# Name the csv file as orig.ini_[sectype].csv, and create it
# FIXME: this will blindly overwrite files (and could leave stale ones for sections that aren't written)
# will be better to check for argv[1]*.csv and fail early
with open(sys.argv[1] + "_" + sectype + ".csv", "w", newline='') as out_f:
# The fields are (alphebatized) sections with the option name first
out_csv = csv.DictWriter(out_f, fieldnames=["option"] + sections)
out_csv.writeheader()
# Assemble a row for each option and assemble it across sections
for opt in options:
# No need to initialize the row dictionary, DictWriter inserts blank entries automatically
row = {}
# Label the row
row["option"] = opt
# Get the option for each section (if it exists) and put it in the row
for sec in sections:
if opt in entries[sec]:
row[sec] = entries[sec][opt]
out_csv.writerow(row)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment