Add with all current work

This commit is contained in:
mmcwilliams 2023-01-10 21:30:40 -05:00
commit 6010e067e8
13 changed files with 491 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
developers.sqlite

20
common.sh Normal file
View File

@ -0,0 +1,20 @@
#!/bin/bash
#set -e
DATABASE_URL=$(realpath "./developers.sqlite")
createUUID () {
uuidgen | tr '[:upper:]' '[:lower:]'
}
db () {
sqlite3 "${DATABASE_URL}" "${1}"
}
setup () {
cat "./setup.sql" | sqlite3 "${DATABASE_URL}"
echo "Setup ${DATABASE_URL}..."
}
setup

203
import.py Normal file
View File

@ -0,0 +1,203 @@
#!/bin/bash
import sqlite3
import csv
import os
from uuid import uuid4
from os import path
RECIPES='recipes/'
SUPPLY='supply.csv'
con = sqlite3.connect('developers.sqlite')
c = con.cursor()
def uuid () :
return str(uuid4())
def getRecipe (name) :
query="SELECT recipe_id FROM recipes WHERE (name = LOWER(?)) LIMIT 1;"
res = c.execute(query, (name,))
return c.fetchone()
def hasRecipe (name) :
recipe = getRecipe(name)
if recipe is not None:
return recipe[0]
return ''
def createRecipe (name) :
id=uuid()
query="INSERT OR IGNORE INTO recipes (recipe_id, name) VALUES (?, LOWER(?));"
c.execute(query, (id, name,))
con.commit()
return id
def getChemical (chemical) :
query="SELECT chemical_id FROM chemicals WHERE (name = LOWER(?)) LIMIT 1;"
res = c.execute(query, (chemical,))
return c.fetchone()
def hasChemical (chemical) :
chemical = getChemical(chemical)
if chemical is not None:
return chemical[0]
return ''
def createChemical (chemical) :
id=uuid()
query="INSERT OR IGNORE INTO chemicals (chemical_id, name) VALUES (?, LOWER(?));"
c.execute(query, (id, chemical,))
con.commit()
return id
def ensureChemical (chemical) :
chemical_id = hasChemical(chemical)
if chemical_id == '' :
chemical_id = createChemical(chemical)
return chemical_id
def getSupply (url) :
query="SELECT supply_id FROM supply WHERE (url = ?) LIMIT 1;"
res = c.execute(query, (url,))
return c.fetchone()
def hasSupply (url) :
supply = getSupply(url)
if supply is not None:
return supply[0]
return ''
def createSupply (chemical_id, url, g, ml, price) :
id=uuid()
query="INSERT OR IGNORE INTO supply (supply_id,chemical_id,url,grams,milliliters,price) VALUES (?,?,?,?,?,?);"
c.execute(query, (id,chemical_id,url,g,ml,price,))
con.commit()
return id
def ensureSupply (chemical_id, url, g, ml, price) :
supply_id = hasSupply(url)
if supply_id == '' :
supply_id = createSupply(chemical_id, url, g, ml, price)
return supply_id
def displaySupply (supply_id, chemical) :
query="SELECT round((price/100.0)/grams, 2) FROM supply WHERE (supply_id = ?);"
res = c.execute(query, (supply_id,))
data = c.fetchone()
ratio = data[0]
print(f"{chemical}: {ratio} $/g")
def getRecipe (recipe) :
query="SELECT recipe_id FROM recipes WHERE (name = LOWER(?)) LIMIT 1;"
res = c.execute(query, (recipe,))
return c.fetchone()
def hasRecipe (recipe) :
recipe = getRecipe(recipe)
if recipe is not None:
return recipe[0]
return ''
def createComponent (chemical, recipe_id, chemical_id, g, ml, makes, note) :
id = uuid()
query="INSERT OR IGNORE INTO components (component_id,recipe_id,chemical_id,grams,milliliters,makes,note) VALUES (?,?,?,?,?,?,?);"
c.execute(query, (id, recipe_id, chemical_id, g, ml, makes, note))
con.commit()
val=f"{g}g" if g != 'NULL' else f"{ml}ml"
print(f"Added component {chemical} {val}")
def createRecipe (recipe) :
id=uuid()
query="INSERT OR IGNORE INTO recipes (recipe_id, name) VALUES (?, ?);"
c.execute(query, (id, recipe,))
con.commit()
return id
def importRecipe (filePath) :
name = path.basename(filePath).replace('.csv', '').replace('_', ' ')
recipe_id = hasRecipe(name)
if recipe_id != '' :
print(f"Recipe {name} already exists")
return
recipe_id = createRecipe(name)
with open(filePath, newline='') as csvfile:
reader = csv.reader(csvfile, delimiter=',', quotechar='|')
for row in reader:
chemical=row[0]
if chemical == 'chemical' :
continue
g=float(row[1]) if row[1] != '' else 'NULL'
ml=float(row[2]) if row[2] != '' else 'NULL'
makes=int(row[3])
note=row[4]
chemical_id=ensureChemical(chemical)
createComponent(chemical, recipe_id, chemical_id, g, ml, makes, note)
print(f"Imported {name}")
def displayCents (val) :
return '${:,.2f}'.format(val / 100.0)
def displayCost (ml) :
query = "SELECT recipe_id,UPPER(name) FROM recipes ORDER BY name ASC;"
c.execute(query)
rows = c.fetchall()
for row in rows:
recipe = row[1]
print(f"{recipe}: {ml/1000} liters")
query = """SELECT a.name, (c.grams * (s.price/s.grams)) / c.makes, (c.grams/c.makes) FROM components AS c
INNER JOIN supply AS s
ON c.chemical_id = s.chemical_id
INNER JOIN chemicals AS a
ON c.chemical_id = a.chemical_id
WHERE c.recipe_id = ?;"""
c.execute(query, (row[0],))
components = c.fetchall()
total = 0
for component in components:
name = component[0]
price = component[1] * ml
grams = component[2] * ml
total += price
print(f"{name} : [{grams}g] {displayCents(price)}")
print("------")
print(f"Total: {displayCents(total)}")
print("------")
print('-------')
print('SUPPLY')
print('-------')
with open(SUPPLY, newline='') as csvfile:
reader = csv.reader(csvfile, delimiter=',', quotechar='|')
for row in reader:
chemical=row[0]
if chemical == 'chemical' :
continue
url=row[1]
g=float(row[2]) if row[2] != '' else 'NULL'
ml=float(row[3]) if row[3] != '' else 'NULL'
price=int(row[4])
chemical_id = ensureChemical(chemical)
supply_id = ensureSupply(chemical_id, url, g, ml, price)
displaySupply(supply_id, chemical)
print('-------')
print('RECIPES')
print('-------')
for recipe in os.listdir(RECIPES):
if recipe.endswith('.csv') :
filePath=f'{RECIPES}{recipe}'
importRecipe(filePath)
print('----')
print('COST')
print('----')
displayCost(3785) #gallon

