├── .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 | --------------------------------------------------------------------------------