def offers(): """ Page to accept and decline substitution requests :return: """ from openstudio.os_class import Class from openstudio.os_classes_otc_sub_availables import ClassesOTCSubAvailables cotcID = request.vars['cotcID'] cotc = db.classes_otc(cotcID) cls = Class(cotc.classes_id, cotc.ClassDate) response.title = T('Classes') response.subtitle = T('Sub teachers') response.view = 'general/tabs_menu.html' subs_avail = ClassesOTCSubAvailables() table = subs_avail.list_formatted(cotcID) content = DIV( H4(T('Sub offers')), H5(cls.get_name()), table ) back = os_gui.get_button( 'back', URL('index') ) return dict( content=content, menu=get_menu('index'), back=back )
def checkin_booking_options(): """ List booking options for a customer """ cuID = request.vars['cuID'] clsID = request.vars['clsID'] date_formatted = request.vars['date'] date = datestr_to_python(DATE_FORMAT, date_formatted) customer = Customer(cuID) cls = Class(clsID, date) starttime = cls.cls.Starttime.strftime(TIME_FORMAT) pretty_date = date.strftime('%B %d, %Y') classtype = db.school_classtypes(cls.cls.school_classtypes_id) location = db.school_locations(cls.cls.school_locations_id) response.title = T(classtype.Name) response.subtitle = SPAN(starttime, ' ', pretty_date) response.view = 'selfcheckin/checkin.html' return_url = URL('selfcheckin', 'checkin', vars={ 'clsID': clsID, 'date': date_formatted }) cls = Class(clsID, date) ah = AttendanceHelper() options = ah.get_customer_class_booking_options_formatted( clsID, date, customer, trial=True, list_type='selfcheckin', controller='classes') cancel = os_gui.get_button('noicon', return_url, title=T('Cancel'), btn_size='') content = DIV(H3(T('Booking options for'), ' ', customer.row.display_name, _class='center'), BR(), options, DIV(BR(), cancel, _class="col-md-12 center"), _class="row") back = os_gui.get_button('back', return_url) return dict(content=content, back=back)
def email_reminders_teachers_sub_request_open(self): """ Send teachers reminders when a sub for their class hasn't been found yet. :return: """ from openstudio.os_class import Class from openstudio.os_mail import OsMail from openstudio.os_sys_email_reminders import SysEmailReminders T = current.T db = current.db TODAY_LOCAL = current.TODAY_LOCAL # Check if reminders configured sys_reminders = SysEmailReminders('teachers_sub_request_open') reminders = sys_reminders.list() mails_sent = 0 for reminder in reminders: # Get list of open classes on reminder date reminder_date = TODAY_LOCAL + datetime.timedelta(reminder.Days) query = (db.classes_otc.Status == 'open') & \ (db.classes_otc.ClassDate == reminder_date) rows = db(query).select(db.classes_otc.ALL) for row in rows: clsID = row.classes_id cls = Class(clsID, row.ClassDate) regular_teachers = cls.get_regular_teacher_ids() if not regular_teachers['error']: auth_teacher_id = regular_teachers['auth_teacher_id'] teacher = db.auth_user(auth_teacher_id) os_mail = OsMail() result = os_mail.render_email_template( 'teacher_sub_request_open_reminder', classes_otc_id=row.id, return_html=True) send_result = False if not result['error']: send_result = os_mail.send( message_html=result['html_message'], message_subject=T("Reminder - open class"), auth_user_id=auth_teacher_id) if send_result: mails_sent += 1 # send reminder to teacher return "Sent mails: %s" % mails_sent
def _render_email_template_teacher_sub_request_open_reminder( self, template_content, cotcID): """ Render mail template for teacher no sub found yet reminders :param cotcID: db.classes_otc.id :return: html - mail body for reminder """ from openstudio.os_class import Class T = current.T db = current.db DATE_FORMAT = current.DATE_FORMAT TIME_FORMAT = current.TIME_FORMAT error = False error_msg = '' cotc = db.classes_otc(cotcID) cls = Class(cotc.classes_id, cotc.ClassDate) regular_teachers = cls.get_regular_teacher_ids() teacher_name = '' if not regular_teachers['error']: auth_teacher_id = regular_teachers['auth_teacher_id'] teacher = db.auth_user(auth_teacher_id) teacher_name = teacher.first_name class_info = TABLE(TR( TH(T('Date'), _align="right"), TD(cotc.ClassDate.strftime(DATE_FORMAT), _align="left")), TR( TH(T('Time'), _align="right"), TD(cls.cls.Starttime.strftime(TIME_FORMAT), ' - ', cls.cls.Endtime.strftime(TIME_FORMAT), _align="left")), TR(TH(T('Location'), _align="right"), TD(cls.get_location_name(), _align="left")), TR(TH(T('Class'), _align="right"), TD(cls.get_classtype_name(), _align="left")), _cellspacing="0", _cellpadding='5px', _width='100%', border="0") description = class_info content = XML(template_content.format(teacher_name=teacher_name, )) return dict(error=error, error_msg=error_msg, content=content, description=description)
def class_cancel_confirm(): """ Ask user for confirmation about really cancelling booking for a class """ from openstudio.os_class_attendance import ClassAttendance from openstudio.os_class import Class clattID = request.vars['clattID'] clatt = ClassAttendance(clattID) if not clatt.row.auth_customer_id == auth.user.id: return T('This class is not yours') response.title = T('Confirmation') response.subtitle = T('Cancel class booking?') response.view = 'profile/index.html' cls = Class(clatt.row.classes_id, clatt.row.ClassDate) name = XML(cls.get_name_shop()) yes = A(T('Yes'), _href=URL('class_cancel', vars={'clattID':clattID}), _class='btn btn-primary') return_url = class_cancel_get_return_url() no = A(T('No'), _href=return_url, _class='btn btn-default') if clatt.get_cancellation_possible(): buttons = DIV(yes, no, _class='') else: hours_limit = get_sys_property('shop_classes_cancellation_limit') buttons = DIV(B(T("We're sorry to inform you that this booking can no longer be cancelled.")), BR(), SPAN(T('We accept cancellations until'), ' ', hours_limit, ' ', T('hours before the start of a class.'), _class='grey'), BR(), A(T('Back'), _href=URL('profile', 'classes'), _class='btn btn-link')) content = DIV(DIV(DIV(H3(T('Are you sure you want to cancel your booking for the following class?')), H4(name), BR(), buttons), _class='box-body'), _class='box box-primary center') back = os_gui.get_button('back', return_url) return dict(content=content, back=back)
def get_class_teacher_payment(): """ :return: """ from openstudio.os_class import Class set_headers() clsID = request.vars['clsID'] date_received = request.vars['date'] date = datestr_to_python("%Y-%m-%d", date_received) cls = Class(clsID, date) return dict(payment=cls.get_teacher_payment())
def _render_email_class_info_mail(self, clattID): """ :param template_content: Mail content :param workshops_products_id: db.workshops_products.id :return: mail body for workshop """ from .os_class_attendance import ClassAttendance from .os_class import Class from .os_customer import Customer db = current.db T = current.T DATE_FORMAT = current.DATE_FORMAT TIME_FORMAT = current.TIME_FORMAT clatt = ClassAttendance(clattID) clsID = clatt.row.classes_id date = clatt.row.ClassDate cls = Class(clsID, date) customer = Customer(clatt.row.auth_customer_id) description = TABLE(TR(TH(T('Date')), TD(clatt.row.ClassDate.strftime(DATE_FORMAT), _align="left")), TR(TH(T('Time')), TD(cls.get_starttime(), _aligh="left")), TR(TH(T('Class')), TD(cls.get_classtype_name(), _aligh="left")), TR(TH(T('Location')), TD(cls.get_location_name(), _aligh="left")), _cellspacing="0", _cellpadding='5px', _width='100%', border="0") content = '' class_otc_mail = db.classes_otc_mail(classes_id=clsID, ClassDate=date) class_mail = db.classes_mail(classes_id=clsID) if class_otc_mail: content = class_otc_mail.MailContent or "" elif class_mail: content = class_mail.MailContent or "" return dict( content=DIV( XML(content.format( customer_first_name = customer.row.first_name )) ), description=description )
def _render_email_template_teacher_sub_offer(self, template_content, classes_otc_sub_avail_id): """ :param classes_otc_sub_avail_id: :return: mail body for declined class sub request """ from openstudio.os_class import Class from openstudio.os_teacher import Teacher db = current.db T = current.T DATE_FORMAT = current.DATE_FORMAT TIME_FORMAT = current.TIME_FORMAT cotcsa = db.classes_otc_sub_avail(classes_otc_sub_avail_id) teacher = Teacher(cotcsa.auth_teacher_id) cotc = db.classes_otc(cotcsa.classes_otc_id) cls = Class(cotc.classes_id, cotc.ClassDate) class_info = TABLE(TR( TH(T('Date'), _align="right"), TD(cotc.ClassDate.strftime(DATE_FORMAT), _align="left")), TR( TH(T('Time'), _align="right"), TD(cls.cls.Starttime.strftime(TIME_FORMAT), ' - ', cls.cls.Endtime.strftime(TIME_FORMAT), _align="left")), TR(TH(T('Location'), _align="right"), TD(cls.get_location_name(), _align="left")), TR(TH(T('Class'), _align="right"), TD(cls.get_classtype_name(), _align="left")), _cellspacing="0", _cellpadding='5px', _width='100%', border="0") content = XML( template_content.format(teacher_name=teacher.get_first_name())) return dict(content=content, description=class_info)
def _render_email_template_teacher_sub_requests_daily_summary( self, template_content, auth_user_id): """ :param template_content: :param auth_user_id: :return: """ from openstudio.os_class import Class from openstudio.os_teacher import Teacher from openstudio.tools import OsTools from openstudio.os_classes_otcs import ClassesOTCs db = current.db T = current.T os_tools = OsTools() cotcs = ClassesOTCs() teacher = Teacher(auth_user_id) DATE_FORMAT = current.DATE_FORMAT TIME_FORMAT = current.TIME_FORMAT error = False error_msg = '' date_from = current.TODAY_LOCAL + datetime.timedelta(days=1) date_until = date_from + datetime.timedelta(days=45) # G get list of allowed class types query = (db.teachers_classtypes.auth_user_id == auth_user_id) classtype_rows = db(query).select(db.teachers_classtypes.ALL) ct_ids = [] for row in classtype_rows: ct_ids.append(int(row.school_classtypes_id)) open_classes_for_teacher = 0 open_classes = '' description = '' if ct_ids: sys_hostname = os_tools.get_sys_property('sys_hostname') description = XML( template_content.format(teacher_name=teacher.get_first_name(), link_employee_portal=URL( 'ep', 'index', scheme='https', host=sys_hostname))) open_classes = TABLE( THEAD( TR( TH(T("Date"), _align="left"), TH(T("Time"), _align="left"), TH(T("Location"), _align="left"), TH(T("Class"), _align="left"), # TH(), )), _cellspacing="0", _cellpadding='5px', _width='100%', border="0") # Get Open classes in the next 45 days rows = cotcs.get_sub_teacher_rows(date_from, date_until, school_classtypes_ids=ct_ids, only_open=True) for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] date = row.classes_otc.ClassDate clsID = row.classes.id cls = Class(clsID, date) regular_teachers = cls.get_regular_teacher_ids() if regular_teachers['auth_teacher_id'] == auth_user_id: continue open_classes.append( TR( TD(repr_row.classes_otc.ClassDate, _align="left"), TD(repr_row.classes.Starttime, _align="left"), TD(repr_row.classes.school_locations_id, _align="left"), TD(repr_row.classes.school_classtypes_id, _align="left"), # TD('Actions here?'), )) open_classes_for_teacher += 1 if not open_classes_for_teacher: error = True error_msg = T( "No upcoming classes with subs required found for this teacher" ) return dict(content=open_classes, description=description, error=error, error_msg=error_msg)
def checkin(): """ Checkin page for self checkin """ clsID = request.vars['clsID'] date_formatted = request.vars['date'] date = datestr_to_python(DATE_FORMAT, date_formatted) session.customers_load_list_search_name = None session.classes_attendance_signin_back = 'self_checkin' cls = Class(clsID, date) starttime = cls.cls.Starttime.strftime(TIME_FORMAT) pretty_date = date.strftime('%B %d, %Y') classtype = db.school_classtypes(cls.cls.school_classtypes_id) location = db.school_locations(cls.cls.school_locations_id) response.title = T(classtype.Name) response.subtitle = SPAN(starttime, ' ', pretty_date) response.logout = A(SPAN(_class='glyphicon glyphicon-chevron-left'), _href=URL('classes', vars={'locID': cls.cls.school_locations_id})) #response.view = 'templates/selfcheckin/checkin.html' # Check if the class is full full = cls.get_full() message = '' if full: message = DIV( BR(), os_gui.get_alert( 'info', SPAN( B(T('Notice')), BR(), T("This class is full, it's no longer possible to check in" )), dismissable=False)) search_results = DIV(LOAD( 'customers', 'load_list.load', target='attendance_list_customers_list', content=os_gui.get_ajax_loader(message=T("Searching...")), vars={ 'list_type': 'selfcheckin_checkin', 'items_per_page': 10, 'clsID': clsID, 'date': date_formatted, 'pictures': False }, ajax=True), _id="attendance_list_customers_list", _class="load_list_customers clear") name = '' show_subscriptions_prop = 'selfcheckin_show_subscriptions' show_subscriptions = get_sys_property(show_subscriptions_prop) if show_subscriptions: show_subscriptions = True else: show_subscriptions = False ah = AttendanceHelper() customers = ah.get_checkin_list_customers_booked( clsID, date, pictures=False, invoices=False, show_notes=False, reservations=False, manage_checkin=False, show_booking_time=False, show_subscriptions=show_subscriptions, class_full=full) form = checkin_get_search_form(clsID, date, name, request.function) content = DIV( SPAN(location.Name, _class='grey'), message, customers, ) if not full: content.append( DIV(H2(T("I'm not yet on the list...")), form, search_results)) return dict(content=content)
def render_email_template(self, email_template, title='', subject='', description='', comments='', template_content=None, auth_user_id=None, customers_orders_id=None, customer_subscriptions_id=None, customers_classcards_id=None, invoices_id=None, invoices_payments_id=None, classes_attendance_id=None, classes_otc_id=None, classes_otc_sub_avail_id=None, workshops_products_customers_id=None, return_html=False): """ Renders default email template uses the render function from gluon.template instead of response.render response throws a RestrictedError when run from the scheduler or shell... and we do want scheduled emails to be rendered :) """ # from gluon.template import parse_template from gluon.template import render db = current.db T = current.T DATETIME_FORMAT = current.DATETIME_FORMAT error = False error_msg = '' request = current.request logo = self._render_email_template_get_logo() template_name = 'default.html' template_path = os.path.join(request.folder, 'views', 'templates', 'email') # Get template if template_content is None: # Get email template from db template_content = self.get_email_template(email_template) # Render template if email_template == 'order_received': subject = T('Order received') # do some pre-processing to show the correct order info content = self._render_email_template_order(template_content, customers_orders_id) # Check for order message from .os_order import Order order = Order(customers_orders_id) if order.order.CustomerNote: comments = DIV( T("We received the following message with your order:"), BR(), BR(), XML(order.order.CustomerNote.replace('\n', '<br>')) ) elif email_template == 'order_delivered': subject = T('Order delivered') # do some pre-processing to show the correct order info content = self._render_email_template_order(template_content, customers_orders_id) elif email_template == 'payment_recurring_failed': subject = T('Recurring payment failed') content = self._render_email_template_payment_recurring_failed(template_content) elif email_template == "subscription_created": subject = T("Subscription activated") result = self._render_email_template_subscription_created( template_content, customer_subscriptions_id ) title = T("Your subscription has been activated!") description = result['description'] content = result['content'] elif email_template == 'teacher_sub_requests_daily_summary': result = self._render_email_template_teacher_sub_requests_daily_summary( template_content, auth_user_id ) title = T("Daily summary - open classes") description = result['description'] content = result['content'] error = result['error'] error_msg = result['error_msg'] elif email_template == 'teacher_sub_request_open_reminder': result = self._render_email_template_teacher_sub_request_open_reminder( template_content, classes_otc_id ) title = T("A friendly reminder") description = result['description'] content = result['content'] error = result['error'] error_msg = result['error_msg'] elif email_template == 'teacher_sub_offer_declined': result = self._render_email_template_teacher_sub_offer( template_content, classes_otc_sub_avail_id ) title = T("Substitute offer declined") description = result['description'] content = result['content'] elif email_template == 'teacher_sub_offer_accepted': result = self._render_email_template_teacher_sub_offer( template_content, classes_otc_sub_avail_id ) title = T("Thank you for teaching this class") description = result['description'] content = result['content'] elif email_template == "trial_follow_up": result = self._render_email_trial_follow_up( template_content, classes_attendance_id=classes_attendance_id, customers_classcards_id=customers_classcards_id, ) subject = T("Trial follow up") content = result['content'] elif email_template == 'classes_info_mail': from .os_class import Class from .os_class_attendance import ClassAttendance clatt = ClassAttendance(classes_attendance_id) cls = Class(clatt.row.classes_id, clatt.row.ClassDate) class_name = cls.get_name() subject = T("Class booking") + " " + class_name title = T("We've reserved your spot!") result = self._render_email_class_info_mail(classes_attendance_id) content = result['content'] description = result['description'] elif email_template == 'workshops_info_mail': wspc = db.workshops_products_customers(workshops_products_customers_id) wsp = db.workshops_products(wspc.workshops_products_id) ws = db.workshops(wsp.workshops_id) subject = ws.Name title = ws.Name result = self._render_email_workshops_info_mail(wspc, wsp, ws) content = result['content'] description = result['description'] elif (email_template == 'sys_verify_email' or email_template == 'sys_reset_password'): template_name = 'default_simple.html' content = XML(template_content) subject = subject else: template_name = 'default.html' content = XML(template_content) subject = subject footer = XML(self.get_email_template('sys_email_footer')) template = os.path.join( template_path, template_name ) context = dict( logo = logo, title = title, description = description, content = content, comments = comments, footer = footer, request = request ) html_message = render( filename = template, path = template_path, context = context ) if return_html: return dict( msg_subject = subject or "", html_message = html_message, error = error, error_msg = error_msg ) else: msgID = db.messages.insert( msg_content = html_message, msg_subject = subject ) return msgID
def edit(): """ :return: shows order """ from openstudio.os_class import Class response.title = T('Order #') + request.vars['coID'] response.subtitle = T('Edit') response.view = 'general/only_content.html' coID = request.vars['coID'] order = Order(coID) cuID = order.order.auth_customer_id customer = Customer(cuID) # Info table info = TABLE(THEAD(TR(TH(T('Customer')), TH(T('Ordered on')), TH(T('Status')), TH(), )), _class='table') # Display status for field in db.customers_orders: field.readable = False field.writable = False db.customers_orders.Status.readable = True db.customers_orders.Status.writable = True crud.messages.record_updated = T('Saved') form = crud.update(db.customers_orders, coID) result = set_form_id_and_get_submit_button(form, 'MainForm') form = result['form'] submit = result['submit'] form = DIV(XML('<form id="MainForm" action="#" enctype="multipart/form-data" method="post">'), form.custom.widget.Status, form.custom.end) #status = form # status = represent_customers_orders_status(order.order.Status, order.order) # Display ordered on ordered_on = represent_datetime(order.order.DateCreated, order.order) customer_link = A(customer.get_name(), _href=URL('customers', 'edit', args=customer.row.id)) info.append(TR( TD(customer_link), TD(ordered_on), TD(form), TD() )) # Info content content = DIV(DIV(info, _class='col-md-8 no-padding-left')) # Display items rows = order.get_order_items_rows() header = THEAD(TR(TH(T('Product')), TH(T('Description')), TH(SPAN(T('Amount incl. VAT'), _class='right')), TH(T("G/L Account")), TH(T("Cost center")), TH(T("Class check-in")), TH(), ) ) table = TABLE(header, _class='table table-striped table-hover order-items') for i, row in enumerate(rows): repr_row = list(rows[i:i + 1].render())[0] class_name = "" if row.customers_orders_items.classes_id and row.customers_orders_items.ClassDate: cls = Class(row.customers_orders_items.classes_id, row.customers_orders_items.ClassDate) class_name = cls.get_name() table.append(TR( TD(repr_row.customers_orders_items.ProductName), TD(repr_row.customers_orders_items.Description), TD(SPAN(repr_row.customers_orders_items.TotalPriceVAT, _class='right')), TD(repr_row.customers_orders_items.accounting_glaccounts_id), TD(repr_row.customers_orders_items.accounting_costcenters_id), TD(class_name), TD(), )) # Display totals amounts = order.get_amounts() footer = TFOOT(TR(TD(), TD(B(T('Subtotal'))), TD(SPAN(CURRSYM, ' ', format(amounts.TotalPrice, '.2f'), _class='bold right')), TD() ), TR(TD(), TD(B(T('VAT'))), TD(SPAN(CURRSYM, ' ', format(amounts.VAT, '.2f'), _class='bold right')), TD() ), TR(TD(), TD(B(T('Total'))), TD(SPAN(CURRSYM, ' ', format(amounts.TotalPriceVAT, '.2f'), _class='bold right')), TD() )) table.append(footer) content.append(table) # Customer message customer_message = '' if order.order.CustomerNote: customer_message = DIV( B(T('Customer message')), BR(), BR(), XML(order.order.CustomerNote.replace("\n", "<br>")), ) content.append(customer_message) back = os_gui.get_button('back', edit_get_return_url(cuID)) return dict(content=content, back=back, save=submit)