179
import.sh Normal file
View File

@ -0,0 +1,179 @@
#!/bin/bash
rm -f ./developers.sqlite
source ./common.sh
RECIPES=./recipes
FILES=$(mktemp)
SUPPLY=$(realpath ./supply.csv)
mkdir -p recipes
hasRecipe () {
query="SELECT recipe_id FROM recipes WHERE (name = '${1}') LIMIT 1;"
db "${query}"
}
createRecipe () {
id=$(createUUID)
name="${1}"
query="INSERT OR IGNORE INTO recipes (recipe_id, name) VALUES ('${id}', '${name}');"
db "${query}"
echo "${id}"
}
hasChemical () {
query="SELECT chemical_id FROM chemicals WHERE (name = '${1}') LIMIT 1;"
db "${query}"
}
createChemical () {
chemical_id="$(createUUID)"
query="INSERT OR IGNORE INTO chemicals (chemical_id, name) VALUES ('${chemical_id}', '${1}');"
db "${query}"
}
chemical () {
chemical_id=$(hasChemical "${1}")
if [[ "${chemical_id}" == "" ]]; then
chemical_id=$(createChemical "${1}");
fi
echo "${chemical_id}"
}
hasSupply () {
query="SELECT supply_id FROM supply WHERE (url = '${1}') LIMIT 1;"
db "${query}"
}
component () {
cols="${1}"
IFS=',' read -ra VALS <<< "${2}"
recipe_id="${3}"
id=$(createUUID)
query="INSERT OR IGNORE INTO components (component_id,recipe_id,${cols}) VALUES ('${id}','${recipe_id}',"
before=""
first=""
for i in "${VALS[@]}"; do
val=NULL
if [[ "${first}" == "" ]]; then
chemical_id=$(chemical "${i}")
val="'${chemical_id}'"
first="completed"
elif [[ "${i}" != "" ]]; then
val="'${i}'"
fi
query="${query}${before}${val}"
before=","
done
query="${query});"
echo "${query}"
db "${query}"
}
import () {
name=$(basename "${1}")
name=${name%.*}
name=$(echo ${name} | sed 's/_/ /g')
recipe_id=$(hasRecipe "${name}")
if [[ "${recipe_id}" != "" ]]; then
echo "${name} recipe already exists."
return
else
recipe_id=$(createRecipe "${name}")
fi
cols="chemical,grams,milliliters,makes"
while read line; do
line=$(echo "${line}" | xargs)
if [[ "${line}" == "chemical"* ]]; then
cols="${line}"
cols="${cols/chemical,/chemical_id,}"
continue
fi
component "${cols}" "${line}" "${recipe_id}"
done < "${1}"
echo "Imported ${name}."
}
list () {
echo "RECIPES:"
db "SELECT name FROM recipes ORDER BY name DESC;"
echo "CHEMICALS:"
db "SELECT name FROM chemicals ORDER BY name DESC"
}
supply () {
line=$(echo "${1}" | xargs)
first=""
before=""
if [[ "${line}" == "chemical,"* ]]; then
return
fi
chemical=$(echo "${line}" | awk -F',' '{print $1}')
url=$(echo "${line}" | awk -F',' '{print $2}')
chemical_id=$(chemical "${chemical}")
has_supply=$(hasSupply "${url}")
if [[ "${has_supply}" != "" ]]; then
return
fi
supply_id=$(createUUID)
query="INSERT OR IGNORE INTO supply (supply_id,chemical_id,url,grams,milliliters,price) VALUES ('${supply_id}','${chemical_id}',"
IFS=',' read -ra VALS <<< "${line}"
for i in "${VALS[@]}"; do
if [[ "${first}" == "" ]]; then
first="complete"
continue
fi
val=NULL
if [[ "${i}" != "" ]]; then
val="'${i}'"
fi
query="${query}${before}${val}"
before=","
done
query="${query});"
#echo "${query}"
db "${query}"
ratio=$(db "SELECT round((price/100.0)/grams, 2) FROM supply WHERE (supply_id = '${supply_id}');")
echo "${chemical}: ${ratio} $/g"
}
priceRecipe () {
recipesList=$(mktemp)
query="SELECT r.recipe_id FROM recipes AS r ORDER BY r.name;"
db "${query}" > "${recipesList}"
while read id; do
db "SELECT name FROM recipes WHERE recipe_id = '${id}' LIMIT 1;"
echo "---"
db "SELECT COUNT(*) FROM components WHERE recipe_id = '${id}';"
query="SELECT ch.name FROM components AS c
INNER JOIN chemicals AS ch ON ch.chemical_id = c.chemical_id
WHERE c.recipe_id = '${id}';"
db "${query}"
echo " "
done < "${recipesList}"
}
ls -1 "${RECIPES}/"*.csv > "${FILES}"
while read file; do
import "${file}"
done < "${FILES}"
echo "---"
while read line; do
supply "${line}"
done < "${SUPPLY}"
echo "---"
priceRecipe
#list

