summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Phillips <david@sighup.nz>2018-11-18 22:11:25 +1300
committerDavid Phillips <david@sighup.nz>2018-11-18 22:33:14 +1300
commit9606df3ddc5da274bee5256666cc188b2ed50a4e (patch)
tree2656fdad0c0c2b557fd20d646dcf44e64835701f
downloadbsmx2btsqlite-9606df3ddc5da274bee5256666cc188b2ed50a4e.tar.xz
Dump semi-working product
Still has to operate on live files, probably brewtarget wants all of the tables present in a mergee database
-rwxr-xr-xbsmx2btsqlite.py202
1 files changed, 202 insertions, 0 deletions
diff --git a/bsmx2btsqlite.py b/bsmx2btsqlite.py
new file mode 100755
index 0000000..25292f6
--- /dev/null
+++ b/bsmx2btsqlite.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python
+
+# FIXME/WARN/NOTE: This software is pretty darned janky at the moment. It
+# was written to work once with a file from the website of Gladfield Malts.
+# I made it public just in case anyone else is stuck out there and doesn't
+# want to do a bunch of manual data entry. I hope you find it useful, but
+# don't be surprised if it doesn't do what you want. It currently only
+# supports grains as ingredients, and what I presume to be a narrow view
+# of the bsmx format (Selection).
+#
+# As it stands, usage is limited to working on the main db itself. First make
+# a backup of your database (you have been warned) and then something like:
+#
+# ./bsmx2btsqlite.py foo.bsmx ~/.config/brewtarget/database.sqlite
+#
+# You should see output like:
+#
+# Item count: 33
+# Added grain Gladfield Ale Malt
+# Added grain Gladfield American Ale Malt
+# Added grain Gladfield Aurora Malt
+# Added grain Gladfield Biscuit Malt
+# Added grain Gladfield Brown Malt
+# Added grain Gladfield Chocolate Rye Malt
+# Added grain Gladfield Crystal Rye Malt
+# Added grain Gladfield Dark Chocolate Malt
+# Added grain Gladfield Dark Crystal Malt
+# Added grain Gladfield Eclipse Wheat
+# Added grain Gladfield German Pilsner Malt
+# Added grain Gladfield Gladiator Malt
+# Added grain Gladfield Lager Light
+# Added grain Gladfield Light Chocolate Malt
+# Added grain Gladfield Light Crystal Malt
+# Added grain Gladfield Manuka Smoked Malt
+# Added grain Gladfield Medium Crystal Malt
+# Added grain Gladfield Munich Malt
+# Added grain Gladfield Organic Pilsner
+# Added grain Gladfield Pilsner Malt
+# Added grain Gladfield Red Back Malt
+# Added grain Gladfield Red Back Wheat
+# Added grain Gladfield Roast Barley
+# Added grain Gladfield Roasted Wheat
+# Added grain Gladfield Rye Malt
+# Added grain Gladfield Shepherds Delight Malt
+# Added grain Gladfield Sour Grapes Malt
+# Added grain Gladfield Supernova Malt
+# Added grain Gladfield Toffee Malt
+# Added grain Gladfield Vienna Malt
+# Added grain Gladfield Wheat Malt
+# Added grain Harraway's Rolled Oats
+# Added grain Harraways Rolled Barley
+
+import sys
+import sqlite3
+import xml.etree.ElementTree
+
+def getFirstTagNamed(parent, name):
+ """
+ Returns the first child node of `parent` that has the tag name `name`
+ """
+ for t in list(parent):
+ if t.tag == name:
+ return t
+
+def getTextFirstTagNamed(parent, name):
+ """
+ Returns the text content of the first child node of `parent` that has the
+ tag name `name`, or an empty string if it has no text content
+ """
+ t = getFirstTagNamed(parent, name)
+ if t.text is None:
+ return ""
+ else:
+ return t.text
+
+def processGrain(g, dbc):
+ """
+ Run a query on dbc to add an ingredient for the node g
+ """
+ mappings = {
+ "name": "F_G_NAME",
+ "origin": "F_G_ORIGIN",
+ "supplier": "F_G_SUPPLIER",
+ "color": "F_G_COLOR",
+ "yield": "F_G_YIELD",
+ "coarse_fine_diff": "F_G_COARSE_FINE_DIFF",
+ "moisture": "F_G_MOISTURE",
+ "diastatic_power": "F_G_DIASTATIC_POWER",
+ "protein": "F_G_PROTEIN",
+ "max_in_batch": "F_G_MAX_IN_BATCH",
+ "add_after_boil": "F_G_ADD_AFTER_BOIL",
+ "recommend_mash": "F_G_RECOMMEND_MASH",
+ "is_mashed": "F_G_RECOMMEND_MASH", # FIXME? This is the same source as as recommend_mash
+ "notes": "F_G_NOTES",
+ }
+ assert(g.tag == "Grain")
+
+ fields = ", ".join(mappings.keys())
+ placeholders = ("?, " * (len(mappings) - 1)) + "?"
+ statement = "INSERT INTO fermentable("+fields+") VALUES("+placeholders+")"
+
+ values = [getTextFirstTagNamed(g, mappings[x]) for x in mappings.keys()]
+ dbc.execute(statement, values)
+ print ("Added grain %s" % getTextFirstTagNamed(g, mappings["name"]))
+
+
+def processData(d, dbc):
+ """
+ Run queries on cursor dbc to insert data for all of the child nodes of d,
+ where each child is an ingredient tag, e.g. <Grain> etc
+ """
+ handlers = {
+ "Grain": processGrain
+ }
+ for t in list(d):
+ # FIXME make non-case-sensitive
+ if t.tag in handlers.keys():
+ handlers[t.tag](t, dbc)
+ else:
+ raise Exception("Processing of " + t.tag + " is not implemented")
+
+
+# unused at the moment
+#ferm_schema = """
+#CREATE TABLE fermentable(
+# id integer PRIMARY KEY autoincrement,
+# -- BeerXML properties
+# name varchar(256) not null DEFAULT '',
+# ftype varchar(32) DEFAULT 'Grain',
+# amount real DEFAULT 0.0,
+# yield real DEFAULT 0.0,
+# color real DEFAULT 0.0,
+# add_after_boil boolean DEFAULT 0,
+# origin varchar(32) DEFAULT '',
+# supplier varchar(256) DEFAULT '',
+# notes text DEFAULT '',
+# coarse_fine_diff real DEFAULT 0.0,
+# moisture real DEFAULT 0.0,
+# diastatic_power real DEFAULT 0.0,
+# protein real DEFAULT 0.0,
+# max_in_batch real DEFAULT 100.0,
+# recommend_mash boolean DEFAULT 0,
+# is_mashed boolean DEFAULT 0,
+# ibu_gal_per_lb real DEFAULT 0.0,
+# -- Display stuff
+# display_unit integer DEFAULT -1,
+# display_scale integer DEFAULT -1,
+# -- meta data
+# deleted boolean DEFAULT 0,
+# display boolean DEFAULT 1,
+# folder varchar(256) DEFAULT ''
+#)
+#"""
+
+# FIXME dirty hack. This needs farming out to some HTML parser which understands
+# these entities. YMMV, you probably need to add some of these depending on your
+# bsmx file you're trying to import
+entities = """
+<!DOCTYPE doctypeName [
+ <!ENTITY deg "°">
+ <!ENTITY rsquo "&quot;">
+ <!ENTITY lsquo "&quot;">
+ <!ENTITY ndash "-">
+]>"""
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 3:
+ print("Syntax: %s source.bsmx target.sqlite" % (sys.argv[0]))
+ sys.exit(1)
+
+ source_file = sys.argv[1]
+ sqlite_file = sys.argv[2]
+
+ # slurp here is related to entities hack
+ with open(source_file) as f:
+ x = f.read()
+
+ # there it is
+ e = xml.etree.ElementTree.fromstring(entities + x)
+
+ db = sqlite3.connect(sqlite_file)
+ dbc = db.cursor()
+
+ # Removed temporarily. Operate instead on the file directly
+ # Longer-term intention is to pump out standalone dbs that can be merged in by
+ # brewtarget itself
+ #dbc.execute(ferm_schema)
+
+ assert(e.tag == "Selections")
+ for t in e:
+ if t.tag == "Size":
+ print("Item count: " + t.text)
+ elif t.tag == "Data":
+ processData(t, dbc)
+ else:
+ # FIXME silently passes on remainder of tags - any other useful info?
+ pass
+
+ dbc.close()
+ db.commit()
+ db.close()