Add with all current work
This commit is contained in:
commit
6010e067e8
|
@ -0,0 +1 @@
|
||||||
|
developers.sqlite
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
|
|
@ -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,
|
|
|
@ -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,
|
|
|
@ -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,
|
|
|
@ -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,
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
chemical,grams,milliliters,makes,note
|
||||||
|
Sodium Sulfite,75,,1000,
|
||||||
|
Sodium Metabisulfite,3.5,,1000,
|
||||||
|
Sodium Isoascorbate,12,,1000,
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rm -rf developers.sqlite
|
||||||
|
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
python3 import.py
|
|
@ -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)
|
||||||
|
);
|
|
@ -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
|
|
Loading…
Reference in New Issue