View File

@ -0,0 +1,7 @@
chemical,grams,milliliters,makes,note
Water,,750,1000,
Metol,2,,1000,
Sodium Sulfite (anhydrous),80,,1000,
Hydroquinone,4,,1000,
Borax,4,,1000,
Potassium Bromide,0.5,,1000,
1 chemical grams milliliters makes note
2 Water 750 1000
3 Metol 2 1000
4 Sodium Sulfite (anhydrous) 80 1000
5 Hydroquinone 4 1000
6 Borax 4 1000
7 Potassium Bromide 0.5 1000

8
recipes/FX-11.csv Normal file
View File

@ -0,0 +1,8 @@
chemical,grams,milliliters,makes,note
Water,,700,1000,
Phenidone,0.25,,1000,
Hydroquinone,5,,1000,
Glycin,1.5,,1000,
Sodium Sulfite (anhydrous),125,,1000,
Borax,2.5,,1000,granular
Potassium Bromide,0.5,,1000,
1 chemical grams milliliters makes note
2 Water 700 1000
3 Phenidone 0.25 1000
4 Hydroquinone 5 1000
5 Glycin 1.5 1000
6 Sodium Sulfite (anhydrous) 125 1000
7 Borax 2.5 1000 granular
8 Potassium Bromide 0.5 1000

