Skip to content

Commit 4aaf325

Browse files
committed
Added learning opportunity functionality to scan.py.
Also, moved conf into its own module.
1 parent 94d7451 commit 4aaf325

File tree

3 files changed

+90
-12
lines changed

3 files changed

+90
-12
lines changed

lib/__init__.py

Whitespace-only changes.

lib/conf.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import yaml
2+
3+
class Conf:
4+
def __init__(self):
5+
self._conf_dict = yaml.load(open('/etc/oscar.yaml').read())
6+
7+
def get(self):
8+
return self._conf_dict
9+
10+
11+
c = Conf()
12+
def get():
13+
return c.get()

scan.py

+77-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
#!/usr/bin/env python
22

33
import time
4+
from datetime import datetime
45
import struct
56
import select
7+
import socket
8+
import random
69
import json
7-
import yaml
810
import urllib2
911
import hashlib
1012
import hmac
1113
import base64
14+
1215
import trello
16+
from twilio.rest import TwilioRestClient
17+
18+
from lib import conf
1319

1420

1521
def parse_scanner_data(scanner_data):
@@ -51,10 +57,51 @@ def get_description(self, upc):
5157
json_blob = urllib2.urlopen(url).read()
5258
return json.loads(json_blob)['description']
5359

54-
conf = yaml.load(open('/etc/oscar.yaml').read())
5560

61+
def local_ip():
62+
"""Returns the IP that the local host uses to talk to the Internet."""
63+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
64+
s.connect(("trello.com", 80))
65+
addr = s.getsockname()[0]
66+
s.close()
67+
return addr
68+
69+
70+
def generate_opp_id():
71+
return ''.join(random.sample('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 12))
72+
73+
74+
def opp_url(opp):
75+
return 'http://{0}/learn-barcode/{1}'.format(local_ip(), opp['barcode'])
76+
77+
78+
def create_barcode_opp(barcode, trello_api):
79+
"""Creates a learning opportunity for the given barcode and writes it to Trello
80+
81+
Returns the dict."""
82+
opp = {
83+
'type': 'barcode',
84+
'opp_id': generate_opp_id(),
85+
'barcode': barcode,
86+
'created_dt': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
87+
}
5688

57-
f = open(conf['scanner_device'], 'rb')
89+
rule_lists = trello_api.boards.get_list(conf.get()['trello_db_board'])
90+
opp_list = [x for x in rule_lists if x['name'] == 'learning_opportunities'][0]
91+
trello_api.lists.new_card(opp_list['id'], json.dumps(opp))
92+
return opp
93+
94+
95+
def publish_barcode_opp(opp):
96+
client = TwilioRestClient(conf.get()['twilio_sid'], conf.get()['twilio_token'])
97+
message = client.sms.messages.create(body='''Hi! Oscar here. You scanned a code I didn't recognize. Care to fill me in? {0}'''.format(opp_url(opp)),
98+
to='+{0}'.format(conf.get()['twilio_dest']),
99+
from_='+{0}'.format(conf.get()['twilio_src']))
100+
101+
102+
t = trello.TrelloApi(conf.get()['trello_app_key'])
103+
t.set_token(conf.get()['trello_token'])
104+
f = open(conf.get()['scanner_device'], 'rb')
58105
while True:
59106
print 'Waiting for scanner data'
60107

@@ -76,16 +123,33 @@ def get_description(self, upc):
76123
break
77124

78125
# Parse the binary data as a UPC
79-
upc = parse_scanner_data(scanner_data)
126+
barcode = parse_scanner_data(scanner_data)
127+
print "Scanned barcode '{0}'".format(barcode)
80128

81129
# Get the item's description
82-
u = UPCAPI(conf['digiteyes_app_key'], conf['digiteyes_auth_key'])
83-
desc = u.get_description(upc)
84-
130+
u = UPCAPI(conf.get()['digiteyes_app_key'], conf.get()['digiteyes_auth_key'])
131+
try:
132+
desc = u.get_description(barcode)
133+
print "Received description '{0}' for barcode {1}".format(desc, repr(barcode))
134+
except urllib2.HTTPError, e:
135+
if 'UPC/EAN code invalid' in e.msg and barcode != '0000':
136+
# ('0000' is garbage that my scanner occasionally outputs at random)
137+
print "Barcode {0} not recognized as a UPC; creating learning opportunity".format(repr(barcode))
138+
opp_id = create_barcode_opp(barcode, t)
139+
print "Publishing learning opportunity via SMS"
140+
publish_barcode_opp(opp_id)
141+
continue
142+
elif 'Not found' in e.msg:
143+
print "Barcode {0} not found in UPC database; creating learning opportunity".format(repr(barcode))
144+
opp_id = create_barcode_opp(barcode, t)
145+
print "Publishing learning opportunity via SMS"
146+
publish_barcode_opp(opp_id)
147+
continue
148+
else:
149+
raise
150+
85151
# Match against description rules
86-
t = trello.TrelloApi(conf['trello_app_key'])
87-
t.set_token(conf['trello_token'])
88-
rule_lists = t.boards.get_list(conf['trello_db_board'])
152+
rule_lists = t.boards.get_list(conf.get()['trello_db_board'])
89153
desc_rule_list = [x for x in rule_lists
90154
if x['name'] == 'description_rules'][0]
91155
desc_rules = [json.loads(card['name']) for card in t.lists.get_card(desc_rule_list['id'])]
@@ -98,12 +162,13 @@ def get_description(self, upc):
98162
continue
99163

100164
# Get the current grocery list
101-
lists = t.boards.get_list(conf['trello_grocery_board'])
165+
lists = t.boards.get_list(conf.get()['trello_grocery_board'])
102166
grocery_list = [x for x in lists
103-
if x['name'] == conf['trello_grocery_list']][0]
167+
if x['name'] == conf.get()['trello_grocery_list']][0]
104168
cards = t.lists.get_card(grocery_list['id'])
105169
card_names = [card['name'] for card in cards]
106170

107171
# Add item if it's not there already
108172
if item_to_add not in card_names:
173+
print "Adding '{0}' to grocery list".format(item_to_add)
109174
t.lists.new_card(grocery_list['id'], item_to_add)

0 commit comments

Comments
 (0)