Skip to content

Commit

Permalink
Add lp printing option for unix-like. Resolves #125
Browse files Browse the repository at this point in the history
  • Loading branch information
mrf345 committed Jun 8, 2020
1 parent 6915e8c commit bba0489
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 55 deletions.
5 changes: 4 additions & 1 deletion app/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,9 +649,12 @@ class Settings(db.Model, Mixin):
notifications = db.Column(db.Boolean, nullable=True)
strict_pulling = db.Column(db.Boolean, nullable=True)
visual_effects = db.Column(db.Boolean, nullable=True)
lp_printing = db.Column(db.Boolean, nullable=True)

def __init__(self, notifications=True, strict_pulling=True, visual_effects=True):
def __init__(self, notifications=True, strict_pulling=True, visual_effects=True,
lp_printing=False):
self.id = 0
self.notifications = notifications
self.strict_pulling = strict_pulling
self.visual_effects = visual_effects
self.lp_printing = lp_printing
4 changes: 2 additions & 2 deletions app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ class Printer_f(FlaskForm):
scale = SelectField(coerce=int)
submit = SubmitField('Set ticket')

def __init__(self, inspected_printers_from_view, defLang='en', *args, **kwargs):
def __init__(self, inspected_printers_from_view, lp_printing, defLang='en', *args, **kwargs):
super(Printer_f, self).__init__(*args, **kwargs)
self.kind.label = gtranslator.translate("Select type of ticket to use : ", 'en', [defLang])
self.kind.choices = [
Expand All @@ -571,7 +571,7 @@ def __init__(self, inspected_printers_from_view, defLang='en', *args, **kwargs):

if inspected_printers_from_view:
for printer in inspected_printers_from_view:
if name == 'nt':
if name == 'nt' or lp_printing:
printer_choices.append((f'{printer}', f'Printer Name: {printer}'))
else:
vendor, product = printer.get('vendor'), printer.get('product')
Expand Down
7 changes: 4 additions & 3 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from sqlalchemy.exc import OperationalError

from app.middleware import db, login_manager, files, gtranslator, gTTs, migrate
from app.printer import get_printers
from app.printer import get_printers_usb
from app.views.administrate import administrate
from app.views.core import core
from app.views.customize import cust_app
Expand Down Expand Up @@ -115,7 +115,7 @@ def bundle_app(config={}):
if os.name != 'nt':
# !!! it did not work creates no back-end available error !!!
# !!! strange bug , do not remove !!!
if get_printers():
if get_printers_usb():
pass

@app.route('/language_switch/<language>')
Expand Down Expand Up @@ -198,6 +198,7 @@ def inject_vars():
return dict(path=path, adme=admin_route, brp=Markup('<br>'), ar=ar, version=VERSION, str=str,
defLang=session.get('lang'), getattr=getattr, settings=Settings.get(), Serial=Serial,
checkId=lambda id, records: id in [i.id for i in records], offices=Office.query.all(),
moment_wrapper=moment_wrapper, current_path=quote(request.path, safe=''))
moment_wrapper=moment_wrapper, current_path=quote(request.path, safe=''),
windows=os.name == 'nt', unix=os.name != 'nt')

return app
95 changes: 79 additions & 16 deletions app/printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from PIL import Image, ImageDraw, ImageFont
from os import remove, getcwd, path, name, system

from app.utils import absolute_path, get_with_alias, log_error, convert_to_int_or_hex
from app.utils import absolute_path, get_with_alias, log_error, convert_to_int_or_hex, execute
from app.middleware import gtranslator
from app.constants import VERSION, PRINTED_TICKET_DIMENSIONS, PRINTED_TICKET_MAXIMUM_HEIGH_OR_WIDTH

Expand Down Expand Up @@ -67,7 +67,7 @@ def multiply_within_limit(height_or_width):
'width': multiply_within_limit(width)}


