1
1
#!/usr/bin/env python
2
2
3
3
import time
4
+ from datetime import datetime
4
5
import struct
5
6
import select
7
+ import socket
8
+ import random
6
9
import json
7
- import yaml
8
10
import urllib2
9
11
import hashlib
10
12
import hmac
11
13
import base64
14
+
12
15
import trello
16
+ from twilio .rest import TwilioRestClient
17
+
18
+ from lib import conf
13
19
14
20
15
21
def parse_scanner_data (scanner_data ):
@@ -51,10 +57,51 @@ def get_description(self, upc):
51
57
json_blob = urllib2 .urlopen (url ).read ()
52
58
return json .loads (json_blob )['description' ]
53
59
54
- conf = yaml .load (open ('/etc/oscar.yaml' ).read ())
55
60
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
+ }
56
88
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' )
58
105
while True :
59
106
print 'Waiting for scanner data'
60
107
@@ -76,16 +123,33 @@ def get_description(self, upc):
76
123
break
77
124
78
125
# 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 )
80
128
81
129
# 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
+
85
151
# 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' ])
89
153
desc_rule_list = [x for x in rule_lists
90
154
if x ['name' ] == 'description_rules' ][0 ]
91
155
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):
98
162
continue
99
163
100
164
# 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' ])
102
166
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 ]
104
168
cards = t .lists .get_card (grocery_list ['id' ])
105
169
card_names = [card ['name' ] for card in cards ]
106
170
107
171
# Add item if it's not there already
108
172
if item_to_add not in card_names :
173
+ print "Adding '{0}' to grocery list" .format (item_to_add )
109
174
t .lists .new_card (grocery_list ['id' ], item_to_add )
0 commit comments