├── .gitignore
├── concatenate_cronometer.sh
├── barcodewrite.sh
├── inventory_modifyname.py
├── inventory_xmlout.py
├── inventory_printout.py
├── inventory_modifyexpiry.py
├── inventory_grabrecipes.py
├── inventory
├── inventory_remove.py
├── inventory_addname.py
├── inventory_daily.py
├── inventory_add.py
├── README.md
├── inventory_randomjson.py
├── inventory_grabcronometer.py
├── inventory_add_and_remove.py
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 |
--------------------------------------------------------------------------------
/concatenate_cronometer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "" > complete_cronometer_data.xml
4 | cat ../CRONOMETER-data/foods/*.xml >> complete_cronometer_data.xml
5 | echo "" >> complete_cronometer_data.xml
6 |
--------------------------------------------------------------------------------
/barcodewrite.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #Generates a PDF of barcodes for identifying assorted fruit and vegetables.
4 | barcode -t 3x5 -m 35,35 -b 50134567890 -b 50134567891 -b 50134567892 -b 50134567893 -b 50134567894 -b 50134567895 -b 50134567896 -b 50134567897 -b 50134567898 -b 50134567899 -b 50134567800 -b 50134567801 -b 50134567802 -b 50134567803 -b 50134567804 -b 50134567805 -b 50134567806 -b 50134567807 -b 50134567808 -b 50134567809 -b 50134567810 -b 50134567811 -b 50134567812 -b 50134567813 -b 50134567814 -b 50134567815 -b 50134567816 -b 50134567817 -b 50134567818 -b 50134567819 -b 50134567820 -b 50134567821 -b 50134567822 -b 50134567823 -b 50134567824 -b 50134567825 -b 50134567826 -b 50134567827 -b 50134567828 -b 50134567829 -b 50134567830 -b 50134567831 -b 50134567832 -b 50134567833 -b 50134567834 -e UPC -o bcodetest.ps
5 |
--------------------------------------------------------------------------------
/inventory_modifyname.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import pickle
4 | import sys
5 |
6 | def main():
7 | file = open('inventory.inv', 'r')
8 | inventoryarr = pickle.load(file)
9 | file.close()
10 |
11 | while True:
12 | print "Enter the EAN code of an item you wish to modify."
13 | linetmp = sys.stdin.readline()
14 | if not linetmp:
15 | break
16 | #line = linetmp.rstrip()[7:]
17 | line = linetmp.rstrip()
18 | try:
19 | tmp = inventoryarr[line]
20 | except:
21 | print "This EAN doesn't exist in the database."
22 | break
23 | print "Please enter the name for this item (in days):"
24 | length = sys.stdin.readline()
25 | if length:
26 | #predate = inventoryarr[line][0][1]
27 | inventoryarr[line][0][1]=length
28 | print "Name updated to " + length.rstrip() + "."
29 |
30 |
31 | file = open('inventory.inv', 'w')
32 | pickle.dump(inventoryarr,file)
33 | file.close()
34 |
35 | if __name__ == "__main__":
36 | main()
37 |
--------------------------------------------------------------------------------
/inventory_xmlout.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import pickle
4 | import datetime
5 | import smtplib
6 | import string
7 |
8 | def main():
9 | file = open('inventory.inv', 'r')
10 | inventoryarr = pickle.load(file)
11 | file.close()
12 |
13 | xmlfile = open('inventory.xml', 'w')
14 | #We just check each of the elements
15 | for upc in inventoryarr.keys():
16 | xmlfile.write('- \n\t' + upc + '\n')
17 | if inventoryarr[upc][0][1] is not None:
18 | xmlfile.write('\t' + inventoryarr[upc][0][1] + '\n')
19 | length = inventoryarr[upc][0][0]
20 | for key in inventoryarr[upc][1:]:
21 | buydate = key
22 | newdate = buydate + datetime.timedelta(days=length)
23 | xmlfile.write('\t\t' + newdate.strftime("%A, %d %B %Y") + '\n')
24 | xmlfile.write('
')
25 |
26 | xmlfile.close()
27 |
28 | if __name__ == "__main__":
29 | main()
30 |
--------------------------------------------------------------------------------
/inventory_printout.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import pickle
4 | import datetime
5 | import smtplib
6 | import string
7 |
8 | def main():
9 | try:
10 | file = open('inventory.inv', 'r')
11 | except:
12 | print "Issue opening inventory.inv. Make sure this file is in the current directory. If it is not, add an item to create it."
13 | return
14 | inventoryarr = pickle.load(file)
15 | file.close()
16 |
17 | #We just check each of the elements
18 | for upc in inventoryarr.keys():
19 | if inventoryarr[upc][0][1] is not None:
20 | itemname = upc + ": " + inventoryarr[upc][0][1]
21 | else:
22 | itemname = upc + "Unknown"
23 | length = inventoryarr[upc][0][0]
24 | for key in inventoryarr[upc][1:]:
25 | buydate = key
26 | newdate = buydate + datetime.timedelta(days=length)
27 | print itemname + ", expiring on " + newdate.strftime("%Y-%m-%d, a %A")
28 |
29 | file = open('inventory.inv', 'w')
30 | pickle.dump(inventoryarr,file)
31 | file.close()
32 |
33 | if __name__ == "__main__":
34 | main()
35 |
--------------------------------------------------------------------------------
/inventory_modifyexpiry.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import pickle
4 | import sys
5 |
6 | def main():
7 | file = open('inventory.inv', 'r')
8 | inventoryarr = pickle.load(file)
9 | file.close()
10 |
11 | while True:
12 | print "Enter the EAN code of an item you wish to modify."
13 | linetmp = sys.stdin.readline()
14 | if not linetmp:
15 | break
16 | #line = linetmp.rstrip()[7:]
17 | line = linetmp.rstrip()
18 | try:
19 | tmp = inventoryarr[line]
20 | except:
21 | print "This EAN doesn't exist in the database."
22 | break
23 | print "Please enter the approximate amount of shelf-life for this item (in days):"
24 | length = sys.stdin.readline()
25 | if length:
26 | predate = inventoryarr[line][0][0]
27 | inventoryarr[line][0][0]=int(length)
28 | print "Expiry time updated from " + str(predate) + " days to " + length.rstrip() + " days."
29 |
30 |
31 | file = open('inventory.inv', 'w')
32 | pickle.dump(inventoryarr,file)
33 | file.close()
34 |
35 | if __name__ == "__main__":
36 | main()
37 |
--------------------------------------------------------------------------------
/inventory_grabrecipes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import pickle
4 | #import sys
5 | import os.path
6 | from subprocess import call
7 | from xmlrpclib import ServerProxy, Error
8 | import datetime
9 | import urllib2
10 | import simplejson
11 | from xml.etree import ElementTree
12 | import inventory_grabcronometer
13 |
14 |
15 | def main():
16 | try:
17 | with open('inventory.inv', 'r') as filename:
18 | inventoryarr = pickle.load(filename)
19 | filename.close()
20 | except IOError:
21 | inventoryarr = {}
22 |
23 | call("./concatenate_cronometer.sh", shell=True)
24 | recipearr = inventory_grabcronometer.cronometer_recipes(inventoryarr) #construct recipes with at least one item from inventoryarr
25 | #print out the details of the top few recipes
26 | if recipearr != []:
27 | print "The top ranked recipe is called " + recipearr[0][0].attrib['name'] + " with ranking " + str(recipearr[0][1]) + "."
28 | print "Other top recipes:"
29 | for el in recipearr[1:8]:
30 | print el[0].attrib['name'] + " with ranking " + str(el[1]) + "."
31 |
32 | else:
33 | print "No matching recipes found."
34 |
35 | if __name__ == "__main__":
36 | main()
37 |
--------------------------------------------------------------------------------
/inventory:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import inventory_remove
4 | import inventory_add
5 | import inventory_modifyexpiry
6 | import inventory_modifyname
7 | import inventory_daily
8 | import inventory_printout
9 | import inventory_addname
10 | import inventory_xmlout
11 | import inventory_grabcronometer
12 | import inventory_grabrecipes
13 | import inventory_randomjson
14 |
15 | import argparse
16 |
17 | parser = argparse.ArgumentParser(description='Read and store product information from upc codes.')
18 | parser.add_argument('action', action='store', help='The action we want to perform; can be one of: add, addmanually, modifyexpiry, modifyname, remove, printout, xmlout, or daily. Select add to begin!')
19 |
20 | args = parser.parse_args()
21 | #print args.accumulate(args.integers)
22 | print "You have selected \"" + args.action + "\"."
23 |
24 | {'add' : inventory_add.main, 'addmanually' : inventory_addname.main, 'grabcronometer' : inventory_grabcronometer.main, 'grabrecipes' : inventory_grabrecipes.main, 'modifyexpiry' : inventory_modifyexpiry.main, 'modifyname' : inventory_modifyname.main, 'daily' : inventory_daily.main, 'randomjson' : inventory_randomjson.main, 'remove' : inventory_remove.main, 'printout' : inventory_printout.main, 'xmlout' : inventory_xmlout.main}[args.action]()
25 |
26 |
--------------------------------------------------------------------------------
/inventory_remove.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import pickle
4 | import sys
5 | import datetime
6 |
7 |
8 | def main():
9 | file = open('inventory.inv', 'r')
10 | inventoryarr = pickle.load(file)
11 | file.close()
12 |
13 | shoppinglistfile = open('shoppinglist.txt', 'a')
14 | while True:
15 | print "Scan the UPC of the item you'd like to remove."
16 | linetmp = sys.stdin.readline()
17 | if not linetmp:
18 | break
19 | line = linetmp.rstrip()
20 | #upc = line[7:]
21 | upc = line
22 | try:
23 | tmp = inventoryarr[upc]
24 | except:
25 | print "The item " + upc + " does not yet exist in this database."
26 | continue
27 |
28 | try:
29 | print upc + ": \"" + inventoryarr[upc][0][1] + "\", added on " + inventoryarr[upc].pop(1).strftime("%A, %d %B %Y") + " was successfully deleted."
30 | shoppinglistfile.write(inventoryarr[upc][0][1] + ", " + upc + "\n")
31 | except:
32 | print "The item exists, but you currently have zero stock. Removing record from inventory entirely."
33 | #inventoryarr[upc].pop(0)
34 | inventoryarr.pop(upc,None)
35 |
36 | shoppinglistfile.close()
37 |
38 | print "Remaining items:"
39 | print inventoryarr
40 |
41 |
42 | file = open('inventory.inv', 'w')
43 | pickle.dump(inventoryarr,file)
44 | file.close()
45 |
46 | if __name__ == "__main__":
47 | main()
48 |
--------------------------------------------------------------------------------
/inventory_addname.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import pickle
4 | import sys
5 |
6 | def grabessentials():
7 | itemname = ''
8 | expiry = ''
9 | print "Please enter the descriptive name for this item:"
10 | length = sys.stdin.readline()
11 | if length:
12 | itemname = length
13 | else:
14 | print "You didn't enter a valid name. Try again."
15 | return([])
16 |
17 | print "Please enter the approximate amount of shelf-life for this item (in days):"
18 | length = sys.stdin.readline()
19 | if length:
20 | expiry = int(length)
21 | else:
22 | print "You didn't enter a valid shelf-life. Enter the item again."
23 | return([])
24 | return([expiry,itemname])
25 |
26 |
27 | def main():
28 | file = open('inventory.inv', 'r')
29 | inventoryarr = pickle.load(file)
30 | file.close()
31 |
32 | while True:
33 | print "Enter the EAN code of an item you wish to modify."
34 | linetmp = sys.stdin.readline()
35 | if not linetmp:
36 | break
37 | #line = linetmp.rstrip()[7:]
38 | upc = linetmp.rstrip()
39 | arr = grabessentials()
40 | try:
41 | tmp = inventoryarr[upc]
42 | print "The item is already in the database. Unless you quit now I'll overwrite its name..."
43 | inventoryarr[upc][0] = arr
44 | except:
45 | inventoryarr[upc] = [arr]
46 | print "Added item " + upc + "."
47 |
48 |
49 | file = open('inventory.inv', 'w')
50 | pickle.dump(inventoryarr,file)
51 | file.close()
52 |
53 | if __name__ == "__main__":
54 | main()
55 |
--------------------------------------------------------------------------------
/inventory_daily.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import subprocess
4 | import pickle
5 | import datetime
6 | import smtplib
7 | import string
8 |
9 | #MYEMAIL = "test@test.com"
10 |
11 | #MYFROM = "test@inventory.com"
12 |
13 | def sendmessage(recipient, subject, body):
14 | try:
15 | process = subprocess.Popen(['mail', '-s', subject, recipient],
16 | stdin=subprocess.PIPE)
17 | except Exception, error:
18 | print error
19 | process.communicate(body)
20 |
21 | def main():
22 | file = open('inventory.inv', 'r')
23 | inventoryarr = pickle.load(file)
24 | file.close()
25 |
26 | #We just check for expiry of the elements
27 | for upc in inventoryarr.keys():
28 | length = inventoryarr[upc][0][0]
29 | item = inventoryarr[upc][0][1]
30 | for key in inventoryarr[upc][1:]:
31 | newdate = key + datetime.timedelta(days=length)
32 | print newdate
33 | if newdate < datetime.date.today() + datetime.timedelta(days=2):
34 | subj = "[INVENTORY] \"Items about to expire!\""
35 | text = "\"Item UPC" + upc + ", " + item + " is expiring on " + str(newdate) + ". Consume with expedience!\""
36 | BODY = string.join((
37 | "From: %s" % MYFROM,
38 | "To: %s" % MYEMAIL,
39 | "Subject: %s" % subj ,
40 | "",
41 | text
42 | ), "\r\n")
43 | sendmessage(MYEMAIL, subj, text)
44 | print(text)
45 |
46 | if __name__ == "__main__":
47 | main()
48 |
--------------------------------------------------------------------------------
/inventory_add.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import pickle
4 | import sys
5 | from xmlrpclib import ServerProxy, Error
6 | import datetime
7 | import urllib2
8 | import simplejson
9 | import inventory_grabcronometer
10 |
11 | DEFAULT_EXPIRY_TIME = 600
12 | #The default expiry time for items, in days.
13 |
14 | def main():
15 | try:
16 | with open('inventory.inv', 'r') as filename:
17 | inventoryarr = pickle.load(filename)
18 | filename.close()
19 | except IOError:
20 | inventoryarr = {}
21 |
22 |
23 | while True:
24 | linetmp = sys.stdin.readline()
25 | if not linetmp:
26 | break
27 | line = linetmp.rstrip()
28 | #upc = line[7:]
29 | upc = line
30 | itemdata = datetime.date.today()
31 | if inventoryarr.has_key(upc):
32 | inventoryarr[upc].append(itemdata)
33 | else:
34 | print "UPC" + upc + " is a brand new item. Searching Cronometer..."
35 | arr = inventory_grabcronometer.cronometer(upc)
36 | if arr != 0:
37 | print "Found in Cronometer! Item successfully entered."
38 | specified_expiry_time = int(arr[0])
39 | if specified_expiry_time < 600 and specified_expiry_time > 0:
40 | arr2 = [specified_expiry_time,arr[1],arr[2]]
41 | else:
42 | arr2 = [DEFAULT_EXPIRY_TIME,arr[1],arr[2]]
43 | inventoryarr[upc] = [arr2]
44 | inventoryarr[upc].append(itemdata)
45 | continue
46 | #print "Not found in Cronometer. Searching UPC Database."
47 | print "UPC " + upc + " was not found in Cronometer. Please enter it."
48 | continue
49 |
50 | print "Successfully entered upc " + upc
51 |
52 | print "Your current inventory contains:"
53 | print inventoryarr
54 |
55 | file = open('inventory.inv', 'w')
56 | pickle.dump(inventoryarr,file)
57 | file.close()
58 |
59 | if __name__ == "__main__":
60 | main()
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Inventory
2 | =========
3 |
4 | This set of Python scripts will allow you to interface a barcode reader such as [zbarcam](http://zbar.sourceforge.net/) with a UPC database to keep track of foods as you buy them.
5 |
6 |
7 | Install
8 | =======
9 |
10 | Download the set of scripts, ensure you have a working Python installation (I use Python>=2.6), and run
11 |
12 | ```bash
13 | python inventory --help
14 | ```
15 |
16 | for information.
17 |
18 |
19 | Run
20 | ======
21 |
22 | There are a number of features and options. Most of them are self-described. You can run
23 |
24 | ```bash
25 | python inventory add
26 | ```
27 |
28 | to begin adding items to your database. It is important to note that some of the commands require
29 | input prefixed by the barcode type (only EAN-13 is fully supported at the moment), while others require
30 | just the barcode iteself. This is because some commands are designed to receive input piped from zbarcam,
31 | while others are designed to receive manual input. For example
32 |
33 | ./inventory add
34 |
35 | requires input such as
36 |
37 | 0068100084245
38 |
39 | The "modifyexpiry" allows you to enter a specific product EAN-13 manually and customize its expiry date.
40 | The expiry dates persist through deletion of stock, so you will never need to enter expiry lengths for that particular product again.
41 | The "remove" option allows you to scan barcodes on the way out, removing that product from your inventory.
42 | The "printout" option will print the database out for you, neatly.
43 | Finally, the "daily" option allows for cron or some other scheduling tool to check daily for products nearing expiry.
44 | This is best used with a line in crontab that runs inventory with the daily option.
45 | You will need to enter your email in inventory_daily.py in order for this to properly work,
46 | as well as ensure that your system is a working mailserver.
47 |
48 |
49 | Interfacing with Cronometer
50 | --------------------------
51 |
52 | Inventory also checks your [Cronometer](http://sourceforge.net/projects/cronometer/) database for UPC codes.
53 | First, ensure you have the Cronometer foods folder specified in inventory_grabcronometer.py (it is a global variable).
54 | Then, when you add custom items into Cronometer, just put the UPC in the Comments section. Inventory will automatically scan for Cronometer UPCs before turning to the web.
55 |
56 |
57 | TODO
58 | =========
59 |
60 | Integrate with webserver to display printout status
61 |
--------------------------------------------------------------------------------
/inventory_randomjson.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import pickle
4 | import os.path
5 | from subprocess import call
6 | from xmlrpclib import ServerProxy, Error
7 | import datetime
8 | import urllib2
9 | import json
10 | import re
11 | import random
12 | import qrcode
13 | from pprint import pprint
14 | from PIL import Image
15 |
16 | def main():
17 | try:
18 | with open('inventory.inv', 'r') as filename:
19 | inventoryarr = pickle.load(filename)
20 | filename.close()
21 | except IOError:
22 | inventoryarr = {}
23 |
24 | with open('../CRONOMETER-data/bbcrecipes/bbccouk-recipes.json', 'r') as jsonfile:
25 | jsondata = json.load(jsonfile)
26 | jsonfile.close()
27 |
28 | #Generate a random number between 0 and 105 to narrow down the BBC recipes
29 | bbcchunk = int(105*random.random())
30 | bbcindex = int(99*random.random())
31 |
32 | bestrecipe = 0
33 | ingmatches = 0
34 | bestscore = 0
35 |
36 |
37 | searchinventoryarr = []
38 | for value in inventoryarr.values():
39 | try:
40 | searchinventoryarr.append(value[0][1])
41 | except IndexError:
42 | print "Index err."
43 |
44 | searchinventoryarr = [re.split(', |,| ', value) for value in searchinventoryarr] #split on comma or spaces
45 | searchinventoryarr = [item for items in searchinventoryarr for item in items] #flatten
46 | searchinventoryarr = [re.sub('[^A-Za-z]+', '', value) for value in searchinventoryarr] #only keep ascii letters
47 | searchinventoryarr = [item.lower() for item in searchinventoryarr]
48 |
49 | for searchcounter in range(0,30):
50 | recipeindex = (bbcindex + searchcounter) % 100
51 | jsonrecipe = jsondata[bbcchunk*100 + recipeindex]
52 | jsonings = [item.encode('ascii', 'xmlcharrefreplace') for item in jsonrecipe["ingredients"]]
53 | jsonings = [re.split(', |,| ', value) for value in jsonings] #split on comma or spaces
54 | jsonings = [item for items in jsonings for item in items] #flatten
55 | jsonings = [re.sub('[^A-Za-z]+', '', value) for value in jsonings] #only keep ascii letters
56 | jsonings = [item.lower() for item in jsonings]
57 | recipeingmatches = len(set(searchinventoryarr)&set(jsonings))
58 | recipenumingredients = len(set(jsonings))
59 | recipescore = float(float(recipeingmatches)/float(recipenumingredients))
60 | recipetitle = jsonrecipe["title"].encode('ascii', 'xmlcharrefreplace')
61 | if recipescore > bestscore:
62 | bestrecipe = recipetitle
63 | ingmatches = recipeingmatches
64 | bestscore = recipescore
65 |
66 | recipetitlestripped = re.sub('[^A-Za-z]+', '', bestrecipe)
67 | url = 'https://wbrenna.ca/wilson/recipes/bbcrecipesxml-' + str(bbcchunk) + '.xml#' + recipetitlestripped
68 | print "Here is a semi-random recipe you can try to make: it has a score of " + str(bestscore) + ". It's called \"" + bestrecipe + "\". See it at " + url + "."
69 | img = qrcode.make(url)
70 | img.show()
71 | img.save('qrcode.png')
72 |
73 | if __name__ == "__main__":
74 | main()
75 |
--------------------------------------------------------------------------------
/inventory_grabcronometer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | import sys
4 | import os
5 | from xml.etree import ElementTree
6 | import glob
7 | import re
8 | import datetime
9 |
10 | CRONOMETER_FOLDER = "../CRONOMETER-data/foods/"
11 |
12 | def cronometer_recipes(inventoryarr):
13 | #execute concatenate_cronometer.sh
14 | foodlist = {}
15 | food = ''
16 | foodroot = ElementTree.parse('complete_cronometer_data.xml').getroot()
17 | foods = foodroot.findall('food')
18 | recipes = foodroot.findall('recipe')
19 | validrecipelist = []
20 | #the CRONOMETER item is either a recipe or an item with a UPC
21 | #check to make sure it's a recipe; should start with 1:
36 | expiryindextmp = 1
37 | if expiryindextmp > expiryindex:
38 | expiryindex = expiryindextmp
39 | #print expiryindex
40 | foodlist[inventoryarr[inventoryarritems][0][2]] = expiryindex
41 | print "Gathering and sorting matching recipes..."
42 | for recipe in recipes:
43 | ingredients = recipe.iter('serving')
44 | ingmatch = 0
45 | expirymetric = 0
46 | ingcounter = 0
47 | for ingredient in ingredients:
48 | #print ingredient.attrib
49 | ingcounter = ingcounter + 1
50 | if ingredient.attrib['food'] in foodlist.keys():
51 | ingmatch = ingmatch + 1
52 | expirymetric = expirymetric + foodlist[ingredient.attrib['food']]
53 | #print(foodlist[ingredient.attrib['food']])
54 | if ingmatch > 0:
55 | #add the recipe to our list, and determine whether any of the 0:
70 | arr2 = [specified_expiry_time,arr[1],arr[2]]
71 | else:
72 | arr2 = [DEFAULT_EXPIRY_TIME,arr[1],arr[2]]
73 | inventoryarr[upc] = [arr2]
74 | inventoryarr[upc].append(itemdata)
75 | continue
76 | print "Not found in Cronometer. Please add there."
77 | continue
78 | else:
79 | try:
80 | tmp = inventoryarr[upc]
81 | print "Item added on " + tmp.pop(1).strftime("%A, %d %B %Y") + " was successfully deleted."
82 | except:
83 | print "Item to be removed did not exist in the database."
84 |
85 | linetmp = ""
86 |
87 | print "Successfully entered upc " + upc
88 | if lastRun = True:
89 | print "Saving entries..."
90 | file = open('inventory.inv', 'w')
91 | pickle.dump(inventoryarr,file)
92 | file.close()
93 | lineprev = ""
94 | linetmp = ""
95 | else:
96 | lineprev = linetmp
97 |
98 |
99 | print "Your current inventory contains:"
100 | print inventoryarr
101 |
102 | file = open('inventory.inv', 'w')
103 | pickle.dump(inventoryarr,file)
104 | file.close()
105 |
106 | if __name__ == "__main__":
107 | main()
108 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This software is distributed under a Creative Commons BY-NC-SA 3.0 License.
2 | See http://wbrenna.uwaterloo.ca/wilson/license.php for details.
3 |
4 |
5 | License
6 |
7 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS
8 | PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR
9 | OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS
10 | LICENSE OR COPYRIGHT LAW IS PROHIBITED.
11 |
12 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE
13 | BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED
14 | TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN
15 | CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
16 |
17 | 1. Definitions
18 |
19 | "Adaptation" means a work based upon the Work, or upon the Work and other
20 | pre-existing works, such as a translation, adaptation, derivative work,
21 | arrangement of music or other alterations of a literary or artistic work, or
22 | phonogram or performance and includes cinematographic adaptations or any other
23 | form in which the Work may be recast, transformed, or adapted including in any
24 | form recognizably derived from the original, except that a work that constitutes
25 | a Collection will not be considered an Adaptation for the purpose of this
26 | License. For the avoidance of doubt, where the Work is a musical work,
27 | performance or phonogram, the synchronization of the Work in timed-relation with
28 | a moving image ("synching") will be considered an Adaptation for the purpose of
29 | this License. "Collection" means a collection of literary or artistic works,
30 | such as encyclopedias and anthologies, or performances, phonograms or
31 | broadcasts, or other works or subject matter other than works listed in Section
32 | 1(g) below, which, by reason of the selection and arrangement of their contents,
33 | constitute intellectual creations, in which the Work is included in its entirety
34 | in unmodified form along with one or more other contributions, each constituting
35 | separate and independent works in themselves, which together are assembled into
36 | a collective whole. A work that constitutes a Collection will not be considered
37 | an Adaptation (as defined above) for the purposes of this License. "Distribute"
38 | means to make available to the public the original and copies of the Work or
39 | Adaptation, as appropriate, through sale or other transfer of ownership.
40 | "License Elements" means the following high-level license attributes as selected
41 | by Licensor and indicated in the title of this License: Attribution,
42 | Noncommercial, ShareAlike. "Licensor" means the individual, individuals, entity
43 | or entities that offer(s) the Work under the terms of this License. "Original
44 | Author" means, in the case of a literary or artistic work, the individual,
45 | individuals, entity or entities who created the Work or if no individual or
46 | entity can be identified, the publisher; and in addition (i) in the case of a
47 | performance the actors, singers, musicians, dancers, and other persons who act,
48 | sing, deliver, declaim, play in, interpret or otherwise perform literary or
49 | artistic works or expressions of folklore; (ii) in the case of a phonogram the
50 | producer being the person or legal entity who first fixes the sounds of a
51 | performance or other sounds; and, (iii) in the case of broadcasts, the
52 | organization that transmits the broadcast. "Work" means the literary and/or
53 | artistic work offered under the terms of this License including without
54 | limitation any production in the literary, scientific and artistic domain,
55 | whatever may be the mode or form of its expression including digital form, such
56 | as a book, pamphlet and other writing; a lecture, address, sermon or other work
57 | of the same nature; a dramatic or dramatico-musical work; a choreographic work
58 | or entertainment in dumb show; a musical composition with or without words; a
59 | cinematographic work to which are assimilated works expressed by a process
60 | analogous to cinematography; a work of drawing, painting, architecture,
61 | sculpture, engraving or lithography; a photographic work to which are
62 | assimilated works expressed by a process analogous to photography; a work of
63 | applied art; an illustration, map, plan, sketch or three-dimensional work
64 | relative to geography, topography, architecture or science; a performance; a
65 | broadcast; a phonogram; a compilation of data to the extent it is protected as a
66 | copyrightable work; or a work performed by a variety or circus performer to the
67 | extent it is not otherwise considered a literary or artistic work. "You" means
68 | an individual or entity exercising rights under this License who has not
69 | previously violated the terms of this License with respect to the Work, or who
70 | has received express permission from the Licensor to exercise rights under this
71 | License despite a previous violation. "Publicly Perform" means to perform
72 | public recitations of the Work and to communicate to the public those public
73 | recitations, by any means or process, including by wire or wireless means or
74 | public digital performances; to make available to the public Works in such a way
75 | that members of the public may access these Works from a place and at a place
76 | individually chosen by them; to perform the Work to the public by any means or
77 | process and the communication to the public of the performances of the Work,
78 | including by public digital performance; to broadcast and rebroadcast the Work
79 | by any means including signs, sounds or images. "Reproduce" means to make
80 | copies of the Work by any means including without limitation by sound or visual
81 | recordings and the right of fixation and reproducing fixations of the Work,
82 | including storage of a protected performance or phonogram in digital form or
83 | other electronic medium. 2. Fair Dealing Rights. Nothing in this License is
84 | intended to reduce, limit, or restrict any uses free from copyright or rights
85 | arising from limitations or exceptions that are provided for in connection with
86 | the copyright protection under copyright law or other applicable laws.
87 |
88 | 3. License Grant. Subject to the terms and conditions of this License, Licensor
89 | hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the
90 | duration of the applicable copyright) license to exercise the rights in the Work
91 | as stated below:
92 |
93 | to Reproduce the Work, to incorporate the Work into one or more Collections, and
94 | to Reproduce the Work as incorporated in the Collections; to create and
95 | Reproduce Adaptations provided that any such Adaptation, including any
96 | translation in any medium, takes reasonable steps to clearly label, demarcate or
97 | otherwise identify that changes were made to the original Work. For example, a
98 | translation could be marked "The original work was translated from English to
99 | Spanish," or a modification could indicate "The original work has been
100 | modified."; to Distribute and Publicly Perform the Work including as
101 | incorporated in Collections; and, to Distribute and Publicly Perform
102 | Adaptations. The above rights may be exercised in all media and formats whether
103 | now known or hereafter devised. The above rights include the right to make such
104 | modifications as are technically necessary to exercise the rights in other media
105 | and formats. Subject to Section 8(f), all rights not expressly granted by
106 | Licensor are hereby reserved, including but not limited to the rights described
107 | in Section 4(e).
108 |
109 | 4. Restrictions. The license granted in Section 3 above is expressly made
110 | subject to and limited by the following restrictions:
111 |
112 | You may Distribute or Publicly Perform the Work only under the terms of this
113 | License. You must include a copy of, or the Uniform Resource Identifier (URI)
114 | for, this License with every copy of the Work You Distribute or Publicly
115 | Perform. You may not offer or impose any terms on the Work that restrict
116 | the terms of this License or the ability of the recipient of the Work to
117 | exercise the rights granted to that recipient under the terms of the
118 | License. You may not sublicense the Work. You must keep intact all
119 | notices that refer to this License and to the disclaimer of warranties
120 | with every copy of the Work You Distribute or Publicly Perform. When You
121 | Distribute or Publicly Perform the Work, You may not impose any
122 | effective technological measures on the Work that restrict the ability
123 | of a recipient of the Work from You to exercise the rights granted to
124 | that recipient under the terms of the License. This Section 4(a) applies
125 | to the Work as incorporated in a Collection, but this does not require
126 | the Collection apart from the Work itself to be made subject to the
127 | terms of this License. If You create a Collection, upon notice from any
128 | Licensor You must, to the extent practicable, remove from the Collection
129 | any credit as required by Section 4(d), as requested. If You create an
130 | Adaptation, upon notice from any Licensor You must, to the extent
131 | practicable, remove from the Adaptation any credit as required by
132 | Section 4(d), as requested. You may Distribute or Publicly Perform an
133 | Adaptation only under: (i) the terms of this License; (ii) a later
134 | version of this License with the same License Elements as this License;
135 | (iii) a Creative Commons jurisdiction license (either this or a later
136 | license version) that contains the same License Elements as this License
137 | (e.g., Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable
138 | License"). You must include a copy of, or the URI, for Applicable
139 | License with every copy of each Adaptation You Distribute or Publicly
140 | Perform. You may not offer or impose any terms on the Adaptation that
141 | restrict the terms of the Applicable License or the ability of the
142 | recipient of the Adaptation to exercise the rights granted to that
143 | recipient under the terms of the Applicable License. You must keep
144 | intact all notices that refer to the Applicable License and to the
145 | disclaimer of warranties with every copy of the Work as included in the
146 | Adaptation You Distribute or Publicly Perform. When You Distribute or
147 | Publicly Perform the Adaptation, You may not impose any effective
148 | technological measures on the Adaptation that restrict the ability of a
149 | recipient of the Adaptation from You to exercise the rights granted to
150 | that recipient under the terms of the Applicable License. This Section
151 | 4(b) applies to the Adaptation as incorporated in a Collection, but this
152 | does not require the Collection apart from the Adaptation itself to be
153 | made subject to the terms of the Applicable License. You may not
154 | exercise any of the rights granted to You in Section 3 above in any
155 | manner that is primarily intended for or directed toward commercial
156 | advantage or private monetary compensation. The exchange of the Work for
157 | other copyrighted works by means of digital file-sharing or otherwise
158 | shall not be considered to be intended for or directed toward commercial
159 | advantage or private monetary compensation, provided there is no payment
160 | of any monetary compensation in con-nection with the exchange of
161 | copyrighted works. If You Distribute, or Publicly Perform the Work or
162 | any Adaptations or Collections, You must, unless a request has been made
163 | pursuant to Section 4(a), keep intact all copyright notices for the Work
164 | and provide, reasonable to the medium or means You are utilizing: (i)
165 | the name of the Original Author (or pseudonym, if applicable) if
166 | supplied, and/or if the Original Author and/or Licensor designate
167 | another party or parties (e.g., a sponsor institute, publishing entity,
168 | journal) for attribution ("Attribution Parties") in Licensor's copyright
169 | notice, terms of service or by other reasonable means, the name of such
170 | party or parties; (ii) the title of the Work if supplied; (iii) to the
171 | extent reasonably practicable, the URI, if any, that Licensor specifies
172 | to be associated with the Work, unless such URI does not refer to the
173 | copyright notice or licensing information for the Work; and, (iv)
174 | consistent with Section 3(b), in the case of an Adaptation, a credit
175 | identifying the use of the Work in the Adaptation (e.g., "French
176 | translation of the Work by Original Author," or "Screenplay based on
177 | original Work by Original Author"). The credit required by this Section
178 | 4(d) may be implemented in any reasonable manner; provided, however,
179 | that in the case of a Adaptation or Collection, at a minimum such credit
180 | will appear, if a credit for all contributing authors of the Adaptation
181 | or Collection appears, then as part of these credits and in a manner at
182 | least as prominent as the credits for the other contributing authors.
183 | For the avoidance of doubt, You may only use the credit required by this
184 | Section for the purpose of attribution in the manner set out above and,
185 | by exercising Your rights under this License, You may not implicitly or
186 | explicitly assert or imply any connection with, sponsorship or
187 | endorsement by the Original Author, Licensor and/or Attribution Parties,
188 | as appropriate, of You or Your use of the Work, without the separate,
189 | express prior written permission of the Original Author, Licensor and/or
190 | Attribution Parties. For the avoidance of doubt:
191 |
192 | Non-waivable Compulsory License Schemes. In those jurisdictions in which the
193 | right to collect royalties through any statutory or compulsory licensing scheme
194 | cannot be waived, the Licensor reserves the exclusive right to collect such
195 | royalties for any exercise by You of the rights granted under this License;
196 | Waivable Compulsory License Schemes. In those jurisdictions in which the right
197 | to collect royalties through any statutory or compulsory licensing scheme can be
198 | waived, the Licensor reserves the exclusive right to collect such royalties for
199 | any exercise by You of the rights granted under this License if Your exercise of
200 | such rights is for a purpose or use which is otherwise than noncommercial as
201 | permitted under Section 4(c) and otherwise waives the right to collect royalties
202 | through any statutory or compulsory licensing scheme; and, Voluntary License
203 | Schemes. The Licensor reserves the right to collect royalties, whether
204 | individually or, in the event that the Licensor is a member of a collecting
205 | society that administers voluntary licensing schemes, via that society, from any
206 | exercise by You of the rights granted under this License that is for a purpose
207 | or use which is otherwise than noncommercial as permitted under Section 4(c).
208 | Except as otherwise agreed in writing by the Licensor or as may be otherwise
209 | permitted by applicable law, if You Reproduce, Distribute or Publicly Perform
210 | the Work either by itself or as part of any Adaptations or Collections, You must
211 | not distort, mutilate, modify or take other derogatory action in relation to the
212 | Work which would be prejudicial to the Original Author's honor or reputation.
213 | Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise
214 | of the right granted in Section 3(b) of this License (the right to make
215 | Adaptations) would be deemed to be a distortion, mutilation, modification or
216 | other derogatory action prejudicial to the Original Author's honor and
217 | reputation, the Licensor will waive or not assert, as appropriate, this Section,
218 | to the fullest extent permitted by the applicable national law, to enable You to
219 | reasonably exercise Your right under Section 3(b) of this License (right to make
220 | Adaptations) but not otherwise. 5. Representations, Warranties and Disclaimer
221 |
222 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE FULLEST
223 | EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO
224 | REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED,
225 | STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE,
226 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE
227 | ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF
228 | ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
229 | EXCLUSION OF IMPLIED WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU.
230 |
231 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN
232 | NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL,
233 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS
234 | LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE
235 | POSSIBILITY OF SUCH DAMAGES.
236 |
237 | 7. Termination
238 |
239 | This License and the rights granted hereunder will terminate automatically upon
240 | any breach by You of the terms of this License. Individuals or entities who have
241 | received Adaptations or Collections from You under this License, however, will
242 | not have their licenses terminated provided such individuals or entities remain
243 | in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
244 | survive any termination of this License. Subject to the above terms and
245 | conditions, the license granted here is perpetual (for the duration of the
246 | applicable copyright in the Work). Notwithstanding the above, Licensor reserves
247 | the right to release the Work under different license terms or to stop
248 | distributing the Work at any time; provided, however that any such election will
249 | not serve to withdraw this License (or any other license that has been, or is
250 | required to be, granted under the terms of this License), and this License will
251 | continue in full force and effect unless terminated as stated above. 8.
252 | Miscellaneous
253 |
254 | Each time You Distribute or Publicly Perform the Work or a Collection, the
255 | Licensor offers to the recipient a license to the Work on the same terms and
256 | conditions as the license granted to You under this License. Each time You
257 | Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a
258 | license to the original Work on the same terms and conditions as the license
259 | granted to You under this License. If any provision of this License is invalid
260 | or unenforceable under applicable law, it shall not affect the validity or
261 | enforceability of the remainder of the terms of this License, and without
262 | further action by the parties to this agreement, such provision shall be
263 | reformed to the minimum extent necessary to make such provision valid and
264 | enforceable. No term or provision of this License shall be deemed waived and no
265 | breach consented to unless such waiver or consent shall be in writing and signed
266 | by the party to be charged with such waiver or consent. This License
267 | constitutes the entire agreement between the parties with respect to the Work
268 | licensed here. There are no understandings, agreements or representations with
269 | respect to the Work not specified here. Licensor shall not be bound by any
270 | additional provisions that may appear in any communication from You. This
271 | License may not be modified without the mutual written agreement of the Licensor
272 | and You. The rights granted under, and the subject matter referenced, in this
273 | License were drafted utilizing the terminology of the Berne Convention for the
274 | Protection of Literary and Artistic Works (as amended on September 28, 1979),
275 | the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO
276 | Performances and Phonograms Treaty of 1996 and the Universal Copyright
277 | Convention (as revised on July 24, 1971). These rights and subject matter take
278 | effect in the relevant jurisdiction in which the License terms are sought to be
279 | enforced according to the corresponding provisions of the implementation of
280 | those treaty provisions in the applicable national law. If the standard suite of
281 | rights granted under applicable copyright law includes additional rights not
282 | granted under this License, such additional rights are deemed to be included in
283 | the License; this License is not intended to restrict the license of any rights
284 | under applicable law.
285 |
286 |
--------------------------------------------------------------------------------