def get_printers():
def get_printers_usb():
''' Get list of printers via `PyUSB.core.find`.
Returns
Expand Down Expand Up @@ -99,6 +99,35 @@ def get_printers():
return printers


def get_printers_cli(windows=False, unix=False):
'''Get list of printers via `os.system('lpstat - a')`
Parameters
----------
windows: bool
if Windows system
unix: bool
if unix-like system
Returns
-------
list of printer names.
'''
printers = []

if unix:
printers += [p.split(' ')[0]
for p in execute('lpstat -a', parser='\n')
if p.split(' ')]
elif windows:
printers += execute('wmic printer get sharename',
parser='\n',
encoding='utf-16'
)[1:]

return printers


def assign(vendor_id, product_id, in_ep=None, out_ep=None):
''' Assign a new printer session and attach it.
Expand Down Expand Up @@ -161,19 +190,53 @@ def printit(printer, ticket, office, tnumber,
return printer


def print_ticket_windows(pname, a, b, c, d, cit, ip, l='en', scale=1):
content = printit(Dummy(), a, b, c, d, cit, lang=l, scale=scale).output
def print_ticket_cli(printer, ticket, office, tickets_ahead, task, current_ticket,
host='localhost', language='en', scale=1, windows=False, unix=False):
'''Print a ticket through the Command-Line interface.
Parameters
----------
printer : str
the printer name.
ticket : int
ticket number.
office : str
office number and prefix.
tickets_ahead : int
number of tickets ahead.
task : str
task's name.
current_ticket : int
current ticket in the queue.
host : str, optional
host to find printer on, by default 'localhost'
language : str, optional
printing language, by default 'en'
scale : int, optional
ticket font scale, by default 1
windows : bool, optional
if printing on Windows, by default False
unix : bool, optional
if printing on Unix-like, by default False
'''
ticket_content = printit(Dummy(), ticket, office, tickets_ahead, task,
current_ticket, lang=language, scale=scale).output
file_path = path.join(getcwd(),
f'{uuid.uuid4()}'.replace('-', '') + '.txt')

with open(file_path, 'wb+') as file:
file.write(content)
file.write(ticket_content)

if unix:
system(f'lp -d "{printer}" -o raw "{file_path}"')
elif windows:
system(f'print /D:\\\{host}\\"{printer}" "{file_path}"')

system(f'print /D:\\\localhost\\"{pname}" "{file_path}"')
path.isfile(file_path) and remove(file_path)
if path.isfile(file_path):
remove(file_path)


def printit_ar(pname, ti, ofc, tnu, tas, cticket, lang=None):
def printit_ar(pname, ti, ofc, tnu, tas, cticket, **kwargs):
def fsizeit(text, t, f):
ltxt = "A" * len(t)
return f.getsize(t)
Expand Down Expand Up @@ -289,25 +352,22 @@ def center(text, t, f):
iname = 'dummy.jpg'
finame = path.join(getcwd(), iname)
mt.save(iname, format="JPEG")
pname.image(iname, fragment_height=tt, high_density_vertical=True)
pname.image(finame, fragment_height=tt, high_density_vertical=True)
pname.cut()
pname.close()
if path.isfile(finame):
remove(finame)


def print_ticket_windows_ar(pname, ti, ofc, tnu, tas, cticket, ip, l=None):
def print_ticket_cli_ar(pname, ti, ofc, tnu, tas, cticket, host='localhost', **kwargs):
def fsizeit(text, t, f):
return f.getsize(t)

def center(text, t, f):
fs1, fs2 = fsizeit(text, t, f)
return ((text.size[0] - fs1) / 2, (text.size[1] - fs2) / 2)

if name == 'nt':
fpath = absolute_path('static\\gfonts\\arial.ttf')
else:
fpath = absolute_path('static/gfonts/arial.ttf')
fpath = absolute_path('static/gfonts/arial.ttf')
fonts = [ImageFont.truetype(fpath, 50),
ImageFont.truetype(fpath, 30),
ImageFont.truetype(fpath, 25)]
Expand Down Expand Up @@ -426,8 +486,11 @@ def center(text, t, f):
p.close()
f.close()

text = f'print /D:\\\localhost\\"{pname}" "{sfs[1]}"'
system(text)
if kwargs.get('windows'):
system(f'print /D:\\\{host}\\"{pname}" "{sffs[1]}"')
elif kwargs.get('unix'):
system(f'lp -d "{pname}" -o raw "{sffs[1]}"')

for f in sffs:
if path.isfile(f):
remove(f)
26 changes: 14 additions & 12 deletions app/views/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/. '''

import os
import urllib
from sys import platform
from flask import url_for, flash, render_template, redirect, session, jsonify, Blueprint, current_app
from flask import url_for, flash, render_template, redirect, session, jsonify, Blueprint
from flask_login import current_user, login_required, login_user

import app.forms as forms
import app.database as data
from app.printer import assign, printit, printit_ar, print_ticket_windows, print_ticket_windows_ar
from app.printer import assign, printit, printit_ar, print_ticket_cli, print_ticket_cli_ar
from app.middleware import db, gtranslator
from app.utils import log_error
from app.helpers import (reject_no_offices, reject_operator, is_operator, reject_not_admin,
Expand Down Expand Up @@ -64,11 +63,13 @@ def logged_in_all_good():
@core.route('/serial/<int:t_id>/<int:office_id>', methods=['GET', 'POST'])
def serial(t_id, office_id=None):
''' generate a new ticket and print it. '''
windows = os.name == 'nt'
form = forms.Touch_name(session.get('lang'))
task = data.Task.get(t_id)
office = data.Office.get(office_id)
touch_screen_stings = data.Touch_store.query.first()
ticket_settings = data.Printer.query.first()
touch_screen_stings = data.Touch_store.get()
ticket_settings = data.Printer.get()
settings = data.Settings.get()
printed = not touch_screen_stings.n
numeric_ticket_form = ticket_settings.value == 2
name_or_number = form.name.data or None
Expand Down Expand Up @@ -100,13 +101,14 @@ def serial(t_id, office_id=None):
f'{office.prefix}.{current_ticket}')

try:
if os.name == 'nt':
(print_ticket_windows_ar
if windows or settings.lp_printing:
(print_ticket_cli_ar
if ticket_settings.langu == 'ar' else
print_ticket_windows)(ticket_settings.name,
*common_arguments,
ip=current_app.config.get('LOCALADDR'),
l=ticket_settings.langu)
print_ticket_cli)(ticket_settings.name,
*common_arguments,
language=ticket_settings.langu,
windows=windows,
unix=not windows)
else:
printer = assign(ticket_settings.vendor, ticket_settings.product,
ticket_settings.in_ep, ticket_settings.out_ep)
Expand All @@ -118,7 +120,7 @@ def serial(t_id, office_id=None):
flash('Error: you must have available printer, to use printed', 'danger')
flash('Notice: make sure that printer is properly connected', 'info')

if os.name == 'nt':
if windows:
flash('Notice: Make sure to make the printer shared on the local network', 'info')
elif 'linux' in platform:
flash('Notice: Make sure to execute the command `sudo gpasswd -a $(users) lp` and '
Expand Down
21 changes: 11 additions & 10 deletions app/views/customize.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import app.forms as forms
import app.database as data
from app.middleware import db, files
from app.printer import get_printers
from app.utils import absolute_path, getFolderSize, execute, convert_to_int_or_hex
from app.printer import get_printers_cli, get_printers_usb
from app.utils import absolute_path, getFolderSize, convert_to_int_or_hex
from app.constants import SUPPORTED_MEDIA_FILES
from app.helpers import (reject_not_admin, get_tts_safely, reject_slides_enabled,
reject_videos_enabled)
Expand All @@ -35,13 +35,14 @@ def customize():
def ticket():
''' view of ticket customization '''
windows = os.name == 'nt'
printers = execute('wmic printer get sharename',
parser='\n',
encoding='utf-16'
)[1:] if windows else get_printers()
form = forms.Printer_f(printers, session.get('lang'))
touch_screen_settings = data.Touch_store.get()
settings = data.Settings.get()
printer = data.Printer.get()
touch_screen_settings = data.Touch_store.get()
printers = get_printers_cli(windows=windows, unix=not windows)\
if windows or settings.lp_printing else get_printers_usb()
form = forms.Printer_f(printers,
settings.lp_printing,
session.get('lang'))

if form.validate_on_submit():
if form.kind.data == 1: # Rigestered
Expand All @@ -56,7 +57,7 @@ def ticket():
'danger')
return redirect(url_for('cust_app.ticket'))

if windows:
if windows or settings.lp_printing:
printer.name = printer_id
else:
id_chunks = printer_id.split('_')
Expand Down Expand Up @@ -86,7 +87,7 @@ def ticket():
form.value.data = printer.value
form.scale.data = printer.scale

if windows:
if windows or settings.lp_printing:
form.printers.data = printer.name or ''
else:
form.printers.data = f'{printer.vendor}_{printer.product}'
Expand Down
7 changes: 7 additions & 0 deletions gt_cached.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,13 @@
"fr": "Permet \u00e0 l'administrateur, autoris\u00e9 par les op\u00e9rateurs admin de rechercher avec des param\u00e8tres donn\u00e9s du num\u00e9ro de ticket, du pr\u00e9fixe de ticket et / ou de la date du ticket pour atteindre le ticket sp\u00e9cifique destin\u00e9 \u00e0 \u00eatre recherch\u00e9 de mani\u00e8re dynamique et r\u00e9parable.",
"it": "Consente all'amministratore, autorizzato dagli amministratori, di cercare con determinati parametri il numero del biglietto, il prefisso del biglietto e la data del biglietto per raggiungere lo specifico biglietto destinato alla ricerca in modo dinamico e risolvibile, che era ben inteso nel design."
},
"Alternative printer driver": {
"ar": "\u0628\u0631\u0646\u0627\u0645\u062c \u062a\u0634\u063a\u064a\u0644 \u0627\u0644\u0637\u0627\u0628\u0639\u0629 \u0627\u0644\u0628\u062f\u064a\u0644",
"en": "Alternative printer driver",
"es": "controlador de impresora Alternativa",
"fr": "pilote d'imprimante alternatif",
"it": "driver di stampa alternativo"
},
"Always show ticket number: ": {
"ar": "\u062a\u0638\u0647\u0631 \u062f\u0627\u0626\u0645\u0627 \u0631\u0642\u0645 \u0627\u0644\u062a\u0630\u0643\u0631\u0629:",
"en": "Always show ticket number:",
Expand Down
28 changes: 28 additions & 0 deletions migrations/versions/9e5b998a4c7b_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
""" Add `lp_printing` flag setting.
Revision ID: 9e5b998a4c7b
Revises: b41c62db00a1
Create Date: 2020-06-08 15:33:00.315047
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '9e5b998a4c7b'
down_revision = 'b41c62db00a1'
branch_labels = None
depends_on = None


def upgrade():
try:
op.add_column('settings', sa.Column('lp_printing', sa.Boolean(), nullable=True))
except Exception:
pass


def downgrade():
with op.batch_alter_table('settings') as batch:
batch.drop_column('lp_printing')
9 changes: 9 additions & 0 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@
{{ translate('Disable' if settings.notifications == True else 'Enable', 'en', [defLang]) }}
</a>
</li>
{% if unix %}
<li class="dropdown-header">( {{ translate('Alternative printer driver', 'en', [defLang]) }} )</li>
<li>
<a href="{{ url_for('core.settings', setting='lp_printing', togo=current_path) }}">
<span class="fa fa-print"></span>
{{ translate('Disable' if settings.lp_printing == True else 'Enable', 'en', [defLang]) }}
</a>
</li>
{% endif %}
{% endif %}
</ul>
</li>
Expand Down
Loading

0 comments on commit bba0489

Please sign in to comment.