Skip to content

Instantly share code, notes, and snippets.

@Arachnid
Created September 14, 2020 01:56
Show Gist options
  • Save Arachnid/4ecff2799656ee6db78c4fc17e180b22 to your computer and use it in GitHub Desktop.
Save Arachnid/4ecff2799656ee6db78c4fc17e180b22 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import argparse
import cups
from datetime import datetime, date
from flask import Flask, make_response, request, render_template_string
import io
import json
import logging
from reportlab.pdfgen import canvas
from reportlab.lib import units
import requests
LABEL_WIDTH = 19 * units.mm
LABEL_HEIGHT = 51 * units.mm
MARGIN_TOP = 6
MARGIN_BOTTOM = 17
BREWFATHER_USERID = ''
BREWFATHER_API_KEY = ''
INDEX_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<title>Print beer labels</title>
<style>
.beerLabel {
border: 2px solid black;
border-radius: 5px;
margin-bottom: 1em;
}
.bottlingDate {
float: right;
}
.bottlingDate, .abv {
font-weight: bold;
}
</style>
<script>
function printLabels(data, qty) {
fetch('/print?qty=' + qty, {
method: 'POST',
mode: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
}
</script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-xs-9 beerLabel">
<h2>Empties</h2>
</div>
<div class="col-xs-1">
<button onClick='javascript:printLabels({"name": "Empties", "style": "", "bottlingDate": "", "abv": ""}, 1)'>Print 1</button>
</div>
</div>
<div class="row">
<div class="col-xs-9 beerLabel">
<h2>330ml</h2>
</div>
<div class="col-xs-1">
<button onClick='javascript:printLabels({"name": "330ml", "style": "", "bottlingDate": "", "abv": ""}, 1)'>Print 1</button>
</div>
</div>
<div class="row">
<div class="col-xs-9 beerLabel">
<h2>500ml</h2>
</div>
<div class="col-xs-1">
<button onClick='javascript:printLabels({"name": "500ml", "style": "", "bottlingDate": "", "abv": ""}, 1)'>Print 1</button>
</div>
</div>
</div>
{% for batch in batches %}
<div class="row">
{% set name = batch.name %}
{% if name == "Batch" %}
{% set name = batch.recipe.name %}
{% endif %}
{% set style = "" %}
{% if batch.recipe.style is defined %}
{% set style = batch.recipe.style.name %}
{% endif %}
{% set data = {'name': name, 'style': style, 'bottlingDate': batch.bottlingDate|unixDate, 'abv': batch.measuredAbv} %}
<div class="col-xs-9 beerLabel">
<h2>{{name}}</h2>
<h3>{{style}}</h3>
<div class="bottlingDate">{{batch.bottlingDate|unixDate}}</div>
<div class="abv">{{batch.measuredAbv}}% ABV</div>
</div>
<div class="col-xs-1">
<button onClick='javascript:printLabels({{data|tojson}}, 1)'>Print 1</button>
<button onClick='javascript:printLabels({{data|tojson}}, 6)'>Print 6</button>
</div>
</div>
{% endfor %}
</div>
</body>
</html>
"""
parser = argparse.ArgumentParser(description="Print beer labels")
parser.add_argument("--printer", default="LabelWriter-450", help="Printer name")
parser.add_argument("--copies", default=1, type=int, help="Number of copies per call")
parser.add_argument("--port", default=8080, type=int, help="Port number to run on")
def drawText(c, font, size, x, y, s, leading=None):
text = c.beginText()
text.setTextOrigin(x, y)
text.setFont(font, size)
text.setLeading(leading or size)
text.textLine(s)
c.drawText(text)
def drawCenteredText(c, font, size, x, y, s, leading=None):
w = c.stringWidth(s, font, size)
drawText(c, font, size, x - w // 2, y, s, leading)
def drawRightAlignedText(c, font, size, x, y, s, leading=None):
w = c.stringWidth(s, font, size)
drawText(c, font, size, x - w, y, s, leading)
def generateLabel(beerName, beerStyle, abv, bottlingDate, copies):
data = io.BytesIO()
c = canvas.Canvas(data, pagesize=(LABEL_WIDTH, LABEL_HEIGHT))
for i in range(copies):
c.rotate(90)
drawCenteredText(c, "Helvetica-Bold", 14, LABEL_HEIGHT / 2, -16, beerName)
drawCenteredText(c, "Helvetica", 10, LABEL_HEIGHT / 2, -28, beerStyle)
drawText(c, "Helvetica-Bold", 10, MARGIN_TOP, -48, abv)
drawRightAlignedText(c, "Helvetica-Bold", 10, LABEL_HEIGHT - MARGIN_BOTTOM, -48, bottlingDate)
c.showPage()
c.save()
return data.getvalue()
def printLabels(printer, beerName, beerStyle, abv, bottlingDate, copies):
if abv:
abv = "%s%% ABV" % abv
doc = generateLabel(beerName, beerStyle, abv, bottlingDate, copies)
conn = cups.Connection()
job = conn.createJob(printer, 'labels', {'media': 'om_w167h288_58.76x101.6mm'})
conn.startDocument(printer, job, 'label.pdf', 'application/pdf', 1)
conn.writeRequestData(doc, len(doc))
conn.finishDocument(printer)
app = Flask(__name__)
args = parser.parse_args()
@app.template_filter('unixDate')
def unixDate(ts):
return datetime.utcfromtimestamp(ts / 1000).date().isoformat()
@app.route('/', methods=['GET'])
def index():
batches = requests.get('https://api.brewfather.app/v1/batches?complete=true&status=Completed', auth=(BREWFATHER_USERID, BREWFATHER_API_KEY)).json()
batches.sort(key=lambda b: -b['batchNo'])
return render_template_string(INDEX_TEMPLATE, batches=batches)
@app.route('/print', methods=['POST'])
def printLabelsHandler():
data = json.loads(request.data)
qty = int(request.args.get('qty', 1))
printLabels(args.printer, data['name'], data['style'], data['abv'], data['bottlingDate'], qty)
return ''
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
app.run(port=args.port)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment