Inventory diff
Two stock counts compared in both directions: added, gone, changed and renamed items, an item very likely moved to a new code, a units balance, and what a one-way check never sees.
Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.
About
Two stock counts, last month's and this month's, each item a code, a name and a quantity, compared in both directions.
main.eml- the menu, both counts side by side, the differences, the one-way checks, and changes to the new countstock.eml- the two counts, codes in order, the differences, items very likely moved to a new code, the units balance, and the changes
How each part works:
- As in the corpus case
inventory-diff-report, a difference has two directions: what is new now, and what is gone since. Checking each item now against before finds the added and the changed, and never the gone; checking before against now never finds the added. - Items on both counts are changed (another quantity), renamed (another name under the same code), or the same. A quantity of 0 is still on the list: counted and empty, not gone.
- The same item counted under a new code shows up as one gone and one added. When a gone item and an added one have the same name and quantity, the program says it is very likely one item.
- Units now - units before = added - gone + the change on the items in both: 537 - 561 = 42 - 17 - 49.
- As in the corpus case
dict-inventory-report, the counts are dicts keyed by code, always reported in sorted order.
What is checked: menu choices 1 to 5; a code of a capital letter and 3 digits; a quantity from 0 to 99999, or remove for an item on the new count; a name of up to 20 lowercase letters and spaces for a new one. An empty answer cancels.
Sessions: sessions/basic.in gives: - both counts, the differences and the one-way checks; - B205 taken off and B201 added back, after which brackets are the same item again; - glue from 0 to 4, the differences and the one-way checks.
sessions/bad-input.in gives: - menu choices 0 and x; - codes: a cancel, d400, D40 and DD400; - quantities for D400: a cancel, x, 100000 and -3; - a new code Z999: a cancel, Tape, a name too long, then tape with a cancel and remove, tape 2, and masking tape 7; - the differences.
Built on the verified corpus cases inventory-diff-report (a difference taken in both directions) and dict-inventory-report (counts in a dict, reported in a fixed order).
Recorded sessions
What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.
bad-input
interpreter: byte-equal== Inventory diff ==
Two stock counts, last month's and this month's, compared in both directions.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 0
Pick a number from 1 to 5.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> x
Pick a number from 1 to 5.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)>
Cancelled.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> d400
Type a capital letter and 3 digits, such as D400.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> D40
Type a capital letter and 3 digits, such as D400.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> DD400
Type a capital letter and 3 digits, such as D400.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> D400
quantity (0 to 99999, or remove)>
Cancelled.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> D400
quantity (0 to 99999, or remove)> x
Type a whole number from 0 to 99999, or remove.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> D400
quantity (0 to 99999, or remove)> 100000
Type a whole number from 0 to 99999, or remove.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> D400
quantity (0 to 99999, or remove)> -3
Type a whole number from 0 to 99999, or remove.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> Z999
name (up to 20 lowercase letters and spaces)>
Cancelled.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> Z999
name (up to 20 lowercase letters and spaces)> Tape
Type up to 20 lowercase letters and spaces.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> Z999
name (up to 20 lowercase letters and spaces)> a very long name indeed yes
Type up to 20 lowercase letters and spaces.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> Z999
name (up to 20 lowercase letters and spaces)> tape
quantity (0 to 99999, or remove)>
Cancelled.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> Z999
name (up to 20 lowercase letters and spaces)> tape
quantity (0 to 99999, or remove)> remove
Z999 is not on the new count.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> Z999
name (up to 20 lowercase letters and spaces)> tape 2
Type up to 20 lowercase letters and spaces.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> Z999
name (up to 20 lowercase letters and spaces)> masking tape
quantity (0 to 99999, or remove)> 7
Z999 masking tape added to the new count, 7.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 2
Differences, before to now:
added: B205 brackets 12
added: D400 tape 30
added: Z999 masking tape 7
gone: B201 brackets 12
gone: C301 sealant 5
changed: A101 nuts 300 -> 260 (-40)
changed: C300 glue 9 -> 0 (-9)
C300 is still on the list, at 0: counted and empty, not gone.
renamed: B200 hinges -> door hinges
the same: 2 items
Very likely one item under a new code: B201 brackets is gone and B205 brackets added, with the same name and quantity (12).
Units: 561 before, 544 now: +49 added, -17 gone, -49 on the items in both.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 5
Bye.
What was typed (48 lines)
0
x
4
4
d400
4
D40
4
DD400
4
D400
4
D400
x
4
D400
100000
4
D400
-3
4
Z999
4
Z999
Tape
4
Z999
a very long name indeed yes
4
Z999
tape
4
Z999
tape
remove
4
Z999
tape 2
4
Z999
masking tape
7
2
5
basic
interpreter: byte-equal== Inventory diff ==
Two stock counts, last month's and this month's, compared in both directions.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 1
code before now
A100 bolts 120 bolts 120
A101 nuts 300 nuts 260
A102 washers 75 washers 75
B200 hinges 40 door hinges 40
B201 brackets 12 -
B205 - brackets 12
C300 glue 9 glue 0
C301 sealant 5 -
D400 - tape 30
7 items before, 7 now.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 2
Differences, before to now:
added: B205 brackets 12
added: D400 tape 30
gone: B201 brackets 12
gone: C301 sealant 5
changed: A101 nuts 300 -> 260 (-40)
changed: C300 glue 9 -> 0 (-9)
C300 is still on the list, at 0: counted and empty, not gone.
renamed: B200 hinges -> door hinges
the same: 2 items
Very likely one item under a new code: B201 brackets is gone and B205 brackets added, with the same name and quantity (12).
Units: 561 before, 537 now: +42 added, -17 gone, -49 on the items in both.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 3
Checking each item now against before finds B205, D400, A101 and C300 - and never what is gone: B201 and C301.
Checking each item before against now finds B201, C301, A101 and C300 - and never what is new: B205 and D400.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> B205
quantity (0 to 99999, or remove)> remove
B205 brackets taken off the new count.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> B201
name (up to 20 lowercase letters and spaces)> brackets
quantity (0 to 99999, or remove)> 12
B201 brackets added to the new count, 12.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 2
Differences, before to now:
added: D400 tape 30
gone: C301 sealant 5
changed: A101 nuts 300 -> 260 (-40)
changed: C300 glue 9 -> 0 (-9)
C300 is still on the list, at 0: counted and empty, not gone.
renamed: B200 hinges -> door hinges
the same: 3 items
Units: 561 before, 537 now: +30 added, -5 gone, -49 on the items in both.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 4
code (a capital letter and 3 digits, such as D400)> C300
quantity (0 to 99999, or remove)> 4
C300 glue: 0 -> 4.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 2
Differences, before to now:
added: D400 tape 30
gone: C301 sealant 5
changed: A101 nuts 300 -> 260 (-40)
changed: C300 glue 9 -> 4 (-5)
renamed: B200 hinges -> door hinges
the same: 3 items
Units: 561 before, 541 now: +30 added, -5 gone, -45 on the items in both.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 3
Checking each item now against before finds D400, A101 and C300 - and never what is gone: C301.
Checking each item before against now finds C301, A101 and C300 - and never what is new: D400.
1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit
choice> 5
Bye.
What was typed (17 lines)
1
2
3
4
B205
remove
4
B201
brackets
12
2
4
C300
4
2
3
5
Modules
The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.
main.eml(entry)
eml# P123 inventory diff: two stock counts side by side, the differences in
# both directions, what a one-way check misses, and changes to the new
# count.
import stock
def plain(s):
0 => i
len(s) => j
while i < j and s[i] == " ":
i + 1 => i
while j > i and s[j - 1] == " ":
j - 1 => j
return s[i:j]
def pad(s, n):
s => out
while len(out) < n:
out + " " => out
return out
def lpad(s, n):
s => out
while len(out) < n:
" " + out => out
return out
def names_text(ns):
"" => out
for i in [0:len(ns) - 1]:
if i > 0 and i == len(ns) - 1:
out + " and " => out
elif i > 0:
out + ", " => out
out + ns[i] => out
return out
def signed(n):
if n > 0:
return "+" + str(n)
return str(n)
def item_text(count, code):
return code + " " + count[code][0] + " " + str(count[code][1])
def show(before, now):
" code before now" ^0
for code in all_codes(before, now):
"-" => a
"-" => b
if code in before:
before[code][0] + " " + str(before[code][1]) => a
if code in now:
now[code][0] + " " + str(now[code][1]) => b
(" " + code + " " + pad(a, 22) + b) ^0
(" " + str(len(before)) + " items before, " + str(len(now)) + " now.") ^0
def all_codes(before, now):
{} => both
for c in before:
1 => both[c]
for c in now:
1 => both[c]
return stock.sorted_codes(both)
def differences(before, now):
stock.diff(before, now) => d
"Differences, before to now:" ^0
if len(d["added"]) == 0:
" added: none" ^0
for code in d["added"]:
(" added: " + item_text(now, code)) ^0
if len(d["gone"]) == 0:
" gone: none" ^0
for code in d["gone"]:
(" gone: " + item_text(before, code)) ^0
for code in d["changed"]:
now[code][1] - before[code][1] => delta
(" changed: " + code + " " + now[code][0] + " " + str(before[code][1]) + " -> " + str(now[code][1]) + " (" + signed(delta) + ")") ^0
if now[code][1] == 0:
(" " + code + " is still on the list, at 0: counted and empty, not gone.") ^0
for code in d["renamed"]:
(" renamed: " + code + " " + before[code][0] + " -> " + now[code][0]) ^0
(" the same: " + str(len(d["same"])) + " items") ^0
for p in stock.moved(before, now, d):
("Very likely one item under a new code: " + p[0] + " " + before[p[0]][0] + " is gone and " + p[1] + " " + now[p[1]][0] + " added, with the same name and quantity (" + str(now[p[1]][1]) + ").") ^0
stock.balance(before, now, d) => b
("Units: " + str(b[0]) + " before, " + str(b[1]) + " now: " + signed(b[2]) + " added, " + signed(0 - b[3]) + " gone, " + signed(b[4]) + " on the items in both.") ^0
def one_way(before, now):
stock.diff(before, now) => d
[] => found
for code in d["added"]:
found + [code] => found
for code in d["changed"]:
found + [code] => found
("Checking each item now against before finds " + list_or_none(found) + " - and never what is gone: " + list_or_none(d["gone"]) + ".") ^0
[] => found2
for code in d["gone"]:
found2 + [code] => found2
for code in d["changed"]:
found2 + [code] => found2
("Checking each item before against now finds " + list_or_none(found2) + " - and never what is new: " + list_or_none(d["added"]) + ".") ^0
def list_or_none(xs):
if len(xs) == 0:
return "nothing"
return names_text(xs)
def good_code(c):
if len(c) != 4 or c[0] < "A" or c[0] > "Z":
return False
for k in [1:3]:
if c[k] < "0" or c[k] > "9":
return False
return True
def ask_qty():
# the quantity, -1 to cancel or refuse, -2 to remove
plain(input("quantity (0 to 99999, or remove)> ")) => t
if t == "":
"Cancelled." ^0
return 0 - 1
if t == "remove":
return 0 - 2
if len(t) == 0 or len(t) > 5:
"Type a whole number from 0 to 99999, or remove." ^0
return 0 - 1
for c in t:
if c < "0" or c > "9":
"Type a whole number from 0 to 99999, or remove." ^0
return 0 - 1
return int(t)
def change(now):
plain(input("code (a capital letter and 3 digits, such as D400)> ")) => code
if code == "":
"Cancelled." ^0
return now
if not good_code(code):
"Type a capital letter and 3 digits, such as D400." ^0
return now
if code in now:
ask_qty() => q
if q == 0 - 1:
return now
if q == 0 - 2:
(code + " " + now[code][0] + " taken off the new count.") ^0
return stock.remove_item(now, code)
(code + " " + now[code][0] + ": " + str(now[code][1]) + " -> " + str(q) + ".") ^0
return stock.set_item(now, code, now[code][0], q)
plain(input("name (up to 20 lowercase letters and spaces)> ")) => name
if name == "":
"Cancelled." ^0
return now
if len(name) > 20:
"Type up to 20 lowercase letters and spaces." ^0
return now
for c in name:
if not ((c >= "a" and c <= "z") or c == " "):
"Type up to 20 lowercase letters and spaces." ^0
return now
ask_qty() => q
if q == 0 - 1:
return now
if q == 0 - 2:
(code + " is not on the new count.") ^0
return now
(code + " " + name + " added to the new count, " + str(q) + ".") ^0
return stock.set_item(now, code, name, q)
stock.before_count() => before
stock.now_count() => now
"== Inventory diff ==" ^0
"Two stock counts, last month's and this month's, compared in both directions." ^0
True => running
while running:
"" ^0
"1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit" ^0
plain(input("choice> ")) => choice
if choice == "1":
show(before, now)
elif choice == "2":
differences(before, now)
elif choice == "3":
one_way(before, now)
elif choice == "4":
change(now) => now
elif choice == "5":
False => running
else:
"Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import stock
def plain(s):
i = 0
j = len(s)
while i < j and s[i] == " ":
i = i + 1
while j > i and s[j - 1] == " ":
j = j - 1
return s[i:j]
def pad(s, n):
out = s
while len(out) < n:
out = out + " "
return out
def lpad(s, n):
out = s
while len(out) < n:
out = " " + out
return out
def names_text(ns):
out = ""
for i in range(0, len(ns)):
if i > 0 and i == len(ns) - 1:
out = out + " and "
elif i > 0:
out = out + ", "
out = out + ns[i]
return out
def signed(n):
if n > 0:
return "+" + str(n)
return str(n)
def item_text(count, code):
return code + " " + count[code][0] + " " + str(count[code][1])
def show(before, now):
print(" code before now")
for code in all_codes(before, now):
a = "-"
b = "-"
if code in before:
a = before[code][0] + " " + str(before[code][1])
if code in now:
b = now[code][0] + " " + str(now[code][1])
print(" " + code + " " + pad(a, 22) + b)
print(" " + str(len(before)) + " items before, " + str(len(now)) + " now.")
def all_codes(before, now):
both = {}
for c in before:
both[c] = 1
for c in now:
both[c] = 1
return stock.sorted_codes(both)
def differences(before, now):
d = stock.diff(before, now)
print("Differences, before to now:")
if len(d["added"]) == 0:
print(" added: none")
for code in d["added"]:
print(" added: " + item_text(now, code))
if len(d["gone"]) == 0:
print(" gone: none")
for code in d["gone"]:
print(" gone: " + item_text(before, code))
for code in d["changed"]:
delta = now[code][1] - before[code][1]
print(" changed: " + code + " " + now[code][0] + " " + str(before[code][1]) + " -> " + str(now[code][1]) + " (" + signed(delta) + ")")
if now[code][1] == 0:
print(" " + code + " is still on the list, at 0: counted and empty, not gone.")
for code in d["renamed"]:
print(" renamed: " + code + " " + before[code][0] + " -> " + now[code][0])
print(" the same: " + str(len(d["same"])) + " items")
for p in stock.moved(before, now, d):
print("Very likely one item under a new code: " + p[0] + " " + before[p[0]][0] + " is gone and " + p[1] + " " + now[p[1]][0] + " added, with the same name and quantity (" + str(now[p[1]][1]) + ").")
b = stock.balance(before, now, d)
print("Units: " + str(b[0]) + " before, " + str(b[1]) + " now: " + signed(b[2]) + " added, " + signed(0 - b[3]) + " gone, " + signed(b[4]) + " on the items in both.")
def one_way(before, now):
d = stock.diff(before, now)
found = []
for code in d["added"]:
found = found + [code]
for code in d["changed"]:
found = found + [code]
print("Checking each item now against before finds " + list_or_none(found) + " - and never what is gone: " + list_or_none(d["gone"]) + ".")
found2 = []
for code in d["gone"]:
found2 = found2 + [code]
for code in d["changed"]:
found2 = found2 + [code]
print("Checking each item before against now finds " + list_or_none(found2) + " - and never what is new: " + list_or_none(d["added"]) + ".")
def list_or_none(xs):
if len(xs) == 0:
return "nothing"
return names_text(xs)
def good_code(c):
if len(c) != 4 or c[0] < "A" or c[0] > "Z":
return False
for k in range(1, 4):
if c[k] < "0" or c[k] > "9":
return False
return True
def ask_qty():
t = plain(input("quantity (0 to 99999, or remove)> "))
if t == "":
print("Cancelled.")
return 0 - 1
if t == "remove":
return 0 - 2
if len(t) == 0 or len(t) > 5:
print("Type a whole number from 0 to 99999, or remove.")
return 0 - 1
for c in t:
if c < "0" or c > "9":
print("Type a whole number from 0 to 99999, or remove.")
return 0 - 1
return int(t)
def change(now):
code = plain(input("code (a capital letter and 3 digits, such as D400)> "))
if code == "":
print("Cancelled.")
return now
if not good_code(code):
print("Type a capital letter and 3 digits, such as D400.")
return now
if code in now:
q = ask_qty()
if q == 0 - 1:
return now
if q == 0 - 2:
print(code + " " + now[code][0] + " taken off the new count.")
return stock.remove_item(now, code)
print(code + " " + now[code][0] + ": " + str(now[code][1]) + " -> " + str(q) + ".")
return stock.set_item(now, code, now[code][0], q)
name = plain(input("name (up to 20 lowercase letters and spaces)> "))
if name == "":
print("Cancelled.")
return now
if len(name) > 20:
print("Type up to 20 lowercase letters and spaces.")
return now
for c in name:
if not (c >= "a" and c <= "z" or c == " "):
print("Type up to 20 lowercase letters and spaces.")
return now
q = ask_qty()
if q == 0 - 1:
return now
if q == 0 - 2:
print(code + " is not on the new count.")
return now
print(code + " " + name + " added to the new count, " + str(q) + ".")
return stock.set_item(now, code, name, q)
before = stock.before_count()
now = stock.now_count()
print("== Inventory diff ==")
print("Two stock counts, last month's and this month's, compared in both directions.")
running = True
while running:
print("")
print("1) both counts 2) differences 3) one-way checks 4) change the new count 5) quit")
choice = plain(input("choice> "))
if choice == "1":
show(before, now)
elif choice == "2":
differences(before, now)
elif choice == "3":
one_way(before, now)
elif choice == "4":
now = change(now)
elif choice == "5":
running = False
else:
print("Pick a number from 1 to 5.")
print("Bye.")
stock.eml
eml# P123 inventory diff - two stock counts, each item a code, a name and a
# quantity. As in the corpus case inventory-diff-report, the difference
# has two directions: what is new now and what is gone since, and a report
# that asks only one of them misses the other. As in dict-inventory-report,
# the counts are dicts keyed by code, reported in sorted order. A quantity
# of 0 is still on the list; an item off the list is gone.
#
# A count is {code: [name, quantity]}.
def before_count():
return {"A100": ["bolts", 120], "A101": ["nuts", 300], "A102": ["washers", 75], "B200": ["hinges", 40], "B201": ["brackets", 12], "C300": ["glue", 9], "C301": ["sealant", 5]}
def now_count():
return {"A100": ["bolts", 120], "A101": ["nuts", 260], "A102": ["washers", 75], "B200": ["door hinges", 40], "B205": ["brackets", 12], "C300": ["glue", 0], "D400": ["tape", 30]}
def sorted_codes(count):
# the codes in order, by insertion into a growing sorted list
[] => out
for code in count:
[] => nxt
False => placed
for c in out:
if not placed and code < c:
nxt + [code] => nxt
True => placed
nxt + [c] => nxt
if not placed:
nxt + [code] => nxt
nxt => out
return out
def only_in(a, b):
# the codes in a and not in b, in order
[] => out
for code in sorted_codes(a):
if not (code in b):
out + [code] => out
return out
def diff(before, now):
# {"added", "gone", "changed", "renamed", "same"}: lists of codes
[] => changed
[] => renamed
[] => same
for code in sorted_codes(now):
if code in before:
if before[code][1] != now[code][1]:
changed + [code] => changed
if before[code][0] != now[code][0]:
renamed + [code] => renamed
if before[code][1] == now[code][1] and before[code][0] == now[code][0]:
same + [code] => same
return {"added": only_in(now, before), "gone": only_in(before, now), "changed": changed, "renamed": renamed, "same": same}
def moved(before, now, d):
# pairs [gone code, added code] with the same name and quantity: very
# likely one item counted under a new code
[] => out
for g in d["gone"]:
for a in d["added"]:
if before[g][0] == now[a][0] and before[g][1] == now[a][1]:
out + [[g, a]] => out
return out
def units(count):
0 => n
for code in count:
n + count[code][1] => n
return n
def balance(before, now, d):
# [units before, now, added, gone, change in the items on both]
0 => added
for code in d["added"]:
added + now[code][1] => added
0 => gone
for code in d["gone"]:
gone + before[code][1] => gone
0 => change
for code in d["changed"]:
change + now[code][1] - before[code][1] => change
return [units(before), units(now), added, gone, change]
def copy_count(count):
{} => out
for code in count:
[count[code][0], count[code][1]] => out[code]
return out
def set_item(count, code, name, qty):
copy_count(count) => out
[name, qty] => out[code]
return out
def remove_item(count, code):
{} => out
for c in count:
if c != code:
[count[c][0], count[c][1]] => out[c]
return out
Python projection (stock.py)
def before_count():
return {"A100": ["bolts", 120], "A101": ["nuts", 300], "A102": ["washers", 75], "B200": ["hinges", 40], "B201": ["brackets", 12], "C300": ["glue", 9], "C301": ["sealant", 5]}
def now_count():
return {"A100": ["bolts", 120], "A101": ["nuts", 260], "A102": ["washers", 75], "B200": ["door hinges", 40], "B205": ["brackets", 12], "C300": ["glue", 0], "D400": ["tape", 30]}
def sorted_codes(count):
out = []
for code in count:
nxt = []
placed = False
for c in out:
if not placed and code < c:
nxt = nxt + [code]
placed = True
nxt = nxt + [c]
if not placed:
nxt = nxt + [code]
out = nxt
return out
def only_in(a, b):
out = []
for code in sorted_codes(a):
if not code in b:
out = out + [code]
return out
def diff(before, now):
changed = []
renamed = []
same = []
for code in sorted_codes(now):
if code in before:
if before[code][1] != now[code][1]:
changed = changed + [code]
if before[code][0] != now[code][0]:
renamed = renamed + [code]
if before[code][1] == now[code][1] and before[code][0] == now[code][0]:
same = same + [code]
return {"added": only_in(now, before), "gone": only_in(before, now), "changed": changed, "renamed": renamed, "same": same}
def moved(before, now, d):
out = []
for g in d["gone"]:
for a in d["added"]:
if before[g][0] == now[a][0] and before[g][1] == now[a][1]:
out = out + [[g, a]]
return out
def units(count):
n = 0
for code in count:
n = n + count[code][1]
return n
def balance(before, now, d):
added = 0
for code in d["added"]:
added = added + now[code][1]
gone = 0
for code in d["gone"]:
gone = gone + before[code][1]
change = 0
for code in d["changed"]:
change = change + now[code][1] - before[code][1]
return [units(before), units(now), added, gone, change]
def copy_count(count):
out = {}
for code in count:
out[code] = [count[code][0], count[code][1]]
return out
def set_item(count, code, name, qty):
out = copy_count(count)
out[code] = [name, qty]
return out
def remove_item(count, code):
out = {}
for c in count:
if c != code:
out[c] = [count[c][0], count[c][1]]
return out