class Serial(db.Model): __tablename__ = "serials" id = db.Column(db.Integer, primary_key=True) number = db.Column(db.Integer) timestamp = db.Column(db.DateTime(), index=True, default=datetime.utcnow) date = db.Column(db.Date(), default=datetime.utcnow().date) name = db.Column(db.String(300), nullable=True) n = db.Column(db.Boolean) p = db.Column(db.Boolean) # stands for proccessed , which be modified after been processed pdt = db.Column(db.DateTime()) # Fix: adding pulled by feature to tickets pulledBy = db.Column(db.Integer) office_id = db.Column(db.Integer, db.ForeignKey('offices.id')) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id')) def __init__(self, number=100, office_id=1, task_id=1, name=None, n=False, p=False, pulledBy=0): self.number = number self.office_id = office_id self.task_id = task_id self.name = name self.n = n # fixing mass use tickets multi operators conflict self.p = p self.pulledBy = pulledBy @property def task(self): return Task.query.filter_by(id=self.task_id).first() @property def office(self): return Office.query.filter_by(id=self.office_id).first()
class Touch_store(db.Model): __tablename__ = 'touchs' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(300)) message = db.Column(db.Text()) hsize = db.Column(db.String(100)) hcolor = db.Column(db.String(100)) hbg = db.Column(db.String(100)) tsize = db.Column(db.String(100)) tcolor = db.Column(db.String(100)) msize = db.Column(db.String(100)) mcolor = db.Column(db.String(100)) mbg = db.Column(db.String(100)) audio = db.Column(db.String(100)) hfont = db.Column(db.String(100)) mfont = db.Column(db.String(100)) tfont = db.Column(db.String(100)) mduration = db.Column(db.String(100)) bgcolor = db.Column(db.String(100)) p = db.Column(db.Boolean) n = db.Column(db.Boolean) tmp = db.Column(db.Integer) akey = db.Column(db.Integer, db.ForeignKey("media.id", ondelete='cascade'), nullable=True) ikey = db.Column(db.Integer, db.ForeignKey("media.id", ondelete='cascade'), nullable=True) def __init__(self, id=0, title="Please select a task to pull a tick for", hsize="500%", hcolor="rgb(129, 200, 139)", hbg="rgba(0, 0, 0, 0.50)", tsize="400%", mbg="rgba(0, 0, 0, 0.50)", msize="400%", mcolor="rgb(255, 255, 0)", tcolor="btn-danger", message="Ticket has been issued, pleas wait your turn", audio="bell_sound.wav", hfont="El Messiri", mfont="Mada", tfont="Amiri", ikey=1, tmp=2, akey=5, mduration="3000", bgcolor="bg_dark.jpg", p=False, n=True): self.id = 0 self.hfont = hfont self.mfont = mfont self.tfont = tfont self.mduration = mduration self.title = title self.message = message self.hsize = hsize self.hcolor = hcolor self.hbg = hbg self.tsize = tsize self.tcolor = tcolor self.msize = msize self.mcolor = mcolor self.mbg = mbg self.audio = audio self.bgcolor = bgcolor self.ikey = ikey self.akey = akey self.p = p self.n = n self.tmp = tmp
class Waiting(db.Model): __tablename__ = "waitings" id = db.Column(db.Integer, primary_key=True) number = db.Column(db.Integer) name = db.Column(db.String(300), nullable=True) n = db.Column(db.Boolean) office_id = db.Column(db.Integer, db.ForeignKey('offices.id')) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id')) def __init__(self, number, office_id, task_id, name=None, n=False): self.number = number self.office_id = office_id self.task_id = task_id self.name = name self.n = n
class User(UserMixin, db.Model, Mixin): __tablename__ = "users" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), unique=True) password_hash = db.Column(db.String(128)) last_seen = db.Column(db.DateTime, index=True, default=datetime.utcnow) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) def __init__(self, name, password, role_id): self.password = password self.name = name self.role_id = role_id def __str__(self): return "<%r>" % self.name @property def password(self): raise AttributeError('password not for reading !!') @password.setter def password(self, password): self.password_hash = generate_password_hash(password) def verify_password(self, password): return check_password_hash(self.password_hash, password) @classmethod def has_default_password(cls): return cls.query.filter_by( id=1).first().verify_password(DEFAULT_PASSWORD)
class Operators(db.Model, Mixin): __tablename__ = "operators" crap = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer) office_id = db.Column(db.Integer, db.ForeignKey('offices.id')) def __init__(self, id, office_id): self.id = id self.office_id = office_id
class Slides(db.Model, Mixin): __tablename__ = "slides" id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(300)) hsize = db.Column(db.String(100)) hcolor = db.Column(db.String(100)) hfont = db.Column(db.String(100)) hbg = db.Column(db.String(100)) subti = db.Column(db.String(300)) tsize = db.Column(db.String(100)) tcolor = db.Column(db.String(100)) tfont = db.Column(db.String(100)) tbg = db.Column(db.String(100)) bname = db.Column(db.String(300)) ikey = db.Column(db.Integer, db.ForeignKey("media.id", ondelete='cascade'), nullable=True)
class Vid(db.Model, Mixin): __tablename__ = "vids" id = db.Column(db.Integer, primary_key=True) vname = db.Column(db.String(300)) enable = db.Column(db.Integer) ar = db.Column(db.Integer) controls = db.Column(db.Integer) mute = db.Column(db.Integer) vkey = db.Column(db.Integer, db.ForeignKey("media.id", ondelete='cascade'), nullable=True) def __init__(self, vname="", enable=0, ar=1, controls=1, mute=2, vkey=6): self.vname = vname self.enable = enable self.ar = ar self.controls = controls self.mute = mute self.vkey = vkey
class AuthTokens(db.Model, Mixin): __tablename__ = 'auth_tokens' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100)) description = db.Column(db.String(300), nullable=True) token = db.Column(db.String(32), unique=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) def __init__(self, name, description=None): self.name = name self.description = description self.token = self.get_unique_token() # NOTE: defaulting role to Adminstator, most likely in the future # we would want this to be customizeable, with varied API permissions. self.role_id = USER_ROLE_ADMIN @classmethod def get_unique_token(cls): token = f'{uuid4()}'.replace('-', '') while cls.get(token=token): token = f'{uuid4()}'.replace('-', '') return token
class Display_store(db.Model, Mixin): __tablename__ = 'displays' id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(300)) hsize = db.Column(db.String(100)) hcolor = db.Column(db.String(100)) h2size = db.Column(db.String(100)) h2color = db.Column(db.String(100)) h2font = db.Column(db.String(100)) hbg = db.Column(db.String(100)) tsize = db.Column(db.String(100)) tcolor = db.Column(db.String(100)) ssize = db.Column(db.String(100)) scolor = db.Column(db.String(100)) audio = db.Column(db.String(10)) hfont = db.Column(db.String(100)) tfont = db.Column(db.String(100)) sfont = db.Column(db.String(100)) mduration = db.Column(db.String(100)) rrate = db.Column(db.String(100)) announce = db.Column(db.String(100)) anr = db.Column(db.Integer) anrt = db.Column(db.String(100)) effect = db.Column(db.String(100)) repeats = db.Column(db.String(100)) bgcolor = db.Column(db.String(100)) tmp = db.Column(db.Integer) prefix = db.Column(db.Boolean, default=False) always_show_ticket_number = db.Column(db.Boolean, default=False) # adding repeat announcement value r_announcement = db.Column(db.Boolean) akey = db.Column(db.Integer, db.ForeignKey("media.id", ondelete='cascade'), nullable=True) ikey = db.Column(db.Integer, db.ForeignKey("media.id", ondelete='cascade'), nullable=True) vkey = db.Column(db.Integer, db.ForeignKey("media.id", ondelete='cascade'), nullable=True) def __init__(self, id=0, title="FQM Queue Management", hsize="500%", hcolor="rgb(129, 200, 139)", h2size="600%", h2color="rgb(184, 193, 255)", h2font="Mada", hbg="rgba(0, 0, 0, 0.5)", tsize="600%", tcolor="rgb(184, 193, 255)", ssize="500%", scolor="rgb(224, 224, 224)", audio="false", hfont="El Messiri", tfont="Mada", repeats="3", effect="fade", sfont="Amiri", mduration="3000", rrate="2000", announce="en-us", ikey=4, vkey=None, akey=None, anr=2, anrt="each", bgcolor="rgb(0,0,0)", tmp=1): self.id = 0 self.tfont = tfont self.hfont = hfont self.h2font = h2font self.sfont = sfont self.mduration = mduration self.rrate = rrate self.title = title self.hsize = hsize self.hcolor = hcolor self.hsize = hsize self.hcolor = hcolor self.h2color = h2color self.h2size = h2size self.hcolor = hcolor self.hbg = hbg self.tsize = tsize self.tcolor = tcolor self.ssize = ssize self.scolor = scolor self.audio = audio self.announce = announce self.bgcolor = bgcolor self.tmp = tmp self.anr = anr self.anrt = anrt self.effect = effect self.repeats = repeats self.ikey = ikey self.vkey = vkey self.akey = akey @classmethod def get(cls): return cls.query.first()
class Serial(db.Model, TicketsMixin): __tablename__ = "serials" id = db.Column(db.Integer, primary_key=True) number = db.Column(db.Integer) timestamp = db.Column(db.DateTime(), index=True, default=datetime.utcnow) date = db.Column(db.Date(), default=datetime.utcnow().date) name = db.Column(db.String(300), nullable=True) n = db.Column(db.Boolean) p = db.Column(db.Boolean) # stands for proccessed , which be modified after been processed pdt = db.Column(db.DateTime()) # Fix: adding pulled by feature to tickets pulledBy = db.Column(db.Integer) on_hold = db.Column(db.Boolean, default=False) office_id = db.Column(db.Integer, db.ForeignKey('offices.id')) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id')) def __init__(self, number=100, office_id=1, task_id=1, name=None, n=False, p=False, pulledBy=0): self.number = number self.office_id = office_id self.task_id = task_id self.name = name self.n = n # fixing mass use tickets multi operators conflict self.p = p self.pulledBy = pulledBy @property def task(self): return Task.query.filter_by(id=self.task_id).first() @property def office(self): return Office.query.filter_by(id=self.office_id).first() @property def puller_name(self): return User.get(self.pulledBy).name @classmethod def all_office_tickets(cls, office_id): ''' get tickets of the common task from other offices. Parameters ---------- office_id: int id of the office to retreive tickets for. Returns ------- Query of office tickets unionned with other offices tickets. ''' strict_pulling = Settings.get().strict_pulling office = Office.get(office_id) all_tickets = cls.query.filter(cls.office_id == office_id, cls.number != 100) if not strict_pulling: for task in office.tasks: other_office_tickets = cls.query.filter( and_(cls.task_id == task.id, cls.office_id != office_id)) if other_office_tickets.count(): all_tickets = all_tickets.union(other_office_tickets) return all_tickets.filter(Serial.number != 100)\ .order_by(Serial.p, Serial.timestamp.desc()) @classmethod def all_task_tickets(cls, office_id, task_id): ''' get tickets related to a given task and office. Parameters ---------- office_id: int id of the office that we're querying from. task_id: int id of the task we want to retrieve its tickets. Returns ------- Query of task tickets filterred based on `strict_pulling`. ''' strict_pulling = Settings.get().strict_pulling filter_parameters = {'office_id': office_id, 'task_id': task_id} if not strict_pulling: filter_parameters.pop('office_id') return cls.query.filter_by(**filter_parameters)\ .filter(cls.number != 100)\ .order_by(cls.p, cls.timestamp.desc()) @classmethod def get_last_pulled_ticket(cls, office_id=None): ''' get the last pulled ticket. Parameters ---------- office_id: int office's id to filter last tickets by. Returns ------- Last ticket pulled record. ''' last_ticket = cls.query.filter_by(p=True)\ .filter(cls.number != 100) if office_id: last_ticket = last_ticket.filter_by(office_id=office_id) return last_ticket.order_by(cls.pdt.desc())\ .first() @classmethod def get_waiting_list_tickets(cls, office_id=None, limit=9): ''' get list of waiting tickets to be processed next. Parameters ---------- office_id: int office's id to filter tickets by. limit: int number of ticket to limit the query to. Returns ------- List of waiting list tickets. ''' waiting_tickets = cls.query.filter_by(p=False)\ .filter(cls.number != 100) if office_id: waiting_tickets = waiting_tickets.filter( cls.office_id == office_id) return waiting_tickets.order_by(cls.pdt.desc())\ .limit(limit)\ .all() def pull(self, office_id): ''' Mark a ticket as pulled and do the dues. Parameters ---------- office_id: int id of the office from which the ticket is pulled. ''' self.p = True self.pdt = datetime.utcnow() self.pulledBy = getattr(current_user, 'id', None) self.office_id = office_id db.session.add(self) db.session.commit() def toggle_on_hold(self): ''' Toggle the ticket `on_hold` status. ''' self.on_hold = not self.on_hold db.session.add(self) db.session.commit()
# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from flask_login import UserMixin, current_user from sqlalchemy.sql import and_, or_ from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime from app.middleware import db from app.constants import USER_ROLES, DEFAULT_PASSWORD mtasks = db.Table( 'mtasks', db.Column('office_id', db.Integer, db.ForeignKey('offices.id'), primary_key=True), db.Column('task_id', db.Integer, db.ForeignKey('tasks.id'), primary_key=True)) class Mixin: @classmethod def get(cls, id=False): if id is False: return cls.query.first() return cls.query.filter_by(id=id).first()
# -*- coding: utf-8 -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from flask_login import UserMixin from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime from app.middleware import db mtasks = db.Table( 'mtasks', db.Column('office_id', db.Integer, db.ForeignKey('offices.id'), primary_key=True), db.Column('task_id', db.Integer, db.ForeignKey('tasks.id'), primary_key=True)) class Mixin: @classmethod def get(cls, id): return cls.query.filter_by(id=id).first() class Office(db.Model, Mixin): __tablename__ = "offices" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Integer, unique=True) timestamp = db.Column(db.DateTime(), index=True, default=datetime.utcnow) prefix = db.Column(db.String(2)) operators = db.relationship('Operators', backref='operators') tasks = db.relationship('Task', secondary=mtasks, lazy='subquery', backref=db.backref('offices', lazy=True))
class Serial(db.Model, TicketsMixin, Mixin): __tablename__ = "serials" query_class = SerialQuery STATUS_WAITING = TICKET_WAITING STATUS_PROCESSED = TICKET_PROCESSED STATUS_UNATTENDED = TICKET_UNATTENDED id = db.Column(db.Integer, primary_key=True) number = db.Column(db.Integer) timestamp = db.Column(db.DateTime(), index=True, default=datetime.utcnow) date = db.Column(db.Date(), default=datetime.utcnow().date) name = db.Column(db.String(300), nullable=True) n = db.Column(db.Boolean) p = db.Column(db.Boolean) # stands for proccessed , which be modified after been processed pdt = db.Column(db.DateTime()) # Fix: adding pulled by feature to tickets pulledBy = db.Column(db.Integer) on_hold = db.Column(db.Boolean, default=False) status = db.Column(db.String(10), default=TICKET_PROCESSED) office_id = db.Column(db.Integer, db.ForeignKey('offices.id')) task_id = db.Column(db.Integer, db.ForeignKey('tasks.id')) ORDERS = { TICKET_ORDER_NEWEST_PROCESSED: [p, timestamp.desc()], TICKET_ORDER_NEWEST: [timestamp.desc()], TICKET_ORDER_OLDEST_PROCESSED: [p, timestamp], TICKET_ORDER_OLDEST: [timestamp] } def __init__(self, number=100, office_id=1, task_id=1, name=None, n=False, p=False, pulledBy=0, status=TICKET_WAITING): self.number = number self.office_id = office_id self.task_id = task_id self.name = name self.n = n self.p = p self.pulledBy = pulledBy self.status = status @property def task(self): return Task.query.filter_by(id=self.task_id).first() @property def office(self): return Office.query.filter_by(id=self.office_id).first() @property def puller_name(self): return User.get(self.pulledBy).name @classmethod def all_clean(cls): return cls.query.filter(cls.number != 100) @classmethod def all_office_tickets(cls, office_id, desc=True, order=True): ''' get tickets of the common task from other offices. Parameters ---------- office_id: int id of the office to retreive tickets for. desc: bool if return it in desending oreder, default is True. Returns ------- Query of office tickets unionned with other offices tickets. ''' strict_pulling = Settings.get().strict_pulling office = Office.get(office_id) all_tickets = cls.query.filter(cls.office_id == office_id, cls.number != 100) if not strict_pulling: for task in office.tasks: other_office_tickets = cls.query.filter( and_(cls.task_id == task.id, cls.office_id != office_id)) if other_office_tickets.count(): all_tickets = all_tickets.union(other_office_tickets) all_tickets = all_tickets.filter(Serial.number != 100) if order: all_tickets = all_tickets.order_by( Serial.p, Serial.timestamp.desc() if desc else Serial.timestamp) return all_tickets @classmethod def all_task_tickets(cls, office_id, task_id, order=True): ''' get tickets related to a given task and office. Parameters ---------- office_id: int id of the office that we're querying from. task_id: int id of the task we want to retrieve its tickets. Returns ------- Query of task tickets filterred based on `strict_pulling`. ''' strict_pulling = Settings.get().strict_pulling filter_parameters = {'office_id': office_id, 'task_id': task_id} if not strict_pulling: filter_parameters.pop('office_id') tickets = cls.query.filter_by(**filter_parameters)\ .filter(cls.number != 100)\ if order: return tickets.order_by(cls.p, cls.timestamp.desc()) return tickets @classmethod def get_last_pulled_ticket(cls, office_id=None): ''' get the last pulled ticket. Parameters ---------- office_id: int office's id to filter last tickets by. Returns ------- Last ticket pulled record. ''' last_ticket = cls.query.filter_by(p=True)\ .filter(cls.number != 100) if office_id: last_ticket = last_ticket.filter_by(office_id=office_id) return last_ticket.order_by(cls.pdt.desc())\ .first() @classmethod def get_waiting_list_tickets(cls, office_id=None, limit=9): ''' get list of waiting tickets to be processed next. Parameters ---------- office_id: int office's id to filter tickets by. limit: int number of ticket to limit the query to. Returns ------- List of waiting list tickets. ''' waiting_tickets = cls.query.filter_by(p=False)\ .filter(cls.number != 100) if office_id: waiting_tickets = waiting_tickets.filter( cls.office_id == office_id) return waiting_tickets.order_by(cls.pdt.desc())\ .limit(limit)\ .all() @classmethod def get_processed_tickets(cls, office_id=None, limit=9, offset=0): '''get list of last processed tickets. Parameters ---------- office_id : int, optional office id to filter tickets for, by default None limit : int, optional limit the list of ticket to it, by default 9 ''' processed_tickets = cls.query.filter(cls.p == True, cls.number != 100) if office_id: processed_tickets = processed_tickets.filter( cls.office_id == office_id) return processed_tickets.order_by(cls.pdt.desc())\ .limit(limit)\ .offset(offset)\ .all() @classmethod def get_next_ticket(cls, task_id=None, office_id=None): strict_pulling = Settings.get().strict_pulling single_row = Settings.get().single_row task = Task.get(0 if single_row else task_id) office = Office.get(0 if single_row else office_id) global_pull = not bool(task_id and office_id) next_tickets = Serial.query.filter(Serial.number != 100, Serial.p != True, Serial.on_hold == False) next_ticket = None if not global_pull: next_ticket = next_tickets.filter(Serial.task_id == task.id) if strict_pulling: next_ticket = next_ticket.filter(Serial.office_id == office.id) next_ticket = (next_tickets if global_pull else next_ticket)\ .order_by(Serial.timestamp)\ .first() if single_row: current_ticket = office.tickets\ .order_by(Serial.timestamp.desc())\ .first() next_ticket = Serial( number=getattr(current_ticket, 'number', 100) + 1, office_id=office.id, task_id=task.id) db.session.add(next_ticket) db.session.commit() return next_ticket @classmethod def create_new_ticket(cls, task, office=None, name_or_number=None): '''Create a new registered or printed ticket. Parameters ---------- task: Task instance task to link the ticket to. office: Office instance office to link the ticket to, default is None. name_or_number: str ticket's name or number value. Returns ------- Serial, exception a new ticket printed or registered ticket. ''' from app.printer import assign, printit, printit_ar, print_ticket_cli, print_ticket_cli_ar windows = os.name == 'nt' touch_screen_stings = Touch_store.get() ticket_settings = Printer.get() settings = Settings.get() printed = not touch_screen_stings.n next_number = cls.query.order_by(cls.number.desc()).first().number + 1 office = office or task.least_tickets_office() ticket, exception = None, None if printed: tickets = Serial.all_office_tickets(office.id, desc=False) current_ticket = getattr(tickets.first(), 'number', None) common_arguments = (f'{office.prefix}.{next_number}', f'{office.prefix}{office.name}', tickets.count(), task.name, f'{office.prefix}.{current_ticket}') try: if windows or settings.lp_printing: (print_ticket_cli_ar if ticket_settings.langu == 'ar' else 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) (printit_ar if ticket_settings.langu == 'ar' else printit)( printer, *common_arguments, lang=ticket_settings.langu, scale=ticket_settings.scale) except Exception as e: exception = e if not exception: ticket = Serial(number=next_number, office_id=office.id, task_id=task.id, name=name_or_number, n=not printed) db.session.add(ticket) db.session.commit() return ticket, exception def pull(self, office_id=None, puller_id=None): ''' Mark a ticket as pulled and do the dues. Parameters ---------- office_id: int id of the office from which the ticket is pulled. ''' self.p = True self.pdt = datetime.utcnow() self.pulledBy = puller_id or getattr(current_user, 'id', None) self.status = TICKET_PROCESSED if office_id: self.office_id = office_id db.session.add(self) db.session.commit() def toggle_on_hold(self): ''' Toggle the ticket `on_hold` status. ''' self.on_hold = not self.on_hold db.session.add(self) db.session.commit()