7
recipes/Kodak_D-19.csv Normal file
View File

@ -0,0 +1,7 @@
chemical,grams,milliliters,makes,note
Water,,500,1000,
Metol,2,,1000,
Sodium Sulfite (anhydrous),90,,1000,
Sodium Carbonate (monohydrate),52.5,,1000,
Hydroquinone,8,,1000,
Potassium Bromide,5,,1000,
1 chemical grams milliliters makes note
2 Water 500 1000
3 Metol 2 1000
4 Sodium Sulfite (anhydrous) 90 1000
5 Sodium Carbonate (monohydrate) 52.5 1000
6 Hydroquinone 8 1000
7 Potassium Bromide 5 1000

6
recipes/Kodak_D-76.csv Normal file
View File

@ -0,0 +1,6 @@
chemical,grams,milliliters,makes,note
Water,,750,1000,
Metol,2,,1000,
Sodium Sulfite (anhydrous),100,,1000,
Hydroquinone,5,,1000,
Borax,2,,1000,
1 chemical grams milliliters makes note
2 Water 750 1000
3 Metol 2 1000
4 Sodium Sulfite (anhydrous) 100 1000
5 Hydroquinone 5 1000
6 Borax 2 1000

6
recipes/Kodak_Xtol_A.csv Normal file
View File

@ -0,0 +1,6 @@
chemical,grams,milliliters,makes,note
Water,,850,1000,
Sodium Sulfite,10,,1000,
Diethylenetriaminepentaacetic Acid Pentasodium Salt,1,,1000,
Sodium Metaborate,4.0,,100,(8 mol)
4-Hydroxymethyl-4-Methyl-1-Phenyl-3-Pyrazolidione,0.2,,1000,
1 chemical grams milliliters makes note
2 Water 850 1000
3 Sodium Sulfite 10 1000
4 Diethylenetriaminepentaacetic Acid Pentasodium Salt 1 1000
5 Sodium Metaborate 4.0 100 (8 mol)
6 4-Hydroxymethyl-4-Methyl-1-Phenyl-3-Pyrazolidione 0.2 1000

4
recipes/Kodak_Xtol_B.csv Normal file
View File

@ -0,0 +1,4 @@
chemical,grams,milliliters,makes,note
Sodium Sulfite,75,,1000,
Sodium Metabisulfite,3.5,,1000,
Sodium Isoascorbate,12,,1000,
1 chemical grams milliliters makes note
2 Sodium Sulfite 75 1000
3 Sodium Metabisulfite 3.5 1000
4 Sodium Isoascorbate 12 1000

7
run.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
rm -rf developers.sqlite
source common.sh
python3 import.py

31
setup.sql Normal file
View File

@ -0,0 +1,31 @@
CREATE TABLE IF NOT EXISTS chemicals (
chemical_id CHAR(36) PRIMARY KEY,
name TEXT UNIQUE
);
CREATE TABLE IF NOT EXISTS recipes (
recipe_id CHAR(36) PRIMARY KEY,
name TEXT UNIQUE
);
CREATE TABLE IF NOT EXISTS components (
component_id CHAR(36) PRIMARY KEY,
recipe_id CHAR(36),
chemical_id CHAR(36),
grams REAL,
milliliters REAL,
makes REAL,
note TEXT,
CONSTRAINT fk_recipes FOREIGN KEY (recipe_id) REFERENCES recipes(recipe_id),
CONSTRAINT fk_chemicals FOREIGN KEY (chemical_id) REFERENCES chemicals(chemical_id)
);
CREATE TABLE IF NOT EXISTS supply (
supply_id CHAR(36) PRIMARY KEY,
chemical_id CHAR(36),
url TEXT UNIQUE,
grams REAL,
milliliters REAL,
price INTEGER,
CONSTRAINT fk_chemicals FOREIGN KEY (chemical_id) REFERENCES chemicals(chemical_id)
);

