Skip to content

Commit

Permalink
Merge pull request #15 from vshn/pimped-printing
Browse files Browse the repository at this point in the history
New label printing
  • Loading branch information
tobru authored Sep 10, 2024
2 parents de308c6 + 23e7856 commit 5fa0034
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 46 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ deployment/apps/podstatus/secret.yaml
deployment/apps/frpc/secret.yaml
deployment/apps/healthcheck/secret.yaml
kubeconfig
tmp/
2 changes: 1 addition & 1 deletion Dockerfile.contactform
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --no-root
FROM python:3.12-bookworm as runtime

Check warning on line 19 in Dockerfile.contactform

View workflow job for this annotation

GitHub Actions / build

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/

RUN apt-get update && \
apt-get install -y nginx && \
apt-get install -y nginx chromium && \
rm -rf /var/lib/apt/lists/*

ENV VIRTUAL_ENV=/app/.venv \
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ command_chaos=sh -c "notify-send -t 2000 $(curl -s -u username:password http://l
### Contactform with Printer

The Contactform app lives in the `contactform/` folder.
It serves two main purposes:
It serves these purposes:

* Collecting leads at the conference booth and store them in Odoo as CRM lead
* Printing of labels for all kind of fun, for example for the booth raffle
Expand All @@ -243,7 +243,17 @@ It allows configuration of the Odoo campaign name, the label header and can opti
As the application runs directly on the Raspberry Pi, it needs to be available on the Internet, so that a booth visitor can directly access it.
This is made possible with FRP, see next section.

The label printing was made possible thanks to the fantastic [brother_ql_web](https://github.com/FriedrichFroebel/brother_ql_web/) Python module.
The label printing is made possible thanks to the fantastic [brother_ql_web](https://github.com/FriedrichFroebel/brother_ql_web/) Python module.

#### APPUiO Voucher

The app generates an APPUiO Voucher and prints it on a label.
The generated QR code links to https://www.appuio.ch/sign-up with URL parameters to prefill the form fields for user convenience:

`?voucher=abc123&company=XYZ&name=John%20Doe&[email protected]&phone=123456789`

For this form field pre-filling to work, a small JavaScript snippet (`hack/field-values-from-url.js`) needs to be available in the APPUiO website.
It is added into a `<script>` tag via the theme configuration in Odoo.

### Connectivity from the Internet

Expand Down
56 changes: 16 additions & 40 deletions contactform/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
from flask import (
Flask,
render_template,
request,
jsonify,
flash,
redirect,
url_for,
Response,
)
from wtforms.validators import DataRequired, Email
from wtforms.fields import *
from flask_wtf import CSRFProtect, FlaskForm
from flask_bootstrap import Bootstrap5
from label_voucher import print_voucher
from label_raffle import print_raffle


from brother_ql_web.configuration import (
Expand All @@ -23,14 +22,6 @@
LabelConfiguration,
WebsiteConfiguration,
)
from brother_ql_web.labels import (
LabelParameters,
create_label_image,
image_to_png_bytes,
generate_label,
print_label,
)
from brother_ql.backends.network import BrotherQLBackendNetwork

from odoo_client import *
from config import *
Expand Down Expand Up @@ -65,12 +56,6 @@
label=LabelConfiguration(
default_size="54",
default_orientation="standard",
default_font_size=70,
default_fonts=[
Font(family="IBM Plex Sans", style="Bold"),
Font(family="Source Code Pro", style="Bold"),
],
default_font=Font(family=config.LABEL_FONT_FAMILY, style=config.LABEL_FONT_STYLE),
),
website=WebsiteConfiguration,
)
Expand Down Expand Up @@ -100,6 +85,9 @@ def index():
form = LeadForm()

if form.validate_on_submit():
# Generate a random voucher code for APPUiO
voucher_code = random_word(6)

# Append data to CSV file
csv_data = {
"Opportunity": f"Event Lead: {form.name.data}",
Expand All @@ -113,6 +101,7 @@ def index():
"Tags": config.TAG_NAME,
"Campaign": config.CAMPAIGN_NAME,
"Source": config.SOURCE_NAME,
"VoucherCode": voucher_code,
}
append_to_csv(csv_data, config.CSV_FILE_PATH)

Expand All @@ -129,7 +118,7 @@ def index():
"partner_name": form.company.data,
"country_id": form.country.data,
"phone": form.phone.data,
"description": form.notes.data,
"description": f"{form.notes.data}<br><br>APPUiO Voucher Code: {voucher_code}",
"campaign_id": config.CAMPAIGN_ID,
"source_id": config.SOURCE_ID,
"tag_ids": [(4, config.TAG_ID)],
Expand All @@ -140,30 +129,17 @@ def index():
logging.error(f"Couldn't create Lead in Odoo: {e}")

if config.PRINTING_ENABLED:
name_splitted = form.name.data.replace(" ", "\n")
label_text = f"{config.LABEL_HEADER}\n" "☺☺☺\n" f"{name_splitted}"

parameters = LabelParameters(
configuration=printer_config,
text=label_text,
label_size="54",
font_size=70,
)

qlr = generate_label(
parameters=parameters,
configuration=printer_config,
save_image_to="sample-out.png" if config.LOG_LEVEL == "DEBUG" else None,
)
try:
print_label(
parameters=parameters,
qlr=qlr,
configuration=printer_config,
backend_class=BrotherQLBackendNetwork,
if config.PRINT_APPUIO_VOUCHER:
print_voucher(
form=form,
voucher_code=voucher_code,
config=config,
printer_config=printer_config,
)
except Exception as e:
flash(f"Printing failed: {e}", "error")

if config.PRINT_RAFFLE_TICKET:
print_raffle(form=form, config=config, printer_config=printer_config)

flash("Thanks for submitting", "success")
return redirect(url_for("index"))
Expand Down
13 changes: 11 additions & 2 deletions contactform/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@ def __init__(self):
self.PRINTING_ENABLED = (
self.get_env_var("PRINTING_ENABLED", "true").lower() == "true"
)
self.PRINT_APPUIO_VOUCHER = (
self.get_env_var("PRINT_APPUIO_VOUCHER", "true").lower() == "true"
)
self.PRINT_RAFFLE_TICKET = (
self.get_env_var("PRINT_APPUIO_VOUCHER", "true").lower() == "true"
)
self.ODOO_CREATELEAD_ENABLED = (
self.get_env_var("ODOO_CREATELEAD_ENABLED", "true").lower() == "true"
)
self.CONFIG_FILE_PATH = self.get_env_var("CONFIG_FILE_PATH", "config.json")
self.BASIC_AUTH_USERNAME = self.get_env_var("BASIC_AUTH_USERNAME")
self.BASIC_AUTH_PASSWORD = self.get_env_var("BASIC_AUTH_PASSWORD")
self.LABEL_FONT_FAMILY = self.get_env_var("LABEL_FONT_FAMILY","DejaVu Sans")
self.LABEL_FONT_STYLE = self.get_env_var("LABEL_FONT_STYLE","Book")
self.LABEL_FONT_FAMILY = self.get_env_var("LABEL_FONT_FAMILY", "DejaVu Sans")
self.LABEL_FONT_STYLE = self.get_env_var("LABEL_FONT_STYLE", "Book")
self.APPUIO_SIGNUP_URL = self.get_env_var(
"APPUIO_SIGNUP_URL", "https://www.appuio.ch/sign-up"
)
self.TAG_ID = None
self.CAMPAIGN_ID = None
self.SOURCE_ID = None
Expand Down
72 changes: 72 additions & 0 deletions contactform/label_raffle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging
from flask import flash
from wtforms.fields import *
from html2image import Html2Image
from brother_ql_web.labels import (
LabelParameters,
generate_label,
print_label,
)
from brother_ql.backends.network import BrotherQLBackendNetwork


def print_raffle(form, config, printer_config):
label_filename = "label_raffle.png"

label_css = """
body, html {
margin: 0;
padding: 0;
height: 100%;
display: grid;
place-items: center;
font-family: sans-serif;
text-align: center;
}
h1 {
font-size: 70px;
}
p {
font-size: 35px;
}
"""
label_html = f"""\
<div>
<h1>{form.name.data}</h1>
<p>{config.LABEL_HEADER}</p>
</div>
"""

hti = Html2Image()
hti.size = (590, 300)
hti.screenshot(
html_str=label_html,
css_str=label_css,
save_as=label_filename,
)

label_image = open(label_filename, "rb")

parameters = LabelParameters(
configuration=printer_config,
image=label_image.read(),
label_size="54",
)

logging.info(f"Printing raffle label for {form.name.data}")
qlr = generate_label(
parameters=parameters,
configuration=printer_config,
save_image_to=(
"print-preview-raffle.png" if config.LOG_LEVEL == "DEBUG" else None
),
)
try:
print_label(
parameters=parameters,
qlr=qlr,
configuration=printer_config,
backend_class=BrotherQLBackendNetwork,
)
except Exception as e:
flash(f"Printing of raffle ticket failed: {e}", "error")
100 changes: 100 additions & 0 deletions contactform/label_voucher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import segno
import urllib
import logging


from flask import flash
from wtforms.fields import *
from html2image import Html2Image
from brother_ql_web.labels import (
LabelParameters,
generate_label,
print_label,
)
from brother_ql.backends.network import BrotherQLBackendNetwork


def print_voucher(form, voucher_code, config, printer_config):
label_filename = "label_voucher.png"
qr_code_filename = "appuio_voucher_qr.png"

label_css = """
body, html {
margin: 0;
padding: 0;
height: 100%;
display: grid;
place-items: center;
font-family: sans-serif;
text-align: center;
}
.logo {
width: 70%;
}
.text {
font-size: 45px;
}
.text_small {
font-size: 35px;
}
"""
label_html = f"""\
<div>
<p><img src="appuio-bw.png" class="logo"></p>
<p class="text">Hi {form.name.data}<p>
<p class="text">Your personal voucher code to try out APPUiO:</p>
<p class="text"><strong>{voucher_code}</strong></p>
<p class="text_small">Register here: {config.APPUIO_SIGNUP_URL}</p>
<p><img src="{qr_code_filename}"></p>
</div>
"""

registration_url_parameters = (
f"?voucher={voucher_code}"
f"&name={urllib.parse.quote(form.name.data)}"
f"&company={urllib.parse.quote(form.company.data)}"
f"&email={urllib.parse.quote(form.email.data)}"
f"&phone={urllib.parse.quote(form.phone.data)}"
)
qrcode = segno.make_qr(f"{config.APPUIO_SIGNUP_URL}{registration_url_parameters}")
qrcode.save(
qr_code_filename,
scale=5,
)

hti = Html2Image(size=(590, 1050))
hti.load_file("contactform/static/images/appuio-bw.png")
hti.load_file(qr_code_filename)
hti.browser.print_command = True if config.LOG_LEVEL == "DEBUG" else False
hti.screenshot(
html_str=label_html,
css_str=label_css,
save_as=label_filename,
)

label_image = open(label_filename, "rb")

parameters = LabelParameters(
configuration=printer_config,
image=label_image.read(),
label_size="54",
high_quality=True,
)

logging.info(f"Printing voucher label for {form.name.data}")
qlr = generate_label(
parameters=parameters,
configuration=printer_config,
save_image_to=(
"print-preview-voucher.png" if config.LOG_LEVEL == "DEBUG" else None
),
)
try:
print_label(
parameters=parameters,
qlr=qlr,
configuration=printer_config,
backend_class=BrotherQLBackendNetwork,
)
except Exception as e:
flash(f"Printing of voucher failed: {e}", "error")
Binary file added contactform/static/images/appuio-bw.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added contactform/static/images/appuio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions contactform/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import csv
import os
import random

from flask import Response, request
from functools import wraps
Expand All @@ -23,6 +24,7 @@ def append_to_csv(data, file_path):
"Tags",
"Campaign",
"Source",
"VoucherCode",
]
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)

Expand Down Expand Up @@ -61,3 +63,10 @@ def decorated(*args, **kwargs):
return f(*args, **kwargs)

return decorated


def random_word(length=5):
consonants = "bcdfghjklmnpqrstvwxyz"
vowels = "aeiou"

return "".join(random.choice((consonants, vowels)[i % 2]) for i in range(length))
Loading

0 comments on commit 5fa0034

Please sign in to comment.