“Who’d I Lend That Book To?” Hard Questions Answered with Python
Hi
@lindemda
DanLindeman
Senior Software Engineer @ Very



You
- Like Libraries
- Books
- Python
- Dusty Pi
- Unused
- ...Still in the plastic

Journey
- Thing-Level
- RFID
- RPI3
- Raspbian
- SPI
- MFRC522-python
- Internet-Level
- Flask
- teensy bit of JS

Motivation
- GRPL Checkout
- IoT can be scary
- Python is helpful

Motivation #2
- Tiddlywinks
- Pi Up My Life
- Elixir App
- Validated with Python


Ready?

Thing Level
Raspberry PI

Python is there to help

RFID
- Radio Frequency Identification
- Everywhere
- Library Checkout
- Amiibo (NFC)
- Access Keycards
- Fyre Festival



RFID "Things"
- Power
- Data
- Size

Buy Things





In short

Reality


hard Way
- Read specs
- Standards
- Datasheets
- Registers

Simple Way
- MFRC522-python
from mfrc522 import SimpleMFRC522
reader = SimpleMFRC522()
def read():
try:
rfid, title = reader.read()
finally:
GPIO.cleanup()
def write(input_text):
try:
rfid, title = reader.write(input_text)
finally:
GPIO.cleanup()
Python is there to help
Adventure awaits

Internet LEvel
"You're saying Flask really weird there, Dan..."
Site Map

/Inventory
@app.route("/inventory")
def inventory():
books = Book.query.all()
return render_template('inventory.html', rows=books)
@app.route("/inventory", methods=['POST'])
def update_inventory():
if 'return' in request.form:
rfid = request.form['return']
book = Book.query.filter(Book.rfid == rfid).first()
book.loaned_to = None
db.session.commit()
elif 'delete' in request.form:
rfid = request.form['delete']
book = Book.query.filter(Book.rfid == rfid).first()
db.session.delete(book)
db.session.commit()
return redirect(url_for('inventory'))

|
"click"
-->
/Inventory

/Register
@app.route('/register')
def register():
return render_template('register.html')
@app.route('/register', methods=['POST'])
def register_post():
form_title = request.form['title']
try:
rfid, title = reader.write(form_title)
finally:
GPIO.cleanup()
book = Book(rfid=rfid, title=title)
db.session.add(book)
db.session.commit()
return redirect(url_for('index'))


"click" ->
"type-type-type"
Oh...
def write(self, text):
id, text_in = self.write_no_block(text)
while not id:
id, text_in = self.write_no_block(text)
return id, text_in
def write_no_block(self, text):
(status, TagType) = self.READER.MFRC522_Request(self.READER.PICC_REQIDL)
... <Cool and crazy Register stuff>
return id, text[0:(len(self.BLOCK_ADDRS) * 16)]
Streaming?
Yield
- Flask can render piece-wise
- From a generator
from flask import Response
@app.route('/large.csv')
def generate_large_csv():
def generate():
for row in iter_all_rows():
yield ','.join(row) + '\n'
return Response(generate(), mimetype='text/csv')
@app.route('/stream')
def streamed_response():
def generate():
yield 'Hello '
yield request.args['name']
yield '!'
return Response(stream_with_context(generate()))
2MeIRL4MeIRL


/Scanning
@app.route('/scanning')
def scanning():
start = time.time()
timeout = 1.0
while time.time() < start + timeout:
try:
id, text = reader.read_no_block()
if (id and id not in scans):
scans.add(id)
break
finally:
GPIO.cleanup()
books = Book.query.filter(Book.rfid.in_(scans)).all()
return render_template('stream.html', rows=books)

|
|
"click"
->
Async/Streaming
- Probably correct
- Isolated Service
- Start Scanning
- Give me scans
- Stop Scanning
Javascript
- Definitely a hack
- Works, really well
<h1>Scan your books</h1>
<a href="/checkout">Done</a>
<ul>
{% for book in rows %}
<li>{{ book.title }}</li>
{% endfor %}
</ul>
<script>
setTimeout(function() {
location.reload();
}, 250);
</script>
<a href="/" >Home Screen</a>
After all... "Simple is better than complex."
Javascript is there to help



/Checkout
@app.route("/checkout")
def checkout():
global scans, cart
cart = scans.copy()
books = Book.query.filter(Book.rfid.in_(cart)).all()
scans = set()
return render_template('checkout.html', rows=books)
@app.route("/checkout", methods=['POST'])
def checkout_post():
global cart
lendee = request.form['lendee']
books = Book.query.filter(Book.rfid.in_(cart)).all()
for book in books:
book.loaned_to = lendee
db.session.commit()
return redirect(url_for('index'))

- Two Scoops of Django
--------
|
"click"
------->

We did the thing!

HAQS
(Hopefully Asked Questions)
Thank you!
- For the chance to put this talk together
- For the chance to spend time with you
- For being you
- For any questions you have (or don't)
- Special thanks to Jace
What are your favorite books?
-
Everything is Illuminated
- Because I like Distributed Systems
-
Designing Data Intensive Applications
- Because I like a good adventure
You gonna Try that Async stuff at Sprints tonight?
-
Probably
- But I also need to get a better "coding on a Pi" setup
- ..and I have the setup working there...
What would you do differently Next time?
- Less Coding on a Pi
- Figure out Poetry on Pi
- Extract RFID to a service
- Tests > Guesses
Sources

Who'd I lend that book to?
By dlindema
Who'd I lend that book to?
- 703