12
supply.csv Normal file
View File

@ -0,0 +1,12 @@
chemical,url,grams,milliliters,price
Sodium Sulfite (anhydrous),https://www.bhphotovideo.com/c/product/124092-REG/Photographers_Formulary_10_1340_1LB_Sodium_Sulfite_1.html,453.592,,695
Sodium Carbonate (monohydrate),https://www.bhphotovideo.com/c/product/124044-REG/Photographers_Formulary_10_1190_1LB_Sodium_Carbonate_Monohydrate.html,453.592,,995
Potassium Bromide,https://www.bhphotovideo.com/c/product/123938-REG/Photographers_Formulary_10_0930_1LB_Potassium_Bromide_1.html,453.592,,2195
Phenidone,https://www.bhphotovideo.com/c/product/123915-REG/Photographers_Formulary_10_0870_100G_Phenidone_100_Grams.html,100,,2695
Metol,https://www.bhphotovideo.com/c/product/123903-REG/Photographers_Formulary_10_0770_100G_Metol_Elon_100.html,100,,1695
Hydroquinone,https://www.bhphotovideo.com/c/product/123903-REG/Photographers_Formulary_10_0770_100G_Metol_Elon_100.html,100,,1695
Glycin,https://www.bhphotovideo.com/c/product/123868-REG/Photographers_Formulary_10_0610_100G_Glycin_N_Parahydroxyphenyl.html,100,,2395
Borax,https://www.bhphotovideo.com/c/product/123755-REG/Photographers_Formulary_10_0260_1LB_Borax_1_lb.html,453.592,,695
Hydroquinone,https://www.bhphotovideo.com/c/product/123887-REG/Photographers_Formulary_10_0670_100G_Hydroquinone_100_Grams.html,100,,895
Sodium Metabisulfite,https://www.bhphotovideo.com/c/product/124063-REG/Photographers_Formulary_10_1280_100G_Sodium_Metabisulfite_100g.html,100,,595
Sodium Metaborate,https://www.bhphotovideo.com/c/product/124068-REG/Photographers_Formulary_10_1285_1LB_Sodium_Metaborate_Balanced_Alkali.html,453.592,,1195
1 chemical url grams milliliters price
2 Sodium Sulfite (anhydrous) https://www.bhphotovideo.com/c/product/124092-REG/Photographers_Formulary_10_1340_1LB_Sodium_Sulfite_1.html 453.592 695
3 Sodium Carbonate (monohydrate) https://www.bhphotovideo.com/c/product/124044-REG/Photographers_Formulary_10_1190_1LB_Sodium_Carbonate_Monohydrate.html 453.592 995
4 Potassium Bromide https://www.bhphotovideo.com/c/product/123938-REG/Photographers_Formulary_10_0930_1LB_Potassium_Bromide_1.html 453.592 2195
5 Phenidone https://www.bhphotovideo.com/c/product/123915-REG/Photographers_Formulary_10_0870_100G_Phenidone_100_Grams.html 100 2695
6 Metol https://www.bhphotovideo.com/c/product/123903-REG/Photographers_Formulary_10_0770_100G_Metol_Elon_100.html 100 1695
7 Hydroquinone https://www.bhphotovideo.com/c/product/123903-REG/Photographers_Formulary_10_0770_100G_Metol_Elon_100.html 100 1695
8 Glycin https://www.bhphotovideo.com/c/product/123868-REG/Photographers_Formulary_10_0610_100G_Glycin_N_Parahydroxyphenyl.html 100 2395
9 Borax https://www.bhphotovideo.com/c/product/123755-REG/Photographers_Formulary_10_0260_1LB_Borax_1_lb.html 453.592 695
10 Hydroquinone https://www.bhphotovideo.com/c/product/123887-REG/Photographers_Formulary_10_0670_100G_Hydroquinone_100_Grams.html 100 895
11 Sodium Metabisulfite https://www.bhphotovideo.com/c/product/124063-REG/Photographers_Formulary_10_1280_100G_Sodium_Metabisulfite_100g.html 100 595
12 Sodium Metaborate https://www.bhphotovideo.com/c/product/124068-REG/Photographers_Formulary_10_1285_1LB_Sodium_Metaborate_Balanced_Alkali.html 453.592 1195