~edwinvandeven/openstudio/2018.1

« back to all changes in this revision

Viewing changes to controllers/workshops.py

  • Committer: Edwin van de Ven
  • Date: 2018-03-07 11:59:31 UTC
  • Revision ID: edwinvandeven@home.nl-20180307115931-kiwy79us9029my79
Moved workshops.py to events.py
Renamed products to tickets

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# coding: utf8
2
 
 
3
 
from openstudio import Workshop, WorkshopsHelper, WorkshopProduct, Invoice, InvoicesHelper, OsMail, TasksHelper
4
 
 
5
 
from general_helpers import get_weekday
6
 
from general_helpers import get_badge
7
 
from general_helpers import get_label
8
 
from general_helpers import get_ajax_loader
9
 
from general_helpers import get_submenu
10
 
from general_helpers import get_input_search
11
 
from general_helpers import datestr_to_python
12
 
from general_helpers import class_get_teachers
13
 
from general_helpers import max_string_length
14
 
from general_helpers import Memo_links
15
 
from general_helpers import workshops_get_full_workshop_product_id
16
 
from general_helpers import classes_get_status
17
 
from general_helpers import set_form_id_and_get_submit_button
18
 
 
19
 
import html2text
20
 
import cStringIO
21
 
import openpyxl
22
 
 
23
 
 
24
 
### helpers begin
25
 
def activity_count_reservations(wsaID, fwsID):
26
 
    '''
27
 
        Returns count of reservations for an activity
28
 
    '''
29
 
    # count full workshop customers
30
 
    query = (db.workshops_products_customers.workshops_products_id == fwsID) & \
31
 
            (db.workshops_products_customers.Cancelled == False) & \
32
 
            (db.workshops_products_customers.Waitinglist == False)
33
 
    count_full_ws = db(query).count()
34
 
    # count activity customers
35
 
    query = activity_list_customers_get_list_activity_query(wsaID)
36
 
    count_activity = db(query).count()
37
 
 
38
 
    return count_full_ws + count_activity
39
 
 
40
 
 
41
 
def activity_get_filled(row, fwsID):
42
 
    '''
43
 
        Formats count of reservations for use in manage page activity list
44
 
    '''
45
 
    total = unicode(row.Spaces)
46
 
    wsaID = row.id
47
 
 
48
 
    count = activity_count_reservations(wsaID, fwsID)
49
 
    used = unicode(count)
50
 
 
51
 
    filled = used + "/" + total
52
 
    if used == total:
53
 
        filled = SPAN(filled, _class='green')
54
 
    return filled
55
 
 
56
 
 
57
 
def get_workshops_menu(page, wsID):
58
 
    vars = {'wsID': wsID}
59
 
 
60
 
    pages = [['workshop_edit',
61
 
              T('General'),
62
 
              URL('workshop_edit', vars=vars)],
63
 
             ['products',
64
 
              T('Products'),
65
 
              URL('products', vars=vars)],
66
 
             ['activities',
67
 
              T('Activities'),
68
 
              URL('activities', vars=vars)],
69
 
             ['tasks',
70
 
              T('Tasks'),
71
 
              URL('tasks', vars=vars)],
72
 
             ['stats',
73
 
              T('Quick stats'),
74
 
              URL('stats', vars=vars)],
75
 
             ['info_mail',
76
 
              T('Info mail'),
77
 
              URL('info_mail', vars=vars)]]
78
 
 
79
 
    return get_submenu(pages, page, horizontal=True, htype='tabs')
80
 
 
81
 
 
82
 
def get_subtitle(wsID):
83
 
    '''
84
 
        Returns a subtitle for a workshop
85
 
    '''
86
 
    workshop = db.workshops(wsID)
87
 
    st = SPAN(workshop.Name + ' ')
88
 
    if workshop.Enddate:
89
 
        if workshop.Startdate == workshop.Enddate:
90
 
            st.append(SPAN(workshop.Startdate.strftime(DATE_FORMAT),
91
 
                           _class='small_font'))
92
 
        else:
93
 
            st.append(SPAN(workshop.Startdate.strftime(DATE_FORMAT) + ' - ' + \
94
 
                           workshop.Enddate.strftime(DATE_FORMAT),
95
 
                           _class='small_font'))
96
 
    elif workshop.Startdate:
97
 
        st.append(SPAN(workshop.Startdate.strftime(DATE_FORMAT),
98
 
                       _class='small_font'))
99
 
 
100
 
    return st
101
 
 
102
 
 
103
 
### helpers end
104
 
 
105
 
def index_get_filter_form(filter_option):
106
 
    filter_options = [('current', T('Current')), ('archived', T('Archived'))]
107
 
 
108
 
    form = FORM(_action='#',
109
 
                _method='post',
110
 
                _class='right select-narrow',
111
 
                _id='workshops_show_filter')
112
 
    select = SELECT(value=filter_option,
113
 
                    _name='filter',
114
 
                    _class='generic-widget',
115
 
                    _onchange="this.form.submit();",
116
 
                    _style='vertical-align:text;')
117
 
    for option in filter_options:
118
 
        selected = False
119
 
        if filter_option == option[0]:
120
 
            selected = True
121
 
        select.append(OPTION(option[1], _value=option[0], _selected=selected))
122
 
 
123
 
    submit = INPUT(_type='submit', _value=T("Go"))
124
 
    form.insert(0, select)
125
 
    form.append(submit)
126
 
 
127
 
    return DIV(form, _class='form_inline')
128
 
 
129
 
 
130
 
@auth.requires(auth.has_membership(group_id='Admins') or \
131
 
               auth.has_permission('read', 'workshops'))
132
 
def index():
133
 
    response.title = T("Events")
134
 
    response.subtitle = T("")
135
 
    response.view = 'general/only_content_no_box.html'
136
 
 
137
 
    # Make sure we're not going back to workshop payments page from here
138
 
    session.workshops_manage_back = None
139
 
 
140
 
    if not session.workshops_show:
141
 
        session.workshops_show = 'current'
142
 
 
143
 
    if 'show_archive' in request.vars:
144
 
        show = request.vars['show_archive']
145
 
        session.workshops_show = show
146
 
 
147
 
    if session.workshops_show == 'current':
148
 
        query = (db.workshops.Archived == False)
149
 
    elif session.workshops_show == 'archive':
150
 
        query = (db.workshops.Archived == True)
151
 
        response.subtitle = T("Archived")
152
 
 
153
 
    if session.workshops_show == 'archive':
154
 
        archive_class = 'active'
155
 
        current_class = ''
156
 
    else:
157
 
        current_class = 'active'
158
 
        archive_class = ''
159
 
 
160
 
    if ('all' in request.vars and session.workshops_show == 'archive') or session.workshops_show == 'current':
161
 
        limit = False
162
 
        show_all = ''
163
 
    else:
164
 
        # Only show initial limit for archive
165
 
        limit = 15
166
 
        show_all = DIV(A(T("Show all"),
167
 
                         _href=URL('workshops', 'index', vars={'all': True}),
168
 
                         _title=T('Show the full archive, might take a little while to load')))
169
 
 
170
 
    delete_permission = auth.has_membership(group_id='Admins') or \
171
 
                        auth.has_permission('delete', 'workshops')
172
 
 
173
 
    add_url = URL('workshop_add')
174
 
    add = os_gui.get_button('add', add_url, T("Add a new event"))
175
 
 
176
 
    archive_buttons = os_gui.get_archived_radio_buttons(
177
 
        session.workshops_show)
178
 
 
179
 
    tools = DIV(archive_buttons, add)
180
 
 
181
 
    content = DIV(
182
 
        UL(LI(A(T('Current'),
183
 
                _href=URL(vars={'show_archive': 'current'})),
184
 
              _class=current_class),
185
 
           LI(A(T('Archive'),
186
 
                _href=URL(vars={'show_archive': 'archive'})),
187
 
              _class=archive_class),
188
 
           # LI(I(_class='fa fa-object-group'),
189
 
           #    _class='pull-left header'),
190
 
           _class='nav nav-tabs pull-right'),
191
 
        DIV(DIV(index_get_content(query, limit),
192
 
                show_all,
193
 
                _class='tab-pane active'),
194
 
            _class='tab-content'),
195
 
        _class='nav-tabs-custom')
196
 
 
197
 
    return dict(content=content, add=add)
198
 
 
199
 
 
200
 
def index_get_content(query, limit=False):
201
 
    '''
202
 
        Returns table with workshop content
203
 
    '''
204
 
    delete_onclick = "return confirm('" + \
205
 
                     T('Are you sure you want to delete this workshop?') + "');"
206
 
 
207
 
    header = THEAD(TR(TH(T('Image')),
208
 
                      TH(T('Name')),
209
 
                      TH(T('Start')),
210
 
                      TH(T('Teacher')),
211
 
                      TH(T('Location')),
212
 
                      TH(T('Shop')),
213
 
                      TH(),  # Actions
214
 
                      ))
215
 
 
216
 
    table = TABLE(header, _class='table table-striped table-hover')
217
 
 
218
 
    orderby = db.workshops.Startdate
219
 
    if session.workshops_show == 'archive':
220
 
        # Sort by latest first in archive
221
 
        orderby = ~db.workshops.Startdate
222
 
 
223
 
 
224
 
    if limit:
225
 
        rows = db(query).select(db.workshops.ALL, limitby=(0, limit), orderby=orderby)
226
 
    else:
227
 
        rows = db(query).select(db.workshops.ALL, orderby=orderby)
228
 
 
229
 
    for i, row in enumerate(rows):
230
 
        repr_row = list(rows[i:i + 1].render())[0]
231
 
 
232
 
        # Shop (API)
233
 
        public = INPUT(value=row.PublicWorkshop,
234
 
                       _type='checkbox',
235
 
                       _value='public',
236
 
                       _disabled='disabled')
237
 
 
238
 
        # Buttons
239
 
        edit = index_get_link_edit(row)
240
 
 
241
 
        pdf = os_gui.get_button('print',
242
 
                                URL('workshops', 'pdf', vars={'wsID': row.id}),
243
 
                                tooltip=T('PDF'))
244
 
 
245
 
        archive = ''
246
 
        archive_permission = auth.has_membership(group_id='Admins') or \
247
 
                             auth.has_permission('update', 'workshops')
248
 
        if archive_permission:
249
 
            archive = index_get_link_archive(row)
250
 
 
251
 
        delete = ''
252
 
        delete_permission = auth.has_membership(group_id='Admins') or \
253
 
                            auth.has_permission('delete', 'workshops')
254
 
        if delete_permission:
255
 
            delete = os_gui.get_button('delete_notext',
256
 
                                       URL('workshop_delete', vars={'wsID': row.id}),
257
 
                                       onclick=delete_onclick)
258
 
 
259
 
        buttons = DIV(edit, pdf, archive, delete, _class='pull-right')
260
 
 
261
 
        tr = TR(
262
 
            TD(repr_row.thumbsmall),
263
 
            TD(max_string_length(repr_row.Name, 34),
264
 
               _title=repr_row.Name),
265
 
            TD(repr_row.Startdate),
266
 
            TD(max_string_length(repr_row.auth_teacher_id, 20),
267
 
               _title=repr_row.auth_teacher_id),
268
 
            TD(max_string_length(repr_row.school_locations_id, 23),
269
 
               _title=repr_row.school_locations_id),
270
 
            TD(public),
271
 
            TD(buttons)
272
 
        )
273
 
 
274
 
        table.append(tr)
275
 
 
276
 
    return table
277
 
 
278
 
 
279
 
def index_get_link_edit(row):
280
 
    '''
281
 
        Returns drop down link for index edit
282
 
    '''
283
 
    vars = {'wsID': row.id}
284
 
 
285
 
    links = []
286
 
 
287
 
    permission = (auth.has_membership(group_id='Admins') or
288
 
                  auth.has_permission('update', 'workshops'))
289
 
    if permission:
290
 
        edit = A((os_gui.get_fa_icon('fa-pencil'), T('Edit')),
291
 
                 _href=URL('workshop_edit', vars=vars))
292
 
        links.append(edit)
293
 
 
294
 
    permission = (auth.has_membership(group_id='Admins') or
295
 
                  auth.has_permission('read', 'workshops_products'))
296
 
    if permission:
297
 
        link = A((os_gui.get_fa_icon('fa-briefcase'), T('Products')),
298
 
                 _href=URL('products', vars=vars))
299
 
        links.append(link)
300
 
 
301
 
    permission = (auth.has_membership(group_id='Admins') or
302
 
                  auth.has_permission('read', 'workshops_activities'))
303
 
    if permission:
304
 
        link = A((os_gui.get_fa_icon('fa-clock-o'), T('Activities')),
305
 
                 _href=URL('activities', vars=vars))
306
 
        links.append(link)
307
 
 
308
 
    permission = (auth.has_membership(group_id='Admins') or
309
 
                  auth.has_permission('read', 'tasks'))
310
 
    if permission:
311
 
        link = A((os_gui.get_fa_icon('fa-check-square-o'), T('Tasks')),
312
 
                 _href=URL('tasks', vars=vars))
313
 
        links.append(link)
314
 
 
315
 
    permission = (auth.has_membership(group_id='Admins') or
316
 
                  auth.has_permission('read', 'workshops'))
317
 
    if permission:
318
 
        link = A((os_gui.get_fa_icon('fa-pie-chart'), ' ', T("Quick Stats")),
319
 
                 _href=URL('stats', vars=vars))
320
 
        links.append(link)
321
 
 
322
 
    duplicate_permission = auth.has_membership(group_id='Admins') or \
323
 
                           auth.has_permission('create', 'workshops')
324
 
    if duplicate_permission:
325
 
        links.append(LI(_role='separator', _class='divider'))
326
 
        duplicate_onclick = "return confirm('" + \
327
 
                            T('Are you sure you want to duplicate this workshop?') + "');"
328
 
 
329
 
        link = A((os_gui.get_fa_icon('fa-clone'), ' ', T('Duplicate')),
330
 
                 _href=URL('workshop_duplicate',
331
 
                           vars={'wsID': row.id}),
332
 
                 _onclick=duplicate_onclick)
333
 
        links.append(link)
334
 
 
335
 
 
336
 
    menu = os_gui.get_dropdown_menu(
337
 
        links=links,
338
 
        btn_text='',
339
 
        btn_size='btn-sm',
340
 
        btn_icon='pencil',
341
 
        menu_class='btn-group pull-left')
342
 
 
343
 
    return menu
344
 
 
345
 
 
346
 
def index_get_link_archive(row):
347
 
    '''
348
 
        Called from the index function. Changes title of archive button
349
 
        depending on whether a workshop is archived or not
350
 
    '''
351
 
    workshop = db.workshops(row.id)
352
 
 
353
 
    if workshop.Archived:
354
 
        tt = T("Move to current")
355
 
    else:
356
 
        tt = T("Archive")
357
 
 
358
 
    return os_gui.get_button('archive',
359
 
                             URL('workshop_archive', vars={'wsID': row.id}),
360
 
                             tooltip=tt)
361
 
 
362
 
 
363
 
@auth.requires(auth.has_membership(group_id='Admins') or \
364
 
               auth.has_permission('update', 'workshops'))
365
 
def workshop_archive():
366
 
    '''
367
 
        This function archives a workshop
368
 
        request.vars[wsID] is expected to be the workshop ID
369
 
    '''
370
 
    wsID = request.vars['wsID']
371
 
    if not wsID:
372
 
        session.flash = T('Unable to (un)archive workshop')
373
 
    else:
374
 
        workshop = db.workshops(wsID)
375
 
 
376
 
        if workshop.Archived:
377
 
            session.flash = T('Moved workshop to current')
378
 
        else:
379
 
            session.flash = T('Archived workshop')
380
 
 
381
 
        workshop.Archived = not workshop.Archived
382
 
        workshop.update_record()
383
 
 
384
 
        # Clear cache
385
 
        cache_clear_workshops()
386
 
 
387
 
    redirect(URL('index'))
388
 
 
389
 
 
390
 
def workshop_add_get_menu(page):
391
 
    '''
392
 
        Returns submenu for adding a workshop
393
 
    '''
394
 
    pages = [['workshop_add', T('1. Workshop'), "#"],
395
 
             ['workshop_add_set_price', T('2. Price'), "#"]]
396
 
 
397
 
    return os_gui.get_submenu(pages, page, horizontal=True, htype='tabs')
398
 
 
399
 
 
400
 
@auth.requires_login()
401
 
def workshop_add():
402
 
    """
403
 
        This function shows an add page for a workshop
404
 
    """
405
 
    response.title = T('Add event')
406
 
    response.subtitle = T("Event info")
407
 
    response.view = 'general/tabs_menu.html'
408
 
 
409
 
    return_url = URL('index')
410
 
    db.workshops.Archived.readable = False
411
 
    db.workshops.Archived.writable = False
412
 
 
413
 
    workshop_hide_teacher_fields()
414
 
 
415
 
    next_url = '/workshops/workshop_add_set_price?wsID=[id]'
416
 
    crud.messages.submit_button = T("Next")
417
 
    crud.messages.record_created = T("")
418
 
    crud.settings.create_onaccept = [workshop_add_onaccept, cache_clear_workshops]
419
 
    crud.settings.create_next = next_url
420
 
    crud.settings.formstyle = 'bootstrap3_stacked'
421
 
    form = crud.create(db.workshops)
422
 
 
423
 
    textareas = form.elements('textarea')
424
 
    for textarea in textareas:
425
 
        textarea['_class'] += ' tmced'
426
 
 
427
 
    result = set_form_id_and_get_submit_button(form, 'MainForm')
428
 
    form = result['form']
429
 
    submit = result['submit']
430
 
 
431
 
    menu = workshop_add_get_menu(request.function)
432
 
    back = os_gui.get_button('back', return_url)
433
 
 
434
 
    return dict(content=form,
435
 
                save=submit,
436
 
                back=back,
437
 
                menu=menu)
438
 
 
439
 
 
440
 
def workshop_add_onaccept(form):
441
 
    '''
442
 
        Insert full workshop product after a workshop is created
443
 
    '''
444
 
    wsID = form.vars.id
445
 
    ws_row = db.workshops(wsID)
446
 
    db.workshops_products.insert(workshops_id=wsID,
447
 
                                 FullWorkshop=True,
448
 
                                 Deletable=False,
449
 
                                 Name=T('Full event'),
450
 
                                 Price=0,
451
 
                                 Description=T('Full event'))
452
 
 
453
 
 
454
 
@auth.requires_login()
455
 
def workshop_add_set_price():
456
 
    '''
457
 
        Set the price of the full workshop product for a workshop
458
 
        Uses the permissions for workshops_products
459
 
    '''
460
 
    wsID = request.vars['wsID']
461
 
    fwspID = workshops_get_full_workshop_product_id(wsID)
462
 
 
463
 
    response.title = T('Add event')
464
 
    response.subtitle = T("Full event price")
465
 
    response.view = 'general/tabs_menu.html'
466
 
 
467
 
    db.workshops_products.Name.readable = False
468
 
    db.workshops_products.Name.writable = False
469
 
    db.workshops_products.Description.readable = False
470
 
    db.workshops_products.Description.writable = False
471
 
    db.workshops_products.ExternalShopURL.readable = False
472
 
    db.workshops_products.ExternalShopURL.writable = False
473
 
    db.workshops_products.AddToCartText.readable = False
474
 
    db.workshops_products.AddToCartText.writable = False
475
 
    db.workshops_products.PublicProduct.readable = False
476
 
    db.workshops_products.PublicProduct.writable = False
477
 
 
478
 
    crud.messages.submit_button = T("Save")
479
 
    crud.messages.record_updated = T("Saved")
480
 
    crud.settings.update_deletable = False
481
 
    crud.settings.update_next = URL('products', vars={'wsID': wsID})
482
 
    form = crud.update(db.workshops_products, fwspID)
483
 
 
484
 
    result = set_form_id_and_get_submit_button(form, 'MainForm')
485
 
    form = result['form']
486
 
    submit = result['submit']
487
 
 
488
 
    menu = workshop_add_get_menu(request.function)
489
 
 
490
 
    return dict(content=form,
491
 
                save=submit,
492
 
                menu=menu)
493
 
 
494
 
 
495
 
@auth.requires(auth.has_membership(group_id='Admins') or
496
 
               auth.has_permission('delete', 'workshops'))
497
 
def workshop_delete():
498
 
    '''
499
 
        Delete a workshop
500
 
    '''
501
 
    wsID = request.vars['wsID']
502
 
 
503
 
    query = (db.workshops.id == wsID)
504
 
    db(query).delete()
505
 
 
506
 
    session.flash = T('Workshop deleted')
507
 
 
508
 
    # clear cache
509
 
    cache_clear_workshops()
510
 
 
511
 
    redirect(URL('index'))
512
 
 
513
 
 
514
 
@auth.requires_login()
515
 
def workshop_edit():
516
 
    """
517
 
        This function shows an edit page for a workshop
518
 
        request.args[0] is expected to be the workshop ID (wsID)
519
 
    """
520
 
    wsID = request.vars['wsID']
521
 
    return_url = URL('index')
522
 
 
523
 
    response.title = T("Edit event")
524
 
    response.subtitle = get_subtitle(wsID)
525
 
    response.view = 'general/tabs_menu.html'
526
 
 
527
 
    workshop_hide_teacher_fields()
528
 
 
529
 
    crud.messages.submit_button = T("Save")
530
 
    crud.messages.record_updated = T("Saved")
531
 
    crud.settings.update_next = URL(vars={'wsID': wsID})
532
 
    crud.settings.update_onaccept = [cache_clear_workshops]
533
 
    crud.settings.update_deletable = False
534
 
    crud.settings.formstyle = 'bootstrap3_stacked'
535
 
    form = crud.update(db.workshops, wsID)
536
 
 
537
 
    form_id = "MainForm"
538
 
    form_element = form.element('form')
539
 
    form['_id'] = form_id
540
 
 
541
 
    elements = form.elements('input, select, textarea')
542
 
    for element in elements:
543
 
        element['_form'] = form_id
544
 
 
545
 
    submit = form.element('input[type=submit]')
546
 
 
547
 
    textareas = form.elements('textarea')
548
 
    for textarea in textareas:
549
 
        textarea['_class'] += ' tmced'
550
 
 
551
 
    menu = get_workshops_menu(request.function, wsID)
552
 
    back = os_gui.get_button('back', return_url)
553
 
    content = DIV(workshop_get_alert_no_activities(wsID),
554
 
                  form)
555
 
 
556
 
    return dict(content=content,
557
 
                back=back,
558
 
                save=submit,
559
 
                menu=menu)
560
 
 
561
 
 
562
 
@auth.requires_login()
563
 
def workshop_edit_teachers():
564
 
    '''
565
 
        Edit teachers for a workshop
566
 
        request.vars['wsID'] is expected to be the workshops_id
567
 
        request.vars['wiz'] is expected to be 'True' in case the wizzard is
568
 
        used
569
 
    '''
570
 
    wsID = request.vars['wsID']
571
 
    wizzard = True if 'wiz' in request.vars else False
572
 
    return_url = URL('index')
573
 
    response.title = T("Edit workshop")
574
 
    response.subtitle = get_subtitle(wsID)
575
 
 
576
 
    response.view = 'general/tabs_menu.html'
577
 
 
578
 
    if wizzard:
579
 
        # call js for styling the form
580
 
        crud.messages.submit_button = T("Next")
581
 
        crud.messages.record_updated = T("")
582
 
        crud.settings.update_next = URL('workshops',
583
 
                                        'workshop_add_set_price',
584
 
                                        vars={'wsID': wsID})
585
 
        back = ''
586
 
        menu = workshop_add_get_menu(request.function)
587
 
        response.title = T('Add workshop')
588
 
        response.subtitle = T("Teachers")
589
 
 
590
 
    else:
591
 
        crud.messages.submit_button = T("Save")
592
 
        crud.messages.record_updated = T("Saved teachers")
593
 
        # crud.settings.update_next = return_url
594
 
        back = os_gui.get_button('back', return_url)
595
 
        menu = workshop_edit_get_submenu(request.function, wsID)
596
 
 
597
 
    db.workshops.Name.readable = False
598
 
    db.workshops.Name.writable = False
599
 
    db.workshops.Startdate.readable = False
600
 
    db.workshops.Startdate.writable = False
601
 
    db.workshops.Enddate.readable = False
602
 
    db.workshops.Enddate.writable = False
603
 
    db.workshops.picture.readable = False
604
 
    db.workshops.picture.writable = False
605
 
    db.workshops.PublicWorkshop.readable = False
606
 
    db.workshops.PublicWorkshop.writable = False
607
 
    db.workshops.Description.readable = False
608
 
    db.workshops.Description.writable = False
609
 
    db.workshops.school_locations_id.readable = False
610
 
    db.workshops.school_locations_id.writable = False
611
 
 
612
 
    crud.settings.update_onaccept = [cache_clear_workshops]
613
 
    crud.settings.update_deletable = False
614
 
    form = crud.update(db.workshops, wsID)
615
 
 
616
 
    result = set_form_id_and_get_submit_button(form, 'MainForm')
617
 
    form = result['form']
618
 
    submit = result['submit']
619
 
 
620
 
    return dict(content=form,
621
 
                back=back,
622
 
                save=submit,
623
 
                menu=menu)
624
 
 
625
 
 
626
 
def workshop_hide_teacher_fields(var=None):
627
 
    '''
628
 
        Sets readable and writable to False for teacher fields in
629
 
        db.workshops
630
 
    '''
631
 
    db.workshops.Teacher.readable = False
632
 
    db.workshops.Teacher.writable = False
633
 
    db.workshops.TeacherEmail.readable = False
634
 
    db.workshops.TeacherEmail.writable = False
635
 
    db.workshops.Teacher2.readable = False
636
 
    db.workshops.Teacher2.writable = False
637
 
    db.workshops.Teacher2Email.readable = False
638
 
    db.workshops.Teacher2Email.writable = False
639
 
    db.workshops.Startdate.readable = False
640
 
    db.workshops.Startdate.writable = False
641
 
 
642
 
 
643
 
@auth.requires(auth.has_membership(group_id='Admins') or \
644
 
               auth.has_permission('create', 'workshops'))
645
 
def workshop_duplicate():
646
 
    '''
647
 
        Duplicate a workshop including products & activities
648
 
    '''
649
 
    wsID = request.vars['wsID']
650
 
    # workshop
651
 
    workshop = db.workshops(wsID)
652
 
    new_wsID = db.workshops.insert(
653
 
        Archived=workshop.Archived,
654
 
        PublicWorkshop=False,
655
 
        Name=workshop.Name + ' ' + T('(Copy)'),
656
 
        Startdate=workshop.Startdate,
657
 
        Enddate=workshop.Enddate,
658
 
        Starttime=workshop.Starttime,
659
 
        Endtime=workshop.Endtime,
660
 
        auth_teacher_id=workshop.auth_teacher_id,
661
 
        auth_teacher_id2=workshop.auth_teacher_id2,
662
 
        Description=workshop.Description,
663
 
        school_locations_id=workshop.school_locations_id,
664
 
        picture=workshop.picture,
665
 
        thumbsmall=workshop.thumbsmall,
666
 
        thumblarge=workshop.thumblarge,
667
 
    )
668
 
 
669
 
    # activities
670
 
    activity_ids_old = []
671
 
    activity_ids_map = []
672
 
    query = (db.workshops_activities.workshops_id == wsID)
673
 
    rows = db(query).select(db.workshops_activities.ALL)
674
 
    for row in rows:
675
 
        new_wsaID = db.workshops_activities.insert(
676
 
            workshops_id=new_wsID,
677
 
            Activity=row.Activity,
678
 
            Activitydate=row.Activitydate,
679
 
            school_locations_id=row.school_locations_id,
680
 
            Starttime=row.Starttime,
681
 
            Endtime=row.Endtime,
682
 
            Spaces=row.Spaces,
683
 
            auth_teacher_id=row.auth_teacher_id,
684
 
            auth_teacher_id2=row.auth_teacher_id2,
685
 
        )
686
 
        activity_ids_old.append(row.id)
687
 
        activity_ids_map.append({'old': row.id,
688
 
                                 'new': new_wsaID})
689
 
 
690
 
    # products
691
 
    products_ids_old = []
692
 
    products_ids_map = []
693
 
    query = (db.workshops_products.workshops_id == wsID)
694
 
    rows = db(query).select(db.workshops_products.ALL)
695
 
    for row in rows:
696
 
        new_wspID = db.workshops_products.insert(
697
 
            workshops_id=new_wsID,
698
 
            FullWorkshop=row.FullWorkshop,
699
 
            Deletable=row.Deletable,
700
 
            PublicProduct=row.PublicProduct,
701
 
            Name=row.Name,
702
 
            Price=row.Price,
703
 
            tax_rates_id=row.tax_rates_id,
704
 
            Description=row.Description,
705
 
            ExternalShopURL=row.ExternalShopURL,
706
 
            AddToCartText=row.AddToCartText,
707
 
            Donation=row.Donation
708
 
        )
709
 
        products_ids_old.append(row.id)
710
 
        products_ids_map.append({'old': row.id,
711
 
                                 'new': new_wspID})
712
 
 
713
 
    # products activities
714
 
    query = db.workshops_products_activities.workshops_products_id.belongs(products_ids_old)
715
 
    rows = db(query).select(db.workshops_products_activities.ALL)
716
 
    for row in rows:
717
 
        for pim in products_ids_map:
718
 
            if pim['old'] == row.workshops_products_id:
719
 
                new_wspID = pim['new']
720
 
 
721
 
        for aim in activity_ids_map:
722
 
            if aim['old'] == row.workshops_activities_id:
723
 
                new_wsaID = aim['new']
724
 
 
725
 
        db.workshops_products_activities.insert(
726
 
            workshops_products_id=new_wspID,
727
 
            workshops_activities_id=new_wsaID
728
 
        )
729
 
 
730
 
    # Clear cache
731
 
    cache_clear_workshops()
732
 
 
733
 
    session.flash = T('You are now editing the duplicated workshop')
734
 
 
735
 
    redirect(URL('workshop_edit', vars={'wsID': new_wsID}))
736
 
 
737
 
 
738
 
def activity_add_edit_update_workshop_datetime_info(form):
739
 
    '''
740
 
        :param form: db.workshops_activities add or edit form
741
 
        Set dates & times for a workshop based on activities
742
 
    '''
743
 
    activity = db.workshops_activities(form.vars.id)
744
 
    workshop = Workshop(activity.workshops_id)
745
 
 
746
 
    workshop.update_dates_times()
747
 
 
748
 
 
749
 
@auth.requires_login()
750
 
def activity_add():
751
 
    """
752
 
        This function shows an add page for a workshop activity
753
 
    """
754
 
    wsID = request.args[0]
755
 
    response.title = T('Event')
756
 
    response.subtitle = get_subtitle(wsID)
757
 
    response.view = 'general/tabs_menu.html'
758
 
 
759
 
    workshop = db.workshops(wsID)
760
 
 
761
 
    db.workshops_activities.workshops_id.default = wsID
762
 
    db.workshops_activities.Teacher.default = workshop.Teacher
763
 
    db.workshops_activities.Teacher2.default = workshop.Teacher2
764
 
    db.workshops_activities.auth_teacher_id.default = workshop.auth_teacher_id
765
 
    db.workshops_activities.auth_teacher_id2.default = workshop.auth_teacher_id2
766
 
 
767
 
    return_url = URL('activities', vars={'wsID': wsID})
768
 
 
769
 
    crud.messages.submit_button = T("Save")
770
 
    crud.messages.record_created = T("Added activity")
771
 
    crud.settings.create_onaccept = [activity_add_edit_update_workshop_datetime_info, cache_clear_workshops]
772
 
    crud.settings.create_next = return_url
773
 
    crud.settings.formstyle = 'table3cols'
774
 
    form = crud.create(db.workshops_activities)
775
 
 
776
 
    form_id = "MainForm"
777
 
    form_element = form.element('form')
778
 
    form['_id'] = form_id
779
 
 
780
 
    elements = form.elements('input, select, textarea')
781
 
    for element in elements:
782
 
        element['_form'] = form_id
783
 
 
784
 
    submit = form.element('input[type=submit]')
785
 
 
786
 
    content = DIV(H4(T('New activity')), BR(), form)
787
 
 
788
 
    back = os_gui.get_button('back', return_url)
789
 
    menu = get_workshops_menu('activities', wsID)
790
 
 
791
 
    return dict(content=content, back=back, menu=menu, save=submit)
792
 
 
793
 
 
794
 
@auth.requires_login()
795
 
def activity_edit():
796
 
    """
797
 
        This function shows an edit page for an activity
798
 
        request.args[0] is expected to be the workshops_activities_id (wsaID)
799
 
    """
800
 
    wsaID = request.args[0]
801
 
    response.title = T('Event')
802
 
    wsID = db.workshops_activities(wsaID).workshops_id
803
 
    response.subtitle = get_subtitle(wsID)
804
 
    response.view = 'general/tabs_menu.html'
805
 
 
806
 
    return_url = URL('activities', vars={'wsID': wsID})
807
 
 
808
 
    crud.messages.submit_button = T("Save")
809
 
    crud.messages.record_updated = T("Saved")
810
 
    crud.messages.record_deleted = T("Deleted activity")
811
 
    crud.settings.update_onaccept = [activity_add_edit_update_workshop_datetime_info, cache_clear_workshops]
812
 
    crud.settings.update_next = return_url
813
 
    crud.settings.update_deletable = False
814
 
 
815
 
    form = crud.update(db.workshops_activities, wsaID)
816
 
 
817
 
    form_id = "MainForm"
818
 
    form_element = form.element('form')
819
 
    form['_id'] = form_id
820
 
 
821
 
    elements = form.elements('input, select, textarea')
822
 
    for element in elements:
823
 
        element['_form'] = form_id
824
 
 
825
 
    submit = form.element('input[type=submit]')
826
 
 
827
 
    back = os_gui.get_button('back', return_url)
828
 
    content = DIV(H4(T('Edit activity')), BR(), form)
829
 
 
830
 
    menu = get_workshops_menu('activities', wsID)
831
 
 
832
 
    return dict(content=content, back=back, menu=menu, save=submit)
833
 
 
834
 
 
835
 
@auth.requires(auth.has_membership(group_id='Admins') or \
836
 
               auth.has_permission('delete', 'workshops_activities'))
837
 
def activity_delete():
838
 
    '''
839
 
        Delete the activity specified by request.args[1] (wsaID)
840
 
    '''
841
 
    wsID = request.args[0]
842
 
    wsaID = request.args[1]
843
 
 
844
 
    # Delete activity
845
 
    query = (db.workshops_activities.id == wsaID)
846
 
    db(query).delete()
847
 
 
848
 
    # Update dates & times for workshop
849
 
    workshop = Workshop(wsID)
850
 
    workshop.update_dates_times()
851
 
 
852
 
    # Clear cache
853
 
    cache_clear_workshops()
854
 
 
855
 
    redirect(URL('activities', vars={'wsID': wsID}))
856
 
 
857
 
 
858
 
@auth.requires(auth.has_membership(group_id='Admins') or \
859
 
               auth.has_permission('read', 'workshops'))
860
 
def mail_activity_attendance():
861
 
    '''
862
 
        This function sends the attendance list for all activities
863
 
        to the workshop teacher
864
 
    '''
865
 
    wsID = request.vars['wsID']
866
 
 
867
 
    workshop = db.workshops(wsID)
868
 
 
869
 
    # start the message.
870
 
    message = "<html>\n<head><STYLE TYPE='text/css'> \
871
 
            <!-- TD{font-size: 9pt; align:left;} \
872
 
            TD, TH { padding:0 20px 0 0; } \
873
 
            TH{font-size: 9pt; text-align:left;} --->  </STYLE></head> \
874
 
            <body style=font-size:10pt; font-style:normal;'>"
875
 
    message += T("Dear teacher(s)")
876
 
    message += ",<br><br>"
877
 
    message += 'Below are the attendance lists for the activities in your '
878
 
    message += 'workshop, ' + workshop.Name + '.<br><br>'
879
 
 
880
 
    fws_rows = activity_list_customers_get_fullws_rows(wsID)
881
 
 
882
 
    query = (db.workshops_activities.workshops_id == wsID)
883
 
    rows = db(query).select(db.workshops_activities.ALL,
884
 
                            orderby=db.workshops_activities.Activitydate | \
885
 
                                    db.workshops_activities.Starttime)
886
 
    for row in rows:
887
 
        title = row.Activity
888
 
        subtitle = T("On ") + row.Activitydate.strftime(DATE_FORMAT) + \
889
 
                   T(' at ') + \
890
 
                   row.Starttime.strftime(TIME_FORMAT)
891
 
        message += unicode(H3(title))
892
 
        message += unicode(subtitle)
893
 
        message += '<br><br>'
894
 
        table = TABLE()
895
 
        for fws_row in fws_rows.render():
896
 
            name = fws_row.auth_user.display_name
897
 
            table.append(TR(TD(name),
898
 
                            TD(unicode(T('Full event')))))
899
 
        wsa_rows = activity_list_customers_get_activity_rows(row.id)
900
 
        for wsa_row in wsa_rows:
901
 
            name = wsa_row.auth_user.display_name,
902
 
            table.append(TR(TD(name), TD()))
903
 
 
904
 
        message += unicode(table)
905
 
        message += '<br>'
906
 
 
907
 
        subject = T("Reservations for") + ' ' + workshop.Name + ', ' + \
908
 
                  T("starting on") + ' ' + \
909
 
                  workshop.Startdate.strftime(DATE_FORMAT) + '.'
910
 
 
911
 
    if db.sys_properties(Property='smtp_signature'):
912
 
        smtp_signature = db.sys_properties(Property='smtp_signature').PropertyValue
913
 
    else:
914
 
        smtp_signature = ""
915
 
    message += smtp_signature
916
 
    message += "<br><br><br><font size=2>" + T("This message is autogenerated by OpenStudio")
917
 
    message += " - <a href='http://www.openstudioproject.com'>www.openstudioproject.com</a></font>"
918
 
    message += "</body></html>"
919
 
 
920
 
    # now send the mail
921
 
    to = []
922
 
    if workshop.TeacherEmail:
923
 
        to.append(workshop.TeacherEmail.strip())
924
 
    if workshop.Teacher2Email:
925
 
        to.append(workshop.Teacher2Email.strip())
926
 
    if workshop.auth_teacher_id:
927
 
        teacher = db.auth_user(workshop.auth_teacher_id)
928
 
        to.append(teacher.email.strip())
929
 
    if workshop.auth_teacher_id2:
930
 
        teacher = db.auth_user(workshop.auth_teacher_id2)
931
 
        to.append(teacher.email.strip())
932
 
 
933
 
    if len(to) < 1:
934
 
        session.flash = T("Please check the teachers' email address(es).")
935
 
    else:
936
 
        check = MAIL.send(
937
 
            to=to,
938
 
            subject=subject,
939
 
            # If reply_to is omitted, then mail.settings.sender is used
940
 
            reply_to=None,
941
 
            message=message)
942
 
 
943
 
        if check:
944
 
            session.flash = T("Successfully sent mail")
945
 
        else:
946
 
            session.flash = T("Unable to send mail")
947
 
 
948
 
    redirect(URL('activities', vars={'wsID': wsID}))
949
 
 
950
 
 
951
 
@auth.requires(auth.has_membership(group_id='Admins') or \
952
 
               auth.has_permission('update', 'workshops_activities'))
953
 
def attendance_delete():
954
 
    '''
955
 
        This function removed the specified attendance record
956
 
        request.vars['id'] is expected to be the
957
 
            workshops_reservation id (wsr_id)
958
 
    '''
959
 
    response.view = 'generic.json'
960
 
    wsr_id = request.vars['id']
961
 
 
962
 
    query = (db.workshops_reservation.id == wsr_id)
963
 
    result = db(query).delete()
964
 
 
965
 
    # result > 0 means something was removed
966
 
    if result:
967
 
        message = T("Successfully removed")
968
 
        status = "success"
969
 
    else:
970
 
        message = T("Uh oh... something went wrong...")
971
 
        status = "fail"
972
 
 
973
 
    return dict(message=message, status=status)
974
 
 
975
 
 
976
 
@auth.requires(auth.has_membership(group_id='Admins') or \
977
 
               auth.has_permission('update', 'workshops_activities'))
978
 
def activity_update_attendance():
979
 
    '''
980
 
        Called as JSON
981
 
        This function is meant to be called with the json extension.
982
 
        It takes id and Info as variables.
983
 
    '''
984
 
    if not request.extension == 'json':
985
 
        return T("Error, please call as JSON")
986
 
 
987
 
    response.view = 'generic.json'
988
 
    status = "fail"
989
 
 
990
 
    if request.vars:
991
 
        cuID = request.vars['cuID']
992
 
        wsaID = request.vars['wsaID']
993
 
 
994
 
        # info
995
 
        if 'attending' in request.vars:
996
 
            result = db.workshops_activities_customers.insert(
997
 
                auth_customer_id=cuID,
998
 
                workshops_activities_id=wsaID)
999
 
        else:
1000
 
            query = \
1001
 
                (db.workshops_activities_customers.auth_customer_id == cuID) & \
1002
 
                (db.workshops_activities_customers.workshops_activities_id == wsaID)
1003
 
            result = db(query).delete()
1004
 
 
1005
 
        if result:
1006
 
            status = "success"
1007
 
            message = T("Updated attendance")
1008
 
        else:
1009
 
            message = T("Uh oh... something went wrong...")
1010
 
    else:
1011
 
        message = T("Error: no data received")
1012
 
 
1013
 
    return dict(status=status, message=message)
1014
 
 
1015
 
 
1016
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1017
 
               auth.has_permission('update', 'workshops_activities'))
1018
 
def activity_duplicate():
1019
 
    '''
1020
 
        This function duplicates an activity and
1021
 
        redirects to the duplicated activity's edit page
1022
 
    '''
1023
 
    wsaID = request.args[0]
1024
 
    row = db.workshops_activities[wsaID]
1025
 
 
1026
 
    id = db.workshops_activities.insert(
1027
 
        workshops_id=row.workshops_id,
1028
 
        Activity=row.Activity + u' (Copy)',
1029
 
        Activitydate=row.Activitydate,
1030
 
        school_locations_id=row.school_locations_id,
1031
 
        auth_teacher_id=row.auth_teacher_id,
1032
 
        auth_teacher_id2=row.auth_teacher_id2,
1033
 
        Starttime=row.Starttime,
1034
 
        Endtime=row.Endtime,
1035
 
        Spaces=row.Spaces)
1036
 
 
1037
 
    # Clear cache
1038
 
    cache_clear_workshops()
1039
 
 
1040
 
    session.flash = T("You are now editing the duplicated activity")
1041
 
 
1042
 
    redirect(URL('activity_edit', args=[id]))
1043
 
 
1044
 
 
1045
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1046
 
               auth.has_permission('read', 'workshops'))
1047
 
def stats():
1048
 
    '''
1049
 
        This function shows a page with a quick view of the revenue and
1050
 
        a table listing the top 10 cities of attending customers.
1051
 
        request.vars['wsID'] is expected to be the workshopID
1052
 
    '''
1053
 
    wsID = request.vars['wsID']
1054
 
    workshop = db.workshops(wsID)
1055
 
    response.title = T('Event')
1056
 
    response.subtitle = get_subtitle(wsID)
1057
 
    response.view = 'general/tabs_menu.html'
1058
 
 
1059
 
    ### top 10 cities begin
1060
 
    cities = DIV(stats_get_top10cities(wsID),
1061
 
                 _class='col-md-6')
1062
 
 
1063
 
    ### top 10 cities end
1064
 
 
1065
 
    ### Revenue begin ###
1066
 
    revenue = DIV(stats_get_revenue(wsID), _class='col-md-6')
1067
 
 
1068
 
    ### Revenue end ###
1069
 
 
1070
 
    content = DIV(DIV(revenue, cities,
1071
 
                      _class='col-md-12'),
1072
 
                  _class='row')
1073
 
 
1074
 
    menu = get_workshops_menu(request.function, wsID)
1075
 
    back = manage_get_back()
1076
 
 
1077
 
    return dict(content=content,
1078
 
                menu=menu,
1079
 
                back=back,
1080
 
                left_sidebar_enabled=True)
1081
 
 
1082
 
 
1083
 
def stats_get_top10cities(wsID):
1084
 
    '''
1085
 
        Returns overview of top10 cities for a workshop
1086
 
    '''
1087
 
    count = db.auth_user.city.count()
1088
 
    query = (db.workshops_products.workshops_id == wsID) & \
1089
 
            (db.workshops_products_customers.workshops_products_id ==
1090
 
             db.workshops_products.id) & \
1091
 
            (db.workshops_products_customers.auth_customer_id ==
1092
 
             db.auth_user.id) & \
1093
 
            ((db.auth_user.city != None) & (db.auth_user.city != ''))
1094
 
 
1095
 
    rows = db(query).select(db.auth_user.city, count,
1096
 
                            groupby=db.auth_user.city,
1097
 
                            orderby=~count | db.auth_user.city, limitby=(0, 10))
1098
 
 
1099
 
    l = []
1100
 
    for row in rows:
1101
 
        l.append([row.auth_user.city, row[count]])
1102
 
 
1103
 
    title = T("Top 10 cities")
1104
 
    table = TABLE(TR(TH(T("City")), TH(T("Customers"))), _class='table')
1105
 
    for item in l:
1106
 
        table.append(TR(*item))
1107
 
 
1108
 
    panel = os_gui.get_panel_table(title, table)
1109
 
 
1110
 
    return panel
1111
 
 
1112
 
 
1113
 
def stats_get_revenue(wsID):
1114
 
    '''
1115
 
        Returns revenue of a workshop, specified by product
1116
 
    '''
1117
 
    total_revenue = 0
1118
 
    table = TABLE(TR(TH(T("Product")),
1119
 
                     TH(T("Sold")),
1120
 
                     TH(SPAN(T('Price'), _class='pull-right')),
1121
 
                     TH(SPAN(T('Total'), _class='pull-right'))),
1122
 
                  _class='table')
1123
 
 
1124
 
    # first get all products
1125
 
    query = (db.workshops_products.workshops_id == wsID)
1126
 
    rows = db(query).select(db.workshops_products.ALL,
1127
 
                            orderby=~db.workshops_products.FullWorkshop | \
1128
 
                                    db.workshops_products.Name)
1129
 
    for row in rows:
1130
 
        # get nr of sold products
1131
 
        query = (db.workshops_products_customers.workshops_products_id == row.id)
1132
 
        count = db(query).count()
1133
 
 
1134
 
        if count:
1135
 
            total = count * (row.Price or 0)
1136
 
            total_revenue += total
1137
 
        else:
1138
 
            total = 0
1139
 
 
1140
 
        table.append(TR(TD(row.Name),
1141
 
                        TD(count),
1142
 
                        TD(SPAN(CURRSYM, ' ',
1143
 
                                row.Price or '', _class='pull-right')),
1144
 
                        TD(SPAN(CURRSYM, ' ',
1145
 
                                total, _class='pull-right'))))
1146
 
 
1147
 
    table.append(TR(TD(T("Total")),
1148
 
                    TD(),
1149
 
                    TD(),
1150
 
                    TD(SPAN(CURRSYM, ' ',
1151
 
                            format(total_revenue, '.2f'), _class='pull-right')),
1152
 
                    _class='total'))
1153
 
    title = T("Revenue")
1154
 
 
1155
 
    panel = os_gui.get_panel_table(title, table)
1156
 
 
1157
 
    return panel
1158
 
 
1159
 
 
1160
 
def manage_get_back(var=None):
1161
 
    '''
1162
 
        Generate back button for workshops manage pages
1163
 
    '''
1164
 
    if session.workshops_manage_back == 'default_workshoppayments_open':
1165
 
        url = URL('default', 'workshop_payments_open')
1166
 
    elif session.workshops_manage_back == 'pinboard':
1167
 
        url = URL('pinboard', 'index')
1168
 
    elif session.workshops_manage_back == 'tasks_index':
1169
 
        url = URL('tasks', 'index')
1170
 
    else:
1171
 
        url = URL('index')
1172
 
 
1173
 
    return os_gui.get_button('back', url)
1174
 
 
1175
 
 
1176
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1177
 
               auth.has_permission('read', 'workshops_products'))
1178
 
def products():
1179
 
    '''
1180
 
        Lists products for a workshop
1181
 
        request.vars['wsID'] is expected to be the workshops_id
1182
 
    '''
1183
 
    wsID = request.vars['wsID']
1184
 
    response.title = T('Event')
1185
 
    response.subtitle = get_subtitle(wsID)
1186
 
    response.view = 'workshops/manage.html'
1187
 
 
1188
 
    ## Products begin ##
1189
 
    products = DIV()
1190
 
    products.append(workshop_get_alert_no_activities(wsID))
1191
 
 
1192
 
    add = ''
1193
 
    perm = auth.has_membership(group_id='Admins') or \
1194
 
           auth.has_permission('create', 'workshops_products')
1195
 
    if perm:
1196
 
        add_url = URL('product_add', args=[wsID])
1197
 
        add = os_gui.get_button('add', add_url, T("Add new product"), _class='pull-right')
1198
 
 
1199
 
    table = TABLE(THEAD(TR(TH(T('Name')),
1200
 
                           TH(T('Description')),
1201
 
                           TH(T('Price')),
1202
 
                           TH(T('Shop')),
1203
 
                           TH(),
1204
 
                           TH(),
1205
 
                           _class='header')),
1206
 
                  _class='table table-hover')
1207
 
 
1208
 
    query = (db.workshops_products.workshops_id == wsID)
1209
 
    rows = db(query).select(db.workshops_products.ALL)
1210
 
    for row in rows.render():
1211
 
        edit = ''
1212
 
        duplicate = ''
1213
 
        delete = ''
1214
 
        activities = ''
1215
 
 
1216
 
        # shop checkbox
1217
 
        shop = INPUT(_type="checkbox",
1218
 
                     _disabled="disabled")
1219
 
        if row.PublicProduct:
1220
 
            shop['_checked'] = "checked"
1221
 
 
1222
 
        if row.FullWorkshop:
1223
 
            fws_label = get_fullws_label()
1224
 
        else:
1225
 
            fws_label = ''
1226
 
 
1227
 
        # check permission for adding activities to products
1228
 
        perm = auth.has_membership(group_id='Admins') or \
1229
 
               auth.has_permission('update', 'workshops_products_activities')
1230
 
        if perm and not row.FullWorkshop:
1231
 
            activities = os_gui.get_button('list_notext',
1232
 
                                           URL('product_activities',
1233
 
                                               vars={'wsID': wsID,
1234
 
                                                     'wspID': row.id}),
1235
 
                                           tooltip=T("Activities"))
1236
 
 
1237
 
        # check permission to update workshops (edit and delete)
1238
 
        perm = auth.has_membership(group_id='Admins') or \
1239
 
               auth.has_permission('update', 'workshops')
1240
 
        if perm:
1241
 
            edit = os_gui.get_button('edit_notext',
1242
 
                                     URL('product_edit', args=[row.id]),
1243
 
                                     tooltip=T('Edit product'))
1244
 
 
1245
 
        # check permission to create workshop products
1246
 
        perm = auth.has_membership(group_id='Admins') or \
1247
 
               auth.has_permission('create', 'workshops_products')
1248
 
        if perm:
1249
 
            duplicate = os_gui.get_button('duplicate',
1250
 
                                          URL('product_duplicate', vars={'wspID':row.id}),
1251
 
                                          tooltip=T("Duplicate product"))
1252
 
 
1253
 
        # check delete permission
1254
 
        if row.Deletable:
1255
 
            confirm_msg = T("Really remove this product?")
1256
 
            onclick = "return confirm('" + confirm_msg + "');"
1257
 
            delete = ''
1258
 
            if auth.has_membership(group_id='Admins') or \
1259
 
                    auth.has_permission('delete', 'workshops_products'):
1260
 
                delete = os_gui.get_button('delete_notext',
1261
 
                                           URL('product_delete', vars={'wsID': wsID,
1262
 
                                                                       'wspID': row.id}),
1263
 
                                           onclick=onclick,
1264
 
                                           tooltip=T('Delete product'),
1265
 
                                           _class='pull-right')
1266
 
 
1267
 
        # check permission to view customers for a product
1268
 
        perm = auth.has_membership(group_id='Admins') or \
1269
 
               auth.has_permission('read', 'workshops_products_customers')
1270
 
        if perm:
1271
 
            customers = os_gui.get_button(
1272
 
                'user_notext',
1273
 
                URL('products_list_customers', vars={'wsID': wsID,
1274
 
                                                     'wspID': row.id}),
1275
 
                tooltip=T('Customers')
1276
 
            )
1277
 
        else:
1278
 
            customers = ''
1279
 
 
1280
 
        buttons = DIV(delete,
1281
 
                      DIV(customers,
1282
 
                          activities,
1283
 
                          edit,
1284
 
                          duplicate,
1285
 
                          _class='btn-group pull-right'))
1286
 
 
1287
 
        table.append(TR(TD(row.Name),
1288
 
                        TD(max_string_length(row.Description, 40)),
1289
 
                        TD(row.Price),
1290
 
                        TD(shop),
1291
 
                        TD(fws_label),
1292
 
                        TD(buttons)))
1293
 
 
1294
 
    products.append(table)
1295
 
 
1296
 
    ## Products end ##
1297
 
 
1298
 
    export = products_get_export(wsID)
1299
 
 
1300
 
    content = products
1301
 
 
1302
 
    menu = get_workshops_menu(request.function, wsID)
1303
 
    back = manage_get_back()
1304
 
 
1305
 
    return dict(content=content,
1306
 
                menu=menu,
1307
 
                back=back,
1308
 
                header_tools=export,
1309
 
                add=add)
1310
 
 
1311
 
 
1312
 
def products_get_export(wsID):
1313
 
    '''
1314
 
        Returns export drop down for schedule
1315
 
    '''
1316
 
    mailinglist = A(os_gui.get_fa_icon('fa-envelope-o'), T("Mailing list"),
1317
 
                    _href=URL('products_export_excel',
1318
 
                              vars={'wsID': wsID, 'export_type':'mailinglist'}))
1319
 
    attendancelist = A(os_gui.get_fa_icon('fa-check-square-o'), T("Attendance list"),
1320
 
                    _href=URL('products_export_excel',
1321
 
                              vars={'wsID': wsID, 'export_type':'attendancelist'}))
1322
 
 
1323
 
    links = [mailinglist, attendancelist]
1324
 
 
1325
 
    export = os_gui.get_dropdown_menu(
1326
 
        links=links,
1327
 
        btn_text='',
1328
 
        btn_size='btn-sm',
1329
 
        btn_icon='download',
1330
 
        menu_class='pull-right')
1331
 
 
1332
 
    return export
1333
 
 
1334
 
 
1335
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1336
 
               auth.has_permission('read', 'workshops_products'))
1337
 
def products_export_excel():
1338
 
    '''
1339
 
        Export mailinglist for a workshop product.
1340
 
        Have one worksheet for each product and one for all products
1341
 
    '''
1342
 
    def add_product_sheet(wspID):
1343
 
        wsp = WorkshopProduct(wspID)
1344
 
        # add sheet
1345
 
        ws = wb.create_sheet(title=wsp.name)
1346
 
        # get db info
1347
 
        left = [ db.auth_user.on(db.auth_user.id == db.workshops_products_customers.auth_customer_id),
1348
 
                 db.workshops_products.on(
1349
 
                     db.workshops_products_customers.workshops_products_id == db.workshops_products.id),
1350
 
                 db.invoices_workshops_products_customers.on(
1351
 
                     db.invoices_workshops_products_customers.workshops_products_customers_id ==
1352
 
                     db.workshops_products_customers.id),
1353
 
                 db.invoices.on(db.invoices_workshops_products_customers.invoices_id == db.invoices.id)
1354
 
                 ]
1355
 
        query = ((db.workshops_products_customers.workshops_products_id == wspID) &
1356
 
                 (db.workshops_products_customers.Cancelled == False))
1357
 
        rows = db(query).select(db.auth_user.first_name,
1358
 
                                db.auth_user.last_name,
1359
 
                                db.auth_user.email,
1360
 
                                db.invoices.InvoiceID,
1361
 
                                db.workshops_products.Name,
1362
 
                                left=left)
1363
 
 
1364
 
        # add info to sheet
1365
 
        for row in rows:
1366
 
            if export_type == 'mailinglist':
1367
 
                ws.append([row.auth_user.first_name,
1368
 
                           row.auth_user.last_name,
1369
 
                           row.auth_user.email])
1370
 
            elif export_type == 'attendancelist':
1371
 
                ws.append([row.auth_user.first_name,
1372
 
                           row.auth_user.last_name,
1373
 
                           row.auth_user.email,
1374
 
                           row.invoices.InvoiceID,
1375
 
                           row.workshops_products.Name])
1376
 
 
1377
 
    wsID = request.vars['wsID']
1378
 
    export_type = request.vars['export_type']
1379
 
    workshop = Workshop(wsID)
1380
 
 
1381
 
    # create filestream
1382
 
    stream = cStringIO.StringIO()
1383
 
 
1384
 
    wb = openpyxl.workbook.Workbook(write_only=True)
1385
 
    # write the sheet for all mail addresses
1386
 
    ws = wb.create_sheet(title="All products")
1387
 
    # get all products for a workshop
1388
 
 
1389
 
    products = workshop.get_products()
1390
 
    product_ids = []
1391
 
    for product in products:
1392
 
        product_ids.append(product.id)
1393
 
 
1394
 
 
1395
 
    left = [ db.auth_user.on(db.auth_user.id == db.workshops_products_customers.auth_customer_id),
1396
 
             db.workshops_products.on(db.workshops_products_customers.workshops_products_id == db.workshops_products.id),
1397
 
             db.invoices_workshops_products_customers.on(
1398
 
                 db.invoices_workshops_products_customers.workshops_products_customers_id ==
1399
 
                 db.workshops_products_customers.id),
1400
 
             db.invoices.on(db.invoices_workshops_products_customers.invoices_id == db.invoices.id),
1401
 
             ]
1402
 
    query = ((db.workshops_products_customers.workshops_products_id.belongs(product_ids)) &
1403
 
             (db.workshops_products_customers.Cancelled == False))
1404
 
    rows = db(query).select(db.auth_user.first_name,
1405
 
                            db.auth_user.last_name,
1406
 
                            db.auth_user.email,
1407
 
                            db.invoices.InvoiceID,
1408
 
                            db.workshops_products.Name,
1409
 
                            left=left)
1410
 
 
1411
 
    emails = []
1412
 
 
1413
 
    for row in rows:
1414
 
        # distinct isn't working here in w2p 2.15.4, this is a workaround
1415
 
        print emails
1416
 
        if row.auth_user.email not in emails:
1417
 
            if export_type == 'mailinglist':
1418
 
                ws.append([row.auth_user.first_name,
1419
 
                           row.auth_user.last_name,
1420
 
                           row.auth_user.email])
1421
 
            elif export_type == 'attendancelist':
1422
 
                ws.append([row.auth_user.first_name,
1423
 
                           row.auth_user.last_name,
1424
 
                           row.auth_user.email,
1425
 
                           row.invoices.InvoiceID,
1426
 
                           row.workshops_products.Name])
1427
 
        emails.append(row.auth_user.email)
1428
 
 
1429
 
    # Add all products
1430
 
    for wspID in product_ids:
1431
 
        add_product_sheet(wspID)
1432
 
 
1433
 
    # Return to user
1434
 
    if export_type == 'mailinglist':
1435
 
        fname = T("Mailinglist") + '.xlsx'
1436
 
    elif export_type == 'attendancelist':
1437
 
        fname = T('AttendanceList') + '.xlsx'
1438
 
    wb.save(stream)
1439
 
 
1440
 
    response.headers['Content-Type'] = 'application/vnd.ms-excel'
1441
 
    response.headers['Content-disposition'] = 'attachment; filename=' + fname
1442
 
 
1443
 
    return stream.getvalue()
1444
 
 
1445
 
 
1446
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1447
 
               auth.has_permission('create', 'workshops_products'))
1448
 
def product_duplicate():
1449
 
    '''
1450
 
        Duplicate workshop product & linked activities
1451
 
    '''
1452
 
    wspID = request.vars['wspID']
1453
 
 
1454
 
    wsp = db.workshops_products(wspID)
1455
 
 
1456
 
    new_wspID = db.workshops_products.insert(
1457
 
        workshops_id = wsp.workshops_id,
1458
 
        FullWorkshop = False,
1459
 
        Deletable = True,
1460
 
        PublicProduct = False,
1461
 
        Name = wsp.Name + ' (Copy)',
1462
 
        Price = wsp.Price,
1463
 
        tax_rates_id = wsp.tax_rates_id,
1464
 
        Description = wsp.Description,
1465
 
        ExternalShopURL = wsp.ExternalShopURL,
1466
 
        AddToCartText = wsp.AddToCartText,
1467
 
        Donation = wsp.Donation
1468
 
    )
1469
 
 
1470
 
    if wsp.FullWorkshop:
1471
 
        query = (db.workshops_activities.workshops_id == wsp.workshops_id)
1472
 
        rows = db(query).select(db.workshops_activities.id)
1473
 
        for row in rows:
1474
 
            db.workshops_products_activities.insert(
1475
 
                workshops_products_id=new_wspID,
1476
 
                workshops_activities_id=row.id
1477
 
            )
1478
 
    else:
1479
 
        query = (db.workshops_products_activities.workshops_products_id == wspID)
1480
 
        rows = db(query).select(db.workshops_products_activities.ALL)
1481
 
        for row in rows:
1482
 
            db.workshops_products_activities.insert(
1483
 
                workshops_products_id = new_wspID,
1484
 
                workshops_activities_id = row.workshops_activities_id
1485
 
            )
1486
 
 
1487
 
    session.flash = T('You are now editing the duplicated product')
1488
 
 
1489
 
    redirect(URL('workshops', 'product_edit', args=new_wspID))
1490
 
 
1491
 
 
1492
 
def workshop_get_alert_no_activities(wsID):
1493
 
    '''
1494
 
        Display an information banner to notify user when no activities have been found
1495
 
    '''
1496
 
    alert = ''
1497
 
 
1498
 
    query = (db.workshops_activities.workshops_id == wsID)
1499
 
    count = db(query).count()
1500
 
    if not count:
1501
 
        add = os_gui.get_button('add', URL('activity_add', args=wsID),
1502
 
                                btn_class='btn-link',
1503
 
                                title=T('Add activity'))
1504
 
        alert = os_gui.get_alert('info', SPAN(
1505
 
            SPAN(T('No activities have been found for this event'), _class="bold"), ' ',
1506
 
            add))
1507
 
 
1508
 
    return alert
1509
 
 
1510
 
 
1511
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1512
 
               auth.has_permission('read', 'workshops_activities'))
1513
 
def activities():
1514
 
    '''
1515
 
        Manage agenda for workshops
1516
 
    '''
1517
 
    wsID = request.vars['wsID']
1518
 
    response.title = T('Event')
1519
 
    response.subtitle = get_subtitle(wsID)
1520
 
    response.view = 'workshops/manage.html'
1521
 
 
1522
 
    ## Modals container
1523
 
    modals = DIV()
1524
 
 
1525
 
    ## Agenda begin  ##
1526
 
    agenda = DIV()
1527
 
 
1528
 
    add = ''
1529
 
    perm = auth.has_membership(group_id='Admins') or \
1530
 
           auth.has_permission('create', 'workshops_activities')
1531
 
    if perm:
1532
 
        add_url = URL('activity_add', args=[wsID])
1533
 
        add = os_gui.get_button('add', add_url, T("Add new activity"), _class='pull-right')
1534
 
 
1535
 
    agenda_items = TABLE(THEAD(TR(TH(T("Date")),
1536
 
                                  TH(T("Time")),
1537
 
                                  TH(T("Activity")),
1538
 
                                  TH(T("Location")),
1539
 
                                  TH(T('Teacher')),
1540
 
                                  TH(T("Filled")),
1541
 
                                  TH())),
1542
 
                         _class='table table-hover')
1543
 
    query = (db.workshops_activities.workshops_id == wsID)
1544
 
    rows = db(query).select(db.workshops_activities.ALL,
1545
 
                            orderby=db.workshops_activities.Activitydate | \
1546
 
                                    db.workshops_activities.Starttime)
1547
 
    for row in rows.render():
1548
 
        wsaID = row.id
1549
 
 
1550
 
        internal_location = not row.school_locations_id is None and \
1551
 
                            not row.school_locations_id == ''
1552
 
        if internal_location:
1553
 
            location = row.school_locations_id
1554
 
        else:
1555
 
            location = row.LocationExternal
1556
 
 
1557
 
        fwsID = workshops_get_full_workshop_product_id(wsID)
1558
 
        activity = TR(TD(row.Activitydate),
1559
 
                      TD(row.Starttime, ' - ', row.Endtime),
1560
 
                      TD(row.Activity),
1561
 
                      TD(location),
1562
 
                      TD(row.auth_teacher_id),
1563
 
                      TD(activity_get_filled(row, fwsID)),
1564
 
                      )
1565
 
 
1566
 
        # check permissions to manage customers attendance
1567
 
        customers = ''
1568
 
 
1569
 
        perm = auth.has_membership(group_id='Admins') or \
1570
 
               auth.has_permission('update', 'workshops_activities_customers')
1571
 
        if perm:
1572
 
            modal_title = row.Activity + ' ' + \
1573
 
                          row.Activitydate + ' ' + \
1574
 
                          T('Customers')
1575
 
            load_content = os_gui.get_ajax_loader(message=T("Loading..."))
1576
 
            modal_content = LOAD('workshops', 'activity_list_customers.load',
1577
 
                                 ajax=True,
1578
 
                                 content=load_content,
1579
 
                                 vars={'wsID': wsID, 'wsaID': row.id})
1580
 
 
1581
 
            customers_button_id = 'wsa_cust_' + unicode(row.id)
1582
 
            btn_icon = SPAN(_class='glyphicon glyphicon-user')
1583
 
 
1584
 
            result = os_gui.get_modal(button_text=XML(btn_icon),
1585
 
                                      modal_title=modal_title,
1586
 
                                      modal_content=modal_content,
1587
 
                                      modal_class=customers_button_id,
1588
 
                                      modal_size='',
1589
 
                                      button_class='btn-sm workshops_show_customers',
1590
 
                                      button_id=customers_button_id)
1591
 
            modals.append(result['modal'])
1592
 
            customers = result['button']
1593
 
 
1594
 
        # check permission to edit activities
1595
 
        edit = ''
1596
 
        duplicate = ''
1597
 
        delete = ''
1598
 
 
1599
 
        perm = auth.has_membership(group_id='Admins') or \
1600
 
               auth.has_permission('update', 'workshops_activities')
1601
 
        if perm:
1602
 
            edit = os_gui.get_button('edit_notext',
1603
 
                                     URL('activity_edit', args=[wsaID]),
1604
 
                                     tooltip=T("Edit activity"))
1605
 
 
1606
 
            duplicate = os_gui.get_button('duplicate',
1607
 
                                          URL('activity_duplicate', args=[wsaID]),
1608
 
                                          tooltip=T("Duplicate activity"))
1609
 
 
1610
 
        if auth.has_membership(group_id='Admins') or \
1611
 
                auth.has_permission('delete', 'workshops_activities'):
1612
 
            confirm_msg = T("Are you sure you want to delete this activity?")
1613
 
            delete = os_gui.get_button('delete_notext',
1614
 
                                       URL('activity_delete', args=[wsID, wsaID]),
1615
 
                                       tooltip=T('Delete activity'),
1616
 
                                       onclick="return confirm('" + confirm_msg + "');")
1617
 
 
1618
 
        buttons = DIV(customers,
1619
 
                      edit,
1620
 
                      duplicate,
1621
 
                      delete,
1622
 
                      _class='btn-group pull-right')
1623
 
        activity.append(TD(buttons))
1624
 
 
1625
 
        agenda_items.append(activity)
1626
 
 
1627
 
    agenda.append(agenda_items)
1628
 
 
1629
 
    permission = auth.has_membership(group_id='Admins') or \
1630
 
                 auth.has_permission('update', 'class_status')
1631
 
    if permission:
1632
 
        oc_count = overlapping_classes_get_count_all(wsID)
1633
 
        if oc_count == 0:
1634
 
            badge_type = 'success'
1635
 
        else:
1636
 
            badge_type = 'default'
1637
 
        oc_badge = get_badge(badge_type, oc_count)
1638
 
        oc = DIV(A(T("Overlapping classes"), ' ', oc_badge,
1639
 
                   _href=URL('overlapping_classes', vars={'wsID': wsID}),
1640
 
                   _class='btn btn-primary btn-sm'),
1641
 
                 _class='pull-right ')
1642
 
        agenda.append(oc)
1643
 
    mail = A(SPAN(_class='glyphicon glyphicon-envelope'), ' ',
1644
 
             T("Mail attendance lists to teacher"),
1645
 
             _href=URL('mail_activity_attendance', vars={'wsID': wsID}),
1646
 
             _class='btn btn-primary btn-sm')
1647
 
    agenda.append(mail)
1648
 
 
1649
 
    ## Agenda end ##
1650
 
 
1651
 
    export_activities = A(SPAN(os_gui.get_fa_icon('fa-check'), ' ',
1652
 
                               T("Attendance")),
1653
 
                          _href=URL('activities_export_attendance',
1654
 
                                    vars={'wsID': wsID}))
1655
 
 
1656
 
    links = [export_activities]
1657
 
 
1658
 
    export = os_gui.get_dropdown_menu(
1659
 
        links=links,
1660
 
        btn_text='',
1661
 
        btn_size='btn-sm',
1662
 
        btn_icon='download',
1663
 
        menu_class='pull-right')
1664
 
 
1665
 
    content = DIV(agenda, modals)
1666
 
 
1667
 
    menu = get_workshops_menu(request.function, wsID)
1668
 
    back = manage_get_back()
1669
 
 
1670
 
    return dict(content=content,
1671
 
                menu=menu,
1672
 
                back=back,
1673
 
                header_tools=export,
1674
 
                add=add)
1675
 
 
1676
 
 
1677
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1678
 
               auth.has_permission('read', 'workshops_activities'))
1679
 
def activities_export_attendance():
1680
 
    '''
1681
 
        Create excel export of workshop attendance
1682
 
        request.vars['wsID'] is expected to be workshops.id
1683
 
    '''
1684
 
    wsID = request.vars['wsID']
1685
 
    workshop = db.workshops(wsID)
1686
 
 
1687
 
    query = (db.workshops_activities.workshops_id == wsID)
1688
 
    rows = db(query).select(db.workshops_activities.id,
1689
 
                            orderby=db.workshops_activities.Activitydate)
1690
 
    wsa_ids = []
1691
 
    for row in rows:
1692
 
        wsa_ids.append(row.id)
1693
 
 
1694
 
    fw_rows = activity_list_customers_get_fullws_rows(wsID)
1695
 
 
1696
 
    # create filestream
1697
 
    stream = cStringIO.StringIO()
1698
 
 
1699
 
    # Create the workbook
1700
 
    title = 'test'
1701
 
    wb = openpyxl.workbook.Workbook(write_only=True)
1702
 
 
1703
 
    # process activities
1704
 
    for wsaID in wsa_ids:
1705
 
        activity = db.workshops_activities(wsaID)
1706
 
        title = activity.Activity[0:30]
1707
 
        date = activity.Activitydate.strftime(DATE_FORMAT)
1708
 
        start = activity.Starttime.strftime(TIME_FORMAT)
1709
 
        ws = wb.create_sheet(title=title)
1710
 
        desc = [workshop.Name + ' ' + date + ' ' + title + ' ' + start]
1711
 
        ws.append(desc)
1712
 
        ws.append([' '])
1713
 
        header = ['Customer', 'Product', 'Attending']
1714
 
        ws.append(header)
1715
 
 
1716
 
        # add full workshop customers
1717
 
        for row in fw_rows.render():
1718
 
            cuID = row.auth_user.id
1719
 
 
1720
 
            # check attendance
1721
 
            attending = ''
1722
 
            check = db.workshops_activities_customers(
1723
 
                auth_customer_id=cuID,
1724
 
                workshops_activities_id=wsaID)
1725
 
            if check:
1726
 
                attending = 'X'
1727
 
 
1728
 
            custname = row.auth_user.display_name
1729
 
            excel_row = [custname, 'Full event', attending]
1730
 
            ws.append(excel_row)
1731
 
 
1732
 
        # add customers with another product
1733
 
        a_rows = activity_list_customers_get_activity_rows(wsaID)
1734
 
        for row in a_rows.render():
1735
 
            cuID = row.auth_user.id
1736
 
            product = row.workshops_products_customers.workshops_products_id
1737
 
 
1738
 
            # check attendance
1739
 
            attending = ''
1740
 
            check = db.workshops_activities_customers(auth_customer_id=cuID,
1741
 
                                                      workshops_activities_id=wsaID)
1742
 
            if check:
1743
 
                attending = 'X'
1744
 
 
1745
 
            custname = row.auth_user.display_name
1746
 
            excel_row = [custname, product, attending]
1747
 
            ws.append(excel_row)
1748
 
 
1749
 
    wb.save(stream)
1750
 
    fname = 'Attendance.xlsx'
1751
 
    response.headers['Content-Type'] = 'application/vnd.ms-excel'
1752
 
    response.headers['Content-disposition'] = 'attachment; filename=' + fname
1753
 
 
1754
 
    return stream.getvalue()
1755
 
 
1756
 
 
1757
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1758
 
               auth.has_permission('update', 'workshops_activities'))
1759
 
def activity_list_customers():
1760
 
    '''
1761
 
        List customers for an activity
1762
 
        request.args[0] is expected to be the workshops_id (wsID)
1763
 
        request.args[1] is expected to be the workshops_activities_id (wsaID)
1764
 
    '''
1765
 
    wsID = request.vars['wsID']
1766
 
    wsaID = request.vars['wsaID']
1767
 
 
1768
 
    # get activity attendance
1769
 
    table = activity_list_customers_get_list(wsID, wsaID)
1770
 
 
1771
 
    return dict(content=table)
1772
 
 
1773
 
 
1774
 
def activity_list_customers_get_list(wsID,
1775
 
                                     wsaID):
1776
 
    '''
1777
 
        Lists customers for activity
1778
 
    '''
1779
 
 
1780
 
    def process_rows(table, rows, fullWS=False):
1781
 
        '''
1782
 
            Helper to add rows to table
1783
 
        '''
1784
 
        for row in rows.render():
1785
 
            # get customer name
1786
 
            cust_name = TD(row.auth_user.display_name, BR(),
1787
 
                           _class='os-customer_name')
1788
 
            if fullWS:
1789
 
                cust_name.append(get_fullws_label())
1790
 
            else:
1791
 
                product = row.workshops_products_customers.workshops_products_id
1792
 
                cust_name.append(get_label('default', product))
1793
 
 
1794
 
            # get attendance checkbox
1795
 
            cuID = row.auth_user.id
1796
 
            attendance = activity_list_customers_get_list_get_attendance(cuID,
1797
 
                                                                         wsaID)
1798
 
            table.append(TR(TD(row.auth_user.thumbsmall,
1799
 
                               _class='os-customer_image_td'),
1800
 
                            cust_name,
1801
 
                            TD(attendance)))
1802
 
        return table
1803
 
 
1804
 
    table = TABLE(TR(TH(''), TH(''), TH(T('Attending')), _class='header'),
1805
 
                  _class='table')
1806
 
 
1807
 
    # Add full workshop customers
1808
 
    rows = activity_list_customers_get_fullws_rows(wsID)
1809
 
    table = process_rows(table, rows, fullWS=True)
1810
 
 
1811
 
    # Add activity customers
1812
 
    rows = activity_list_customers_get_activity_rows(wsaID)
1813
 
    table = process_rows(table, rows)
1814
 
 
1815
 
    return table
1816
 
 
1817
 
 
1818
 
def activity_list_customers_get_fullws_rows(wsID):
1819
 
    '''
1820
 
        Returns rows object for full workshops customers of a workshop
1821
 
    '''
1822
 
    orderby = ~db.auth_user.display_name
1823
 
    fwsID = workshops_get_full_workshop_product_id(wsID)
1824
 
    query = (db.workshops_products_customers.Cancelled == False) & \
1825
 
            (db.workshops_products_customers.Waitinglist == False) & \
1826
 
            (db.workshops_products_customers.workshops_products_id == fwsID)
1827
 
    rows = db(query).select(
1828
 
        db.auth_user.id,
1829
 
        db.auth_user.archived,
1830
 
        db.auth_user.thumbsmall,
1831
 
        db.auth_user.birthday,
1832
 
        db.auth_user.display_name,
1833
 
        db.workshops_products_customers.workshops_products_id,
1834
 
        left=[db.auth_user.on(db.workshops_products_customers.auth_customer_id == \
1835
 
                              db.auth_user.id)],
1836
 
        orderby=orderby)
1837
 
 
1838
 
    return rows
1839
 
 
1840
 
 
1841
 
def activity_list_customers_get_activity_rows(wsaID):
1842
 
    '''
1843
 
        Returns rows for a workshop activity
1844
 
    '''
1845
 
    orderby = ~db.auth_user.display_name
1846
 
    left = [db.auth_user.on(db.auth_user.id == \
1847
 
                            db.workshops_products_customers.auth_customer_id),
1848
 
            db.workshops_products.on(db.workshops_products.id == \
1849
 
                                     db.workshops_products_customers.workshops_products_id)]
1850
 
 
1851
 
    query = activity_list_customers_get_list_activity_query(wsaID)
1852
 
    rows = db(query).select(
1853
 
        db.auth_user.id,
1854
 
        db.auth_user.archived,
1855
 
        db.auth_user.thumbsmall,
1856
 
        db.auth_user.birthday,
1857
 
        db.auth_user.display_name,
1858
 
        db.workshops_products_activities.workshops_activities_id,
1859
 
        db.workshops_products_customers.workshops_products_id,
1860
 
        left=left,
1861
 
        orderby=orderby)
1862
 
 
1863
 
    return rows
1864
 
 
1865
 
 
1866
 
def activity_list_customers_get_list_activity_query(wsaID):
1867
 
    '''
1868
 
        Returns a query that returns a set of all customers in a specific
1869
 
        workshop activity, without the full workshop customers
1870
 
    '''
1871
 
    query = (db.workshops_activities.id ==
1872
 
             db.workshops_products_activities.workshops_activities_id) & \
1873
 
            (db.workshops_products_customers.workshops_products_id ==
1874
 
             db.workshops_products_activities.workshops_products_id) & \
1875
 
            (db.workshops_products_activities.workshops_activities_id ==
1876
 
             wsaID) & \
1877
 
            (db.workshops_products_customers.Waitinglist == False) & \
1878
 
            (db.workshops_products_customers.Cancelled == False)
1879
 
 
1880
 
    return query
1881
 
 
1882
 
 
1883
 
def activity_list_customers_get_list_get_attendance(cuID, wsaID):
1884
 
    '''
1885
 
        Checks whether a customer is attending a class
1886
 
    '''
1887
 
    check = db.workshops_activities_customers(auth_customer_id=cuID,
1888
 
                                              workshops_activities_id=wsaID)
1889
 
 
1890
 
    name = 'attending'
1891
 
    value = 'on'
1892
 
    if check:
1893
 
        checkbox = INPUT(_name=name,
1894
 
                         _value=value,
1895
 
                         _type='checkbox',
1896
 
                         _checked='checked')
1897
 
    else:
1898
 
        checkbox = INPUT(_name=name,
1899
 
                         _value=value,
1900
 
                         _type='checkbox')
1901
 
 
1902
 
    hidden_field_cuID = INPUT(_type="hidden",
1903
 
                              _name="cuID",
1904
 
                              _value=cuID)
1905
 
    hidden_field_wsaID = INPUT(_type="hidden",
1906
 
                               _name="wsaID",
1907
 
                               _value=wsaID)
1908
 
 
1909
 
    form = FORM(checkbox, hidden_field_cuID, hidden_field_wsaID)
1910
 
 
1911
 
    return form
1912
 
 
1913
 
 
1914
 
@auth.requires(auth.has_membership(group_id='Admins') or \
1915
 
               auth.has_permission('read', 'workshops_products_customers'))
1916
 
def products_list_customers():
1917
 
    '''
1918
 
        Lists customers for a product
1919
 
    '''
1920
 
    # set workshop ID
1921
 
    if 'wsID' in request.vars:
1922
 
        wsID = request.vars['wsID']
1923
 
        session.workshops_products_list_customers_wsID = wsID
1924
 
    elif session.workshops_products_list_customers_wsID:
1925
 
        wsID = session.workshops_products_list_customers_wsID
1926
 
 
1927
 
    # set workshop product ID
1928
 
    if 'wspID' in request.vars:
1929
 
        wspID = request.vars['wspID']
1930
 
        session.workshops_products_list_customers_wspID = wspID
1931
 
    elif session.workshops_products_list_customers_wspID:
1932
 
        wspID = session.workshops_products_list_customers_wspID
1933
 
 
1934
 
    session.invoices_edit_back = 'workshops_products_list_customers'
1935
 
    session.invoices_payment_add_back = 'workshops_products_list_customers'
1936
 
    session.workshops_product_resend_info_mail = 'workshops_products_list_customers'
1937
 
 
1938
 
    wsp = WorkshopProduct(wspID)
1939
 
 
1940
 
    response.title = T('Event')
1941
 
    subtitle = SPAN(get_subtitle(wsID), ' > ', T('Customers'), ': ', wsp.name)
1942
 
    response.subtitle = subtitle
1943
 
    response.view = 'workshops/product_list_customers.html'
1944
 
 
1945
 
    session.customers_payment_back = 'workshops'
1946
 
    session.workshops_product_sell_back = None
1947
 
 
1948
 
    add = ''
1949
 
    perm = auth.has_membership(group_id='Admins') or \
1950
 
           auth.has_permission('update', 'workshops_products_customers')
1951
 
    if perm:
1952
 
        sold_out = product_check_sold_out(wsID, wspID)
1953
 
        if not sold_out:
1954
 
            search = get_input_search(_id='product_sell_input_search')
1955
 
            modal_content = DIV(
1956
 
                search,
1957
 
                LOAD('workshops', 'product_sell.load',
1958
 
                     vars={'wsID': wsID, 'wspID': wspID},
1959
 
                     content=os_gui.get_ajax_loader()))
1960
 
 
1961
 
            modal_title = SPAN(T("Add customers to"), ' ', wsp.name)
1962
 
 
1963
 
            button_text = os_gui.get_modal_button_icon('add', T("Add"))
1964
 
            result = os_gui.get_modal(
1965
 
                button_text=button_text,
1966
 
                button_class='btn-sm',
1967
 
                modal_title=modal_title,
1968
 
                modal_content=modal_content,
1969
 
                modal_size='lg',
1970
 
                modal_class='product_sell_modal',
1971
 
                close_id='product_sell_close'
1972
 
            )
1973
 
            add = SPAN(result['button'], result['modal'])
1974
 
        else:
1975
 
            add = DIV(  # BR(),
1976
 
                T("Sold out,"),
1977
 
                ' ',
1978
 
                T("one or more activities listed in this "),
1979
 
                T("product is fully booked."),
1980
 
                _class='green')
1981
 
 
1982
 
    full_wspID = workshops_get_full_workshop_product_id(wsID)
1983
 
    table = TABLE(_class='table')
1984
 
    ## sold products
1985
 
    # add full workshop customers for other activities
1986
 
    # if not int(wspID) == full_wspID:
1987
 
    #     table = products_list_customers_get_list(table,
1988
 
    #                                              wsID,
1989
 
    #                                              full_wspID,
1990
 
    #                                              fullWS=True,
1991
 
    #                                              deletable=False)
1992
 
    # add customers for selected  product
1993
 
    if int(wspID) == full_wspID:  # show "Full workshop for Full workhshop act."
1994
 
        fullWS = True
1995
 
    else:
1996
 
        fullWS = False
1997
 
    table = products_list_customers_get_list(table,
1998
 
                                             wsID,
1999
 
                                             wspID,
2000
 
                                             fullWS,
2001
 
                                             deletable=True)
2002
 
 
2003
 
    content = DIV(H4(T('Customers for'), ' ', wsp.name), BR(), table)
2004
 
 
2005
 
    menu = get_workshops_menu('products', wsID)
2006
 
    back = os_gui.get_button('back', URL('workshops', 'products', vars={'wsID':wsID}))
2007
 
    loader = os_gui.get_ajax_loader(message=T("Refreshing list..."))
2008
 
 
2009
 
    return dict(content=content,
2010
 
                menu=menu,
2011
 
                add=add,
2012
 
                back=back,
2013
 
                loader=loader)
2014
 
 
2015
 
 
2016
 
def products_list_customers_get_list(table,
2017
 
                                     wsID,
2018
 
                                     wspID,
2019
 
                                     fullWS=False,
2020
 
                                     deletable=False):
2021
 
    '''
2022
 
        Append customers to table
2023
 
    '''
2024
 
    wh = WorkshopsHelper()
2025
 
    db_icwspc = db.invoices_workshops_products_customers
2026
 
 
2027
 
    query = (db.workshops_products_customers.workshops_products_id == wspID)
2028
 
    rows = db(query).select(
2029
 
        db.workshops_products_customers.ALL,
2030
 
        db.auth_user.id,
2031
 
        db.auth_user.archived,
2032
 
        db.auth_user.thumbsmall,
2033
 
        db.auth_user.birthday,
2034
 
        db.auth_user.display_name,
2035
 
        db.invoices.id,
2036
 
        db.invoices.InvoiceID,
2037
 
        db.invoices.Status,
2038
 
        db.invoices.payment_methods_id,
2039
 
        left=[db.auth_user.on(db.workshops_products_customers.auth_customer_id == \
2040
 
                              db.auth_user.id),
2041
 
              # db.invoices.on(db.invoices.workshops_products_customers_id ==
2042
 
              #                db.workshops_products_customers.id)
2043
 
              db.invoices_workshops_products_customers.on(
2044
 
                  db_icwspc.workshops_products_customers_id == db.workshops_products_customers.id),
2045
 
              db.invoices.on(db_icwspc.invoices_id == db.invoices.id)],
2046
 
        orderby=db.workshops_products_customers.Cancelled | \
2047
 
                db.workshops_products_customers.Waitinglist | \
2048
 
                db.auth_user.display_name)
2049
 
 
2050
 
    left = [db.workshops_products.on(
2051
 
        db.workshops_products.id == \
2052
 
        db.workshops_products_customers.workshops_products_id),
2053
 
               db.workshops.on(db.workshops_products.workshops_id == \
2054
 
                               db.workshops.id),
2055
 
               db.invoices_workshops_products_customers.on(
2056
 
                   db_icwspc.workshops_products_customers_id ==
2057
 
                   db.workshops_products_customers.id),
2058
 
               db.invoices.on(db_icwspc.invoices_id == db.invoices.id)
2059
 
           ],
2060
 
 
2061
 
    ih = InvoicesHelper()
2062
 
    for i, row in enumerate(rows):
2063
 
        repr_row = list(rows[i:i + 1].render())[0]
2064
 
 
2065
 
        wsp_cuID = row.workshops_products_customers.id
2066
 
 
2067
 
        # Event info link
2068
 
        link_text = T('Send')
2069
 
        if row.workshops_products_customers.WorkshopInfo:
2070
 
            link_text = T('Resend')
2071
 
        resend_link = A(link_text, ' ', T('info mail'),
2072
 
                        _href=URL('product_resend_info_mail', vars={'wspcID':wsp_cuID}))
2073
 
        event_info = wh.get_customer_info(wsp_cuID,
2074
 
                                          wsID,
2075
 
                                          row.workshops_products_customers.WorkshopInfo,
2076
 
                                          resend_link)
2077
 
 
2078
 
        cust_name = TD(SPAN(row.auth_user.display_name, _class='bold'), BR())
2079
 
        if fullWS:
2080
 
            cust_name.append(get_fullws_label())
2081
 
        else:
2082
 
            product = repr_row.workshops_products_customers.workshops_products_id
2083
 
            cust_name.append(get_label('default', product))
2084
 
 
2085
 
        tr_class = ''
2086
 
        if row.workshops_products_customers.Cancelled:
2087
 
            if fullWS:
2088
 
                cust_name.append(' ')
2089
 
            cust_name.append(get_cancelled_label())
2090
 
 
2091
 
        if row.workshops_products_customers.Waitinglist:
2092
 
            wai_url = URL('product_remove_customer_from_waitinglist',
2093
 
                          vars={'wsp_cuID': wsp_cuID,
2094
 
                                'wsID': wsID})
2095
 
            if fullWS:
2096
 
                cust_name.append(' ')
2097
 
            else:
2098
 
                cust_name.append(BR())
2099
 
            cust_name.append(SPAN(T("Waitinglist"),
2100
 
                                  _class='label label-danger'))
2101
 
            cust_name.append(BR())
2102
 
            if not product_check_sold_out(wsID, wspID):
2103
 
                msg = T("Remove from waitinglist, add to list")
2104
 
                cust_name.append(SPAN(A(msg,
2105
 
                                        _href=wai_url,
2106
 
                                        cid=request.cid),
2107
 
                                      _class='small_font'))
2108
 
 
2109
 
        # invoice
2110
 
        if row.invoices.id:
2111
 
            invoice = ih.represent_invoice_for_list(
2112
 
                row.invoices.id,
2113
 
                repr_row.invoices.InvoiceID,
2114
 
                repr_row.invoices.Status,
2115
 
                row.invoices.Status,
2116
 
                row.invoices.payment_methods_id
2117
 
            )
2118
 
        else:
2119
 
            invoice = ''
2120
 
 
2121
 
        buttons = products_list_customers_get_buttons(wsID, wsp_cuID)
2122
 
 
2123
 
        table.append(TR(TD(repr_row.auth_user.thumbsmall,
2124
 
                           _class='os-customer_image_td'),
2125
 
                        cust_name,
2126
 
                        TD(invoice),
2127
 
                        TD(event_info),
2128
 
                        TD(' ', buttons, _class='td-icons'),
2129
 
                        _class=tr_class))
2130
 
 
2131
 
    return table
2132
 
 
2133
 
 
2134
 
def products_list_customers_get_cancelled(wsp_cuID):
2135
 
    '''
2136
 
        Returns whether or not a customer has cancelled a workshop product
2137
 
    '''
2138
 
    row = db.workshops_products_customers(id=wsp_cuID)
2139
 
    return row.Cancelled
2140
 
 
2141
 
 
2142
 
def products_list_customers_get_buttons(wsID, wsp_cuID):
2143
 
    '''
2144
 
        returns buttons for produtcs_list_customers
2145
 
    '''
2146
 
    confirm_remove_msg = T("Really remove this customer from the list?")
2147
 
    cid = request.cid
2148
 
 
2149
 
    cancelled = products_list_customers_get_cancelled(wsp_cuID)
2150
 
    if cancelled:
2151
 
        title_cancel = T('Undo cancellation')
2152
 
    else:
2153
 
        title_cancel = T('Cancel customer')
2154
 
 
2155
 
    btn_delete = ''
2156
 
    if auth.has_membership(group_id='Admins') or \
2157
 
            auth.has_permission('delete', 'workshops_products_customers'):
2158
 
        btn_delete = os_gui.get_button(
2159
 
            'delete_notext',
2160
 
            URL('product_delete_customer', vars={'wsID': wsID,
2161
 
                                                 'wsp_cuID': wsp_cuID}),
2162
 
            cid=cid,
2163
 
            tooltip=T('Remove customer from list'),
2164
 
            onclick="return confirm('" + confirm_remove_msg + "');",
2165
 
        )
2166
 
 
2167
 
    btn_cancel = os_gui.get_button(
2168
 
        'cancel_notext',
2169
 
        URL('product_cancel_customer',
2170
 
            vars={'wsID': wsID,
2171
 
                  'wsp_cuID': wsp_cuID}),
2172
 
        cid=cid,
2173
 
        tooltip=title_cancel)
2174
 
 
2175
 
    return DIV(btn_cancel,
2176
 
               btn_delete,
2177
 
               _class='btn-group pull-right')
2178
 
 
2179
 
 
2180
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2181
 
               auth.has_permission('update', 'workshops_activities'))
2182
 
def product_customer_update_info():
2183
 
    '''
2184
 
        Called as JSON
2185
 
        This function is meant to be called with the json extension.
2186
 
        It takes id and WorkshopInfo as variables.
2187
 
    '''
2188
 
    if not request.extension == 'json':
2189
 
        return T("Error, please call as JSON")
2190
 
 
2191
 
    response.view = 'generic.json'
2192
 
    status = "fail"
2193
 
 
2194
 
    if request.vars:
2195
 
        wsp_cuID = request.vars['id']
2196
 
        row = db.workshops_products_customers(wsp_cuID)
2197
 
 
2198
 
        # info
2199
 
        if 'WorkshopInfo' in request.vars:
2200
 
            row.WorkshopInfo = 'T'
2201
 
        else:
2202
 
            row.WorkshopInfo = 'F'
2203
 
 
2204
 
        result = row.update_record()
2205
 
 
2206
 
        if result:
2207
 
            status = "success"
2208
 
            message = T("Saved")
2209
 
        else:
2210
 
            message = T("Uh oh... something went wrong...")
2211
 
    else:
2212
 
        message = T("Error: no data received")
2213
 
 
2214
 
    return dict(status=status, message=message)
2215
 
 
2216
 
 
2217
 
def products_count_sold(wspID):
2218
 
    '''
2219
 
        Returns count of customers who bought a product
2220
 
    '''
2221
 
    query = (db.workshops_products_customers.workshops_products_id == wspID)
2222
 
    return db(query).count()
2223
 
 
2224
 
 
2225
 
def products_get_return_url(wsID):
2226
 
    '''
2227
 
        return the return URL for products add and edit pages
2228
 
    '''
2229
 
    return URL('products', vars={'wsID': wsID})
2230
 
 
2231
 
 
2232
 
@auth.requires_login()
2233
 
def product_add():
2234
 
    '''
2235
 
        Add a new product
2236
 
    '''
2237
 
    wsID = request.args[0]
2238
 
 
2239
 
    response.title = T('Event')
2240
 
    response.subtitle = get_subtitle(wsID)
2241
 
    response.view = 'general/tabs_menu.html'
2242
 
 
2243
 
    return_url = products_get_return_url(wsID)
2244
 
    next_url = '/workshops/product_activities?wspID=[id]&wsID=' + wsID
2245
 
 
2246
 
    db.workshops_products.workshops_id.default = wsID
2247
 
    crud.messages.submit_button = T("Save")
2248
 
    crud.messages.record_created = T("Saved product")
2249
 
    crud.settings.create_next = next_url
2250
 
    crud.settings.create_onaccept = [cache_clear_workshops]
2251
 
    form = crud.create(db.workshops_products)
2252
 
 
2253
 
    form_id = "MainForm"
2254
 
    form_element = form.element('form')
2255
 
    form['_id'] = form_id
2256
 
 
2257
 
    elements = form.elements('input, select, textarea')
2258
 
    for element in elements:
2259
 
        element['_form'] = form_id
2260
 
 
2261
 
    submit = form.element('input[type=submit]')
2262
 
 
2263
 
    back = os_gui.get_button('back', return_url)
2264
 
    content = DIV(H4(T('New product')), BR(), form)
2265
 
 
2266
 
    menu = get_workshops_menu('products', wsID)
2267
 
 
2268
 
 
2269
 
    return dict(content=content, back=back, menu=menu, save=submit)
2270
 
 
2271
 
 
2272
 
@auth.requires_login()
2273
 
def product_edit():
2274
 
    '''
2275
 
        Edit product
2276
 
    '''
2277
 
    wspID = request.args[0]
2278
 
    wsID = db.workshops_products(wspID).workshops_id
2279
 
 
2280
 
    response.title = T('Event')
2281
 
    response.subtitle = get_subtitle(wsID)
2282
 
    response.view = 'general/tabs_menu.html'
2283
 
 
2284
 
    return_url = URL('products',
2285
 
                     vars={'wsID': wsID})
2286
 
 
2287
 
    crud.messages.submit_button = T("Save")
2288
 
    crud.messages.record_updated = T("Saved product")
2289
 
    crud.settings.update_next = return_url
2290
 
    crud.settings.update_onaccept = [cache_clear_workshops]
2291
 
    crud.settings.update_deletable = False
2292
 
    form = crud.update(db.workshops_products, wspID)
2293
 
 
2294
 
    form_id = "MainForm"
2295
 
    form_element = form.element('form')
2296
 
    form['_id'] = form_id
2297
 
 
2298
 
    elements = form.elements('input, select, textarea')
2299
 
    for element in elements:
2300
 
        element['_form'] = form_id
2301
 
 
2302
 
    submit = form.element('input[type=submit]')
2303
 
 
2304
 
    content = DIV(H4(T('Edit product')), BR(), form)
2305
 
 
2306
 
    menu = get_workshops_menu('products', wsID)
2307
 
 
2308
 
    back = os_gui.get_button('back', URL('products', vars={'wsID':wsID}))
2309
 
 
2310
 
    return dict(content=content, back=back, menu=menu, save=submit)
2311
 
 
2312
 
 
2313
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2314
 
               auth.has_permission('update', 'workshops_products'))
2315
 
def product_delete():
2316
 
    '''
2317
 
        Delete a selected product
2318
 
    '''
2319
 
    wsID = request.vars['wsID']
2320
 
    wspID = request.vars['wspID']
2321
 
 
2322
 
    if db.workshops_products(id=wspID).Deletable:
2323
 
        query = (db.workshops_products.id == wspID)
2324
 
        db(query).delete()
2325
 
 
2326
 
        # Clear cache
2327
 
        cache_clear_workshops()
2328
 
    else:
2329
 
        session.flash = T("Can't delete this product")
2330
 
 
2331
 
    redirect(URL('products', vars={'wsID': wsID}))
2332
 
 
2333
 
 
2334
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2335
 
               auth.has_permission('update', 'workshops_products'))
2336
 
def product_activities():
2337
 
    '''
2338
 
        Add or remove activities from a workshop
2339
 
    '''
2340
 
    wsID = request.vars['wsID']
2341
 
    wspID = request.vars['wspID']
2342
 
 
2343
 
    product = db.workshops_products(wspID)
2344
 
 
2345
 
    response.title = T('Event')
2346
 
    response.subtitle = get_subtitle(wsID)
2347
 
    response.view = 'general/tabs_menu.html'
2348
 
 
2349
 
    pquery = (db.workshops_products_activities.workshops_products_id == wspID)
2350
 
    field = db.workshops_products_activities.workshops_activities_id
2351
 
    selected = db(pquery).select(field)
2352
 
 
2353
 
    selected_ids = []
2354
 
    for row in selected:
2355
 
        selected_ids.append(row.workshops_activities_id)
2356
 
 
2357
 
    query = (db.workshops_activities.workshops_id == wsID)
2358
 
    rows = db(query).select(db.workshops_activities.ALL)
2359
 
 
2360
 
    form = FORM(_id="MainForm")
2361
 
    table = TABLE(THEAD(TR(TH(),
2362
 
                           TH(T('Date')),
2363
 
                           TH(T('Time')),
2364
 
                           TH(T('Activity')),
2365
 
                           TH(T('Location')))),
2366
 
                  _class='table table-hover')
2367
 
 
2368
 
    for row in rows.render():
2369
 
        if row.id in selected_ids:
2370
 
            checkbox = INPUT(_name=row.id,
2371
 
                             _value='on',
2372
 
                             _checked='checked',
2373
 
                             _type='checkbox')
2374
 
        else:
2375
 
            checkbox = INPUT(_name=row.id,
2376
 
                             _value='on',
2377
 
                             _type='checkbox')
2378
 
 
2379
 
        internal_location = not row.school_locations_id is None and \
2380
 
                            not row.school_locations_id == ''
2381
 
        if internal_location:
2382
 
            location = row.school_locations_id
2383
 
        else:
2384
 
            location = row.LocationExternal
2385
 
 
2386
 
        table.append(TR(TD(checkbox),
2387
 
                        TD(row.Activitydate),
2388
 
                        TD(row.Starttime + ' - ' + row.Endtime),
2389
 
                        TD(row.Activity),
2390
 
                        TD(location),
2391
 
                        ))
2392
 
 
2393
 
    form.append(table)
2394
 
 
2395
 
    content = DIV(DIV(H4(T('Activities included in'), ' ', product.Name), form, _class='col-md-8 clear'),
2396
 
                  _class='row')
2397
 
 
2398
 
    if form.process().accepted:
2399
 
        db(pquery).delete()
2400
 
        for activity_id in form.vars:
2401
 
            if form.vars[activity_id] == 'on':
2402
 
                db.workshops_products_activities.insert(
2403
 
                    workshops_activities_id=activity_id,
2404
 
                    workshops_products_id=wspID)
2405
 
        session.flash = T('Saved')
2406
 
        redirect(URL('products', vars={'wsID': wsID}))
2407
 
 
2408
 
    menu = get_workshops_menu('products', wsID)
2409
 
 
2410
 
    back = os_gui.get_button('back', URL('products', vars={'wsID':wsID}))
2411
 
 
2412
 
    return dict(content=content,
2413
 
                menu=menu,
2414
 
                save=os_gui.get_submit_button('MainForm'),
2415
 
                back=back)
2416
 
 
2417
 
 
2418
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2419
 
               auth.has_permission('update', 'workshops_products'))
2420
 
def product_sell():
2421
 
    '''
2422
 
        Page to select which customer to sell a product to
2423
 
    '''
2424
 
    wsID = request.vars['wsID']
2425
 
    wspID = request.vars['wspID']
2426
 
 
2427
 
    wsp = WorkshopProduct(wspID)
2428
 
 
2429
 
    response.title = T('Manage')
2430
 
    subtitle = SPAN(get_subtitle(wsID), ' > ',
2431
 
                    T('Customers'), ': ', wsp.name, ' > ',
2432
 
                    T('Sell product'))
2433
 
    response.subtitle = subtitle
2434
 
 
2435
 
    # reset session variable for search
2436
 
    session.customers_load_list_search_name = None
2437
 
 
2438
 
    _class = 'clear' + ' '
2439
 
    _class += 'load_list_customers' + ' '
2440
 
 
2441
 
    back = os_gui.get_button('back_bs',
2442
 
                             URL('products_list_customers', vars={'wsID': wsID,
2443
 
                                                                  'wspID': wspID}),
2444
 
                             _class='left',
2445
 
                             btn_size='btn')
2446
 
 
2447
 
    search_results = DIV(
2448
 
        LOAD('customers', 'load_list.load',
2449
 
             content=os_gui.get_ajax_loader(message=T("Loading...")),
2450
 
             target='product_sell_customers_list',
2451
 
             vars={'list_type': 'workshops_product_sell',
2452
 
                   'items_per_page': 10,
2453
 
                   'wspID': wspID,
2454
 
                   'wsID': wsID},
2455
 
             ajax=True),
2456
 
        _class=_class,
2457
 
        _id='product_sell_customers_list')
2458
 
 
2459
 
    loader = os_gui.get_ajax_loader(message=T("Searching..."))
2460
 
 
2461
 
    return dict(content=search_results, loader=loader)
2462
 
 
2463
 
 
2464
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2465
 
               auth.has_permission('update', 'workshops_products_customers'))
2466
 
def product_sell_to_customer():
2467
 
    '''
2468
 
        Sell a product to a customer
2469
 
    '''
2470
 
    cuID = request.vars['cuID']
2471
 
    wsID = request.vars['wsID']
2472
 
    wspID = request.vars['wspID']
2473
 
 
2474
 
    waitinglist = False
2475
 
    if 'waiting' in request.vars:
2476
 
        waitinglist = True
2477
 
    # register customer
2478
 
 
2479
 
    session.flash = T("Saved")
2480
 
 
2481
 
    wsp = WorkshopProduct(wspID)
2482
 
    wsp.sell_to_customer(cuID, waitinglist=waitinglist)
2483
 
 
2484
 
    next_url = product_sell_get_return_url(cuID, wsID, wspID)
2485
 
    if session.workshops_product_sell_back == 'customers':
2486
 
        redirect(next_url, client_side=True)
2487
 
    else:
2488
 
        redirect(next_url)
2489
 
 
2490
 
 
2491
 
def product_sell_get_return_url(cuID, wsID, wspID, remove=False):
2492
 
    '''
2493
 
        Return URL for adding a customer to a product or removing a customer
2494
 
        from a product
2495
 
    '''
2496
 
    if session.workshops_product_sell_back == 'customers':
2497
 
        url = URL('customers', 'workshops', vars={'cuID': cuID})
2498
 
    else:
2499
 
        if remove:
2500
 
            url = URL('products_list_customers', vars={'wsID': wsID,
2501
 
                                                       'wspID': wspID})
2502
 
        else:
2503
 
            url = URL('product_sell', vars={'wsID': wsID,
2504
 
                                            'wspID': wspID})
2505
 
 
2506
 
    return url
2507
 
 
2508
 
 
2509
 
# TODO: move to os_workshops.WorkshopProduct so it can also be used in the shop or os_workshops.WorkshopHelper
2510
 
def product_check_sold_out(wsID, wspID):
2511
 
    '''
2512
 
        This function checks if a product is sold out
2513
 
        It's sold out when any of the activities it contains is completely full
2514
 
    '''
2515
 
    check = False
2516
 
 
2517
 
    fwsID = workshops_get_full_workshop_product_id(wsID)
2518
 
    if int(wspID) == fwsID:
2519
 
        # Full workshops check, check if any activity is full
2520
 
        query = (db.workshops_activities.workshops_id == wsID)
2521
 
        rows = db(query).select(db.workshops_activities.ALL)
2522
 
        for row in rows:
2523
 
            reserved = activity_count_reservations(row.id, fwsID)
2524
 
            if reserved >= row.Spaces:
2525
 
                check = True
2526
 
                break
2527
 
    else:
2528
 
        # Product check, check if any a activity is full
2529
 
        query = (db.workshops_products_activities.workshops_products_id ==
2530
 
                 wspID)
2531
 
        rows = db(query).select(db.workshops_products_activities.ALL)
2532
 
        for row in rows:
2533
 
            activity = db.workshops_activities(row.workshops_activities_id)
2534
 
            reserved = activity_count_reservations(activity.id, fwsID)
2535
 
            if reserved >= activity.Spaces:
2536
 
                check = True
2537
 
                break
2538
 
 
2539
 
    return check
2540
 
 
2541
 
 
2542
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2543
 
               auth.has_permission('update', 'workshops_products_customers'))
2544
 
def product_delete_customer():
2545
 
    '''
2546
 
        Remove a customer from the sold products list
2547
 
    '''
2548
 
    wsID = request.vars['wsID']
2549
 
    wsp_cuID = request.vars['wsp_cuID']
2550
 
 
2551
 
    # Cancel invoice (if any)
2552
 
    row = db.invoices_workshops_products_customers(workshops_products_customers_id = wsp_cuID)
2553
 
    iID = row.invoices_id
2554
 
    if iID:
2555
 
        invoice = Invoice(iID)
2556
 
        invoice.set_status('cancelled')
2557
 
 
2558
 
    # get database record for workshop_customers
2559
 
    row = db.workshops_products_customers(
2560
 
        db.workshops_products_customers.id == wsp_cuID)
2561
 
 
2562
 
    cuID = row.auth_customer_id
2563
 
    wspID = row.workshops_products_id
2564
 
 
2565
 
    # remove sold product entry for customer
2566
 
    query = (db.workshops_products_customers.id == wsp_cuID)
2567
 
    db(query).delete()
2568
 
 
2569
 
    # remove any payments connected to workshop activity
2570
 
    query = (db.customers_payments.auth_customer_id == cuID) & \
2571
 
            (db.customers_payments.workshops_products_id == wspID)
2572
 
    db(query).delete()
2573
 
 
2574
 
    session.flash = T("Removed")
2575
 
 
2576
 
    next_url = product_sell_get_return_url(cuID, wsID, wspID, True)
2577
 
    redirect(next_url)
2578
 
 
2579
 
 
2580
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2581
 
               auth.has_permission('update', 'workshops_products_customers'))
2582
 
def product_cancel_customer():
2583
 
    '''
2584
 
        Change status to cancelled or from cancelled to not cancelled
2585
 
    '''
2586
 
    wsID = request.vars['wsID']
2587
 
    wsp_cuID = request.vars['wsp_cuID']
2588
 
 
2589
 
    # Cancel invoice (if any)
2590
 
    row = db.invoices_workshops_products_customers(workshops_products_customers_id = wsp_cuID)
2591
 
    iID = row.invoices_id
2592
 
    if iID:
2593
 
        invoice = Invoice(iID)
2594
 
        invoice.set_status('cancelled')
2595
 
 
2596
 
    # get database record for workshop_customers
2597
 
    row = db.workshops_products_customers(
2598
 
        db.workshops_products_customers.id == wsp_cuID)
2599
 
 
2600
 
    cuID = row.auth_customer_id
2601
 
    wspID = row.workshops_products_id
2602
 
 
2603
 
    row.Cancelled = not row.Cancelled
2604
 
    row.update_record()
2605
 
 
2606
 
    # update invoice status to cancelled if status == sent
2607
 
    query = (db.invoices_workshops_products_customers.workshops_products_customers_id == wsp_cuID)
2608
 
    rows = db(query).select(db.invoices_workshops_products_customers.ALL)
2609
 
    if len(rows):
2610
 
        row = rows.first()
2611
 
        iID = row.invoices_id
2612
 
 
2613
 
        invoice = Invoice(iID)
2614
 
        if invoice.invoice.Status == 'sent':
2615
 
            invoice.set_status('cancelled')
2616
 
 
2617
 
    session.flash = T("Saved")
2618
 
 
2619
 
    next_url = product_sell_get_return_url(cuID, wsID, wspID, True)
2620
 
    redirect(next_url)
2621
 
 
2622
 
 
2623
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2624
 
               auth.has_permission('update', 'workshops_products_customers'))
2625
 
def product_remove_customer_from_waitinglist():
2626
 
    '''
2627
 
        Removes a customer from the waitinglist and add customer to list
2628
 
    '''
2629
 
    wsp_cuID = request.vars['wsp_cuID']
2630
 
    wsID = request.vars['wsID']
2631
 
 
2632
 
    row = db.workshops_products_customers(wsp_cuID)
2633
 
    wspID = row.workshops_products_id
2634
 
    cuID = row.auth_customer_id
2635
 
 
2636
 
    query = (db.workshops_products_customers.id == wsp_cuID)
2637
 
    db(query).delete()
2638
 
 
2639
 
    wsp = WorkshopProduct(wspID)
2640
 
    wsp.sell_to_customer(cuID)
2641
 
 
2642
 
    redirect(URL('products_list_customers', vars={'wsID': wsID,
2643
 
                                                  'wspID': wspID}))
2644
 
 
2645
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2646
 
               auth.has_permission('update', 'workshops_products_customers'))
2647
 
def product_resend_info_mail():
2648
 
    '''
2649
 
        Resend info mail for customer
2650
 
    '''
2651
 
    wspcID = request.vars['wspcID']
2652
 
    wspc = db.workshops_products_customers(wspcID)
2653
 
    cuID = wspc.auth_customer_id
2654
 
    customer = Customer(cuID)
2655
 
    ##
2656
 
    # Send mail
2657
 
    ##
2658
 
    osmail = OsMail()
2659
 
    msgID = osmail.render_email_template('workshops_info_mail', workshops_products_customers_id=wspcID)
2660
 
    sent = osmail.send(msgID, cuID)
2661
 
 
2662
 
    ##
2663
 
    # Check the "Event info" checkbox
2664
 
    ##
2665
 
    if sent:
2666
 
        wspc.WorkshopInfo = True
2667
 
        wspc.update_record()
2668
 
        msg = T('Sent event info mail to ')
2669
 
    else:
2670
 
        msg = T('Unable to send event info mail to ')
2671
 
 
2672
 
    ##
2673
 
    # Notify user
2674
 
    ##
2675
 
    session.flash = msg + customer.row.display_name
2676
 
 
2677
 
    wsp = db.workshops_products(wspc.workshops_products_id)
2678
 
 
2679
 
 
2680
 
    if session.workshops_product_resend_info_mail == 'customers_workshops':
2681
 
        redirect(URL('customers', 'workshops', vars={'cuID':cuID}))
2682
 
    else:
2683
 
        redirect(URL('workshops', 'products_list_customers', vars={'wsID':wsp.workshops_id,
2684
 
                                                                   'wspID':wsp.id}))
2685
 
 
2686
 
 
2687
 
def get_fullws_label(value=None):
2688
 
    '''
2689
 
        Returns label for full Workshop
2690
 
    '''
2691
 
    return get_label('primary', T("Full event"))
2692
 
 
2693
 
 
2694
 
def get_cancelled_label(value=None):
2695
 
    '''
2696
 
        Returns label for cancelled product
2697
 
    '''
2698
 
    return SPAN(T("Cancelled"), _class='label label-warning')
2699
 
 
2700
 
 
2701
 
@auth.requires(auth.has_membership(group_id='Admins') or \
2702
 
               auth.has_permission('update', 'class_status'))
2703
 
def overlapping_classes():
2704
 
    '''
2705
 
        Shows a page of overlapping classes for a given workshop
2706
 
    '''
2707
 
    wsID = request.vars['wsID']
2708
 
    workshop = db.workshops(wsID)
2709
 
    response.title = T('Overlapping classes')
2710
 
    try:
2711
 
        response.subtitle = workshop.Name + ' ' + \
2712
 
                            workshop.Startdate.strftime(DATE_FORMAT)
2713
 
    except AttributeError:
2714
 
        response.subtitle = workshop.Name
2715
 
 
2716
 
    session.classes_status_set_cancelled_back = 'workshops_oc'
2717
 
 
2718
 
    return_url = URL('activities', vars={'wsID': wsID})
2719
 
 
2720
 
    content = []
2721
 
    query = (db.workshops_activities.workshops_id == wsID)
2722
 
    rows = db(query).select(db.workshops_activities.ALL,
2723
 
                            orderby=db.workshops_activities.Activitydate)
2724
 
    for row in rows:
2725
 
        content.append(activity_get_overlapping_classes(row.id))
2726
 
 
2727
 
    cancel_oc = A(T('Cancel all overlapping classes'),
2728
 
                  _href=URL('overlapping_classes_cancel_all',
2729
 
                            vars={'wsID': wsID}),
2730
 
                  _class='btn btn-default btn-sm right')
2731
 
    back = os_gui.get_button('back', return_url)
2732
 
    back = DIV(back, cancel_oc, _class='pull-right')
2733
 
 
2734
 
    return dict(content=content,
2735
 
                back=back)
2736
 
 
2737
 
 
2738
 
def activity_get_overlapping_classes(wsaID):
2739
 
    '''
2740
 
        Check for overlapping classes
2741
 
    '''
2742
 
    activity = db.workshops_activities(wsaID)
2743
 
 
2744
 
    loc_check = not activity.school_locations_id is None or \
2745
 
                not activity.school_locations_id == ''
2746
 
    if loc_check:
2747
 
        classes = TABLE(_class='table table-hover')
2748
 
        classes.append(THEAD(TR(TH(),
2749
 
                                TH(T('Location')),
2750
 
                                TH(T('Class type')),
2751
 
                                TH(T('Class date')),
2752
 
                                TH(T('Start')),
2753
 
                                TH(T('End')),
2754
 
                                TH(T('Teacher')),
2755
 
                                TH(),
2756
 
                                _class='os-table_header')))
2757
 
        activity_weekday = get_weekday(activity.Activitydate)
2758
 
 
2759
 
        query = overlapping_classes_get_query(activity_weekday,
2760
 
                                              activity.school_locations_id,
2761
 
                                              activity.Activitydate,
2762
 
                                              activity.Starttime,
2763
 
                                              activity.Endtime)
2764
 
 
2765
 
        rows = db(query).select(db.classes.ALL)
2766
 
        for row in rows.render():
2767
 
            clsID = row.id
2768
 
            wsID = activity.workshops_id
2769
 
            result = classes_get_status(row.id, activity.Activitydate)
2770
 
            status = result['status_marker']
2771
 
            buttons = activity_get_overlapping_classes_buttons(
2772
 
                wsID,
2773
 
                clsID,
2774
 
                activity.Activitydate,
2775
 
                result['status'])
2776
 
 
2777
 
            location = max_string_length(row.school_locations_id, 15)
2778
 
            classtype = max_string_length(row.school_classtypes_id, 24)
2779
 
            start = row.Starttime
2780
 
            end = row.Endtime
2781
 
            teachers = class_get_teachers(clsID,
2782
 
                                          activity.Activitydate)
2783
 
 
2784
 
            teacher = teachers['teacher']
2785
 
 
2786
 
            tr = TR(TD(status, _class='td_status_marker'),
2787
 
                    TD(location, _class='location'),
2788
 
                    TD(classtype, _class='classtype'),
2789
 
                    TD(activity.Activitydate, _class='class_date'),
2790
 
                    TD(start, _class='class_time'),
2791
 
                    TD(end, _class='class_time'),
2792
 
                    TD(teacher, _class='class_teacher'),
2793
 
                    TD(buttons, _class='show_buttons'))
2794
 
            classes.append(tr)
2795
 
 
2796
 
        if len(classes) == 1:  # only header, so no content
2797
 
            classes = ''
2798
 
 
2799
 
    else:
2800
 
        classes = T("No overlapping classes found.")
2801
 
 
2802
 
    return dict(activity=activity,
2803
 
                classes=classes)
2804
 
 
2805
 
 
2806
 
def overlapping_classes_set_status_cancelled_execute(clsID, date):
2807
 
    '''
2808
 
        Actually cancels the class
2809
 
    '''
2810
 
    cotc = db.classes_otc(classes_id=clsID, ClassDate=date)
2811
 
    if not cotc:
2812
 
        db.classes_otc.insert(
2813
 
            classes_id=clsID,
2814
 
            ClassDate=date,
2815
 
            Status='cancelled'
2816
 
        )
2817
 
    else:
2818
 
        cotc.Status = 'cancelled'
2819
 
        cotc.update_record()
2820
 
 
2821
 
 
2822
 
def overlapping_classes_set_status_cancelled():
2823
 
    '''
2824
 
        Cancels an overlapping class
2825
 
    '''
2826
 
    wsID = request.vars['wsID']
2827
 
    clsID = request.vars['clsID']
2828
 
    date_formatted = request.vars['date']
2829
 
    date = datestr_to_python(DATE_FORMAT, date_formatted)
2830
 
 
2831
 
    overlapping_classes_set_status_cancelled_execute(clsID, date)
2832
 
 
2833
 
    redirect(URL('overlapping_classes', vars={'wsID': wsID}))
2834
 
 
2835
 
 
2836
 
def overlapping_classes_set_status_normal():
2837
 
    '''
2838
 
        Sets status to normal
2839
 
    '''
2840
 
    wsID = request.vars['wsID']
2841
 
    clsID = request.vars['clsID']
2842
 
    date_formatted = request.vars['date']
2843
 
    date = datestr_to_python(DATE_FORMAT, date_formatted)
2844
 
 
2845
 
    cotc = db.classes_otc(classes_id=clsID, ClassDate=date)
2846
 
    if cotc:  # No need to do anything when there are no changes
2847
 
        cotc.Status = 'normal'
2848
 
        cotc.update_record()
2849
 
 
2850
 
        '''
2851
 
         if the status is normal:
2852
 
            check if there are any other changes
2853
 
            otherwise just delete. '''
2854
 
        change = False
2855
 
        fields = [
2856
 
            cotc.school_locations_id,
2857
 
            cotc.school_classtypes_id,
2858
 
            cotc.Starttime,
2859
 
            cotc.Endtime,
2860
 
            cotc.auth_teacher_id,
2861
 
            cotc.auth_teacher_id2,
2862
 
            cotc.Description
2863
 
        ]
2864
 
        for field in fields:
2865
 
            if not field is None or field == '':
2866
 
                change = True
2867
 
 
2868
 
        if not change:
2869
 
            query = (db.classes_otc.id == cotc.id)
2870
 
            result = db(query).delete()
2871
 
 
2872
 
    redirect(URL('overlapping_classes', vars={'wsID': wsID}))
2873
 
 
2874
 
 
2875
 
def overlapping_classes_cancel_all():
2876
 
    '''
2877
 
        Function called which sets all overlapping classes to cancelled
2878
 
    '''
2879
 
    wsID = request.vars['wsID']
2880
 
    # Get all activities for a workshop
2881
 
    query = (db.workshops_activities.workshops_id == wsID)
2882
 
    rows = db(query).select(db.workshops_activities.ALL)
2883
 
    for row in rows:
2884
 
        # Get overlapping classes for activity
2885
 
        date = row.Activitydate
2886
 
        weekday = get_weekday(date)
2887
 
        oquery = overlapping_classes_get_query(weekday,
2888
 
                                               row.school_locations_id,
2889
 
                                               date,
2890
 
                                               row.Starttime,
2891
 
                                               row.Endtime)
2892
 
        crows = db(oquery).select(db.classes.ALL)
2893
 
        for crow in crows:
2894
 
            clsID = crow.id
2895
 
            overlapping_classes_set_status_cancelled_execute(clsID, date)
2896
 
 
2897
 
    redirect(URL('overlapping_classes', vars={'wsID': wsID}))
2898
 
 
2899
 
 
2900
 
def activity_get_overlapping_classes_buttons(wsID, clsID, class_date, status):
2901
 
    '''
2902
 
        Returns (un)cancel buttons for a class
2903
 
    '''
2904
 
    btn_group = DIV(_class='btn-group pull-right')
2905
 
    class_date = class_date.strftime(DATE_FORMAT)
2906
 
    if status == 'cancelled':
2907
 
        btn = A(SPAN(_class='glyphicon glyphicon-ok'),
2908
 
                _href=URL('overlapping_classes_set_status_normal',
2909
 
                          vars={'clsID': clsID,
2910
 
                                'date': class_date,
2911
 
                                'wsID': wsID}),
2912
 
                _title=T('Set status Normal'),
2913
 
                _class='btn btn-default btn-sm green')
2914
 
    else:
2915
 
        btn = A(SPAN(_class='glyphicon glyphicon-ban-circle'),
2916
 
                _href=URL('overlapping_classes_set_status_cancelled',
2917
 
                          vars={'clsID': clsID,
2918
 
                                'date': class_date,
2919
 
                                'wsID': wsID}),
2920
 
                _title=T('Set status Cancelled'),
2921
 
                _class='btn btn-default btn-sm red')
2922
 
 
2923
 
    btn_group.append(btn)
2924
 
 
2925
 
    return btn_group
2926
 
 
2927
 
 
2928
 
def overlapping_classes_get_query(activity_weekday,
2929
 
                                  activity_locations_id,
2930
 
                                  activity_date,
2931
 
                                  starttime,
2932
 
                                  endtime):
2933
 
    '''
2934
 
        Returns query to check for overlapping classes
2935
 
    '''
2936
 
    return (db.classes.school_locations_id == activity_locations_id) & \
2937
 
           (db.classes.Startdate <= activity_date) & \
2938
 
           ((db.classes.Enddate >= activity_date) |
2939
 
            (db.classes.Enddate == None)) & \
2940
 
           (db.classes.Week_day == activity_weekday) & \
2941
 
           (((db.classes.Starttime <= starttime) &
2942
 
             (db.classes.Endtime <= endtime) &
2943
 
             (db.classes.Endtime >= endtime)) |
2944
 
            ((db.classes.Starttime >= starttime) &
2945
 
             (db.classes.Starttime <= endtime) &
2946
 
             (db.classes.Endtime <= endtime) &
2947
 
             (db.classes.Endtime >= starttime)) |
2948
 
            ((db.classes.Starttime >= starttime) &
2949
 
             (db.classes.Starttime <= endtime) &
2950
 
             (db.classes.Endtime >= endtime)) |
2951
 
            ((db.classes.Starttime <= starttime) &
2952
 
             (db.classes.Endtime >= endtime))
2953
 
            )
2954
 
 
2955
 
 
2956
 
def overlapping_classes_get_count(wsaID):
2957
 
    '''
2958
 
        Returns count for overlapping classes
2959
 
    '''
2960
 
    activity = db.workshops_activities(wsaID)
2961
 
    activity_weekday = get_weekday(activity.Activitydate)
2962
 
    query = overlapping_classes_get_query(activity_weekday,
2963
 
                                          activity.school_locations_id,
2964
 
                                          activity.Activitydate,
2965
 
                                          activity.Starttime,
2966
 
                                          activity.Endtime)
2967
 
    return db(query).count()
2968
 
 
2969
 
 
2970
 
def overlapping_classes_get_count_all(wsID):
2971
 
    '''
2972
 
        Returns count of all overlapping classes for a workshop
2973
 
    '''
2974
 
    count = 0
2975
 
    query = (db.workshops_activities.workshops_id == wsID)
2976
 
    rows = db(query).select(db.workshops_activities.ALL)
2977
 
    for row in rows:
2978
 
        count += overlapping_classes_get_count(row.id)
2979
 
 
2980
 
    return count
2981
 
 
2982
 
 
2983
 
# @auth.requires(auth.has_membership(group_id='Admins') or \
2984
 
#                auth.has_permission('update', 'workshops_mail_customers'))
2985
 
# def mail_customers():
2986
 
#     '''
2987
 
#         Send an email to all customers for a workshop
2988
 
#         Checks db.workshops_products_customers to see who should get the mail
2989
 
#     '''
2990
 
#     # Fire up TinyMCE
2991
 
#     response.js = 'tinymce_init_default();'
2992
 
#
2993
 
#     wsID = request.vars['wsID']
2994
 
#
2995
 
#     form = mail_customers_get_form()
2996
 
#
2997
 
#     form_msg = ''
2998
 
#     form_info = ''
2999
 
#     if form.process().accepted:
3000
 
#         mail_customers_send(wsID,
3001
 
#                             form.vars.subject,
3002
 
#                             '<html><body>' + XML(form.vars.message) + '</body></html>')
3003
 
#
3004
 
#         response.flash = T("Message sent")
3005
 
#         redirect(URL('messages', vars={'wsID': wsID}, extension=False),
3006
 
#                  client_side=True)
3007
 
#
3008
 
#     elif form.errors:
3009
 
#         msg = SPAN(B(T('Oh snap!')), ' ',
3010
 
#                    T("Change a few things up and try sending again..."))
3011
 
#         form_msg = os_gui.get_alert('danger', msg)
3012
 
#
3013
 
#     response.flash = None
3014
 
#
3015
 
#     content = DIV(
3016
 
#         form.custom.begin,
3017
 
#         form.custom.widget.subject,
3018
 
#         form.custom.widget.message,
3019
 
#         form.custom.submit,
3020
 
#         form.custom.end
3021
 
#     )
3022
 
#
3023
 
#     return dict(content=content)
3024
 
 
3025
 
 
3026
 
# def mail_customers_send(wsID, subject, message, msgID=None):
3027
 
#     '''
3028
 
#         send mail to customers of a workshop
3029
 
#     '''
3030
 
#     # Save message to database
3031
 
#     if msgID is None:
3032
 
#         msgID = db.messages.insert(msg_subject=subject,
3033
 
#                                    msg_content=message)
3034
 
#         db.workshops_messages.insert(workshops_id=wsID,
3035
 
#                                      messages_id=msgID)
3036
 
#
3037
 
#     # To send, first get list of all customers with email for a workshop
3038
 
#     wh = WorkshopsHelper()
3039
 
#     customers_rows = wh.get_all_workshop_customers(wsID)
3040
 
#
3041
 
#     osmail = OsMail()
3042
 
#     for row in customers_rows:
3043
 
#         osmail.send(msgID, row.auth_user.id)
3044
 
#
3045
 
#     # Get customers for those products
3046
 
#
3047
 
#
3048
 
# def mail_customers_get_form(value=None):
3049
 
#     '''
3050
 
#         Returns a form to mail customers
3051
 
#     '''
3052
 
#     signature = '<br><br>'
3053
 
#     if db.sys_properties(Property='smtp_signature'):
3054
 
#         signature += db.sys_properties(Property='smtp_signature').PropertyValue
3055
 
#
3056
 
#     form = SQLFORM.factory(
3057
 
#         Field('subject',
3058
 
#               requires=IS_NOT_EMPTY()),
3059
 
#         Field('message', 'text',
3060
 
#               default=signature,
3061
 
#               requires=IS_NOT_EMPTY()),
3062
 
#         submit_button=T("Send"))
3063
 
#
3064
 
#     confirm_send_msg = T("Are you sure you want to send this message?")
3065
 
#     submit_onclick = "return confirm('" + confirm_send_msg + "');"
3066
 
#
3067
 
#     form.element('#no_table_subject').attributes['_placeholder'] = T("Subject...")
3068
 
#     form.element('#no_table_message').attributes['_placeholder'] = T("Message...")
3069
 
#     form.element('input[type=submit]').attributes['_onclick'] = submit_onclick
3070
 
#
3071
 
#     # add class for BS3
3072
 
#     form.custom.widget.subject['_class'] += ' form-control'
3073
 
#     # form.custom.widget.message['_class'] += ' form-control'
3074
 
#     form.custom.widget.message['_class'] += ' form-control tmced'
3075
 
#     form.custom.submit['_class'] = ' btn-primary'
3076
 
#
3077
 
#     return form
3078
 
#
3079
 
#
3080
 
# @auth.requires(auth.has_membership(group_id='Admins') or \
3081
 
#                auth.has_permission('read', 'workshops_messages'))
3082
 
# def messages():
3083
 
#     '''
3084
 
#         List messages sent to customers for a workshop.
3085
 
#     '''
3086
 
#     wsID = request.vars['wsID']
3087
 
#     response.title = T('Event')
3088
 
#     response.subtitle = get_subtitle(wsID)
3089
 
#     session.workshops_msgID = None
3090
 
#     # response.view = 'workshops/manage.html'
3091
 
#
3092
 
#     ## Modals container
3093
 
#     modals = DIV()
3094
 
#
3095
 
#     ## Mail button & modal begin ##
3096
 
#     result = messages_get_mail(wsID)
3097
 
#     btn_mail = result['button']
3098
 
#     modals.append(result['modal'])
3099
 
#
3100
 
#     ## Mail button & modal end ##
3101
 
#
3102
 
#     ## Message list begin ##
3103
 
#     content_msg = DIV(_class='container_messages')
3104
 
#
3105
 
#     h = html2text.HTML2Text()
3106
 
#     h.ignore_links = True
3107
 
#     h.images_to_alt = True
3108
 
#
3109
 
#     messages = UL(_class='ul_liststyle_none list_messages col-md-3')
3110
 
#     left = [db.messages.on(db.workshops_messages.messages_id == db.messages.id)]
3111
 
#     query = (db.workshops_messages.workshops_id == wsID)
3112
 
#     rows = db(query).select(db.messages.ALL,
3113
 
#                             db.workshops_messages.ALL,
3114
 
#                             left=left,
3115
 
#                             orderby=~db.workshops_messages.Created_at)
3116
 
#     for i, row in enumerate(rows):
3117
 
#         li_class = 'os-clickable'
3118
 
#         if i == 0:
3119
 
#             session.workshops_msgID = row.messages.id
3120
 
#             li_class += ' active'
3121
 
#
3122
 
#         created_at = row.workshops_messages.Created_at
3123
 
#         created_at = created_at.strftime(DATE_FORMAT)
3124
 
#         msg_preview = h.handle(row.messages.msg_content)
3125
 
#         msg_preview = msg_preview.replace('*', '')
3126
 
#         msg_preview = msg_preview.replace('_', '')
3127
 
#         msg_preview = SPAN(XML(msg_preview[0:60]),
3128
 
#                            _class='vsmall_font grey')
3129
 
#
3130
 
#         subject = max_string_length(row.messages.msg_subject, 25)
3131
 
#
3132
 
#         messages.append(LI(B(subject), ' ',
3133
 
#                            SPAN(created_at,
3134
 
#                                 _class='right vsmall_font grey'),
3135
 
#                            BR(),
3136
 
#                            msg_preview,
3137
 
#                            _class=li_class,
3138
 
#                            _id=row.messages.id))
3139
 
#
3140
 
#     content_msg.append(messages)
3141
 
#
3142
 
#     ## Message list end ##
3143
 
#
3144
 
#     ## Message display begin ##
3145
 
#     message_display = LOAD('messages', 'message.load',
3146
 
#                            ajax=True,
3147
 
#                            target='message_display',
3148
 
#                            content=os_gui.get_ajax_loader(),
3149
 
#                            vars={'category': 'workshops',
3150
 
#                                  'wsID': wsID})
3151
 
#
3152
 
#     content_msg.append(DIV(message_display,
3153
 
#                            _class='col-md-9 message_display'))
3154
 
#
3155
 
#     ## Message display end ##
3156
 
#
3157
 
#     content = DIV(DIV(content_msg, modals, _class='col-md-12'),
3158
 
#                   _class='row')
3159
 
#
3160
 
#     menu = get_workshops_menu(request.function, wsID)
3161
 
#     back = manage_get_back()
3162
 
#
3163
 
#     return dict(back=back,
3164
 
#                 menu=menu,
3165
 
#                 content=content,
3166
 
#                 btn_mail=DIV(btn_mail, _class='pull-right'),
3167
 
#                 left_sidebar_enabled=True)
3168
 
#
3169
 
#
3170
 
# def messages_get_mail(wsID):
3171
 
#     '''
3172
 
#         Returns mail button and modal for all workshop customers
3173
 
#     '''
3174
 
#     workshop = db.workshops(wsID)
3175
 
#     modal_title = T("Mail all customers: ") + workshop.Name
3176
 
#     modal_content = LOAD('workshops', 'mail_customers.load',
3177
 
#                          ajax=True,
3178
 
#                          vars={'wsID': wsID})
3179
 
#
3180
 
#     btn_icon = SPAN(SPAN(_class='glyphicon glyphicon-envelope'), ' ',
3181
 
#                     T('Send message'))
3182
 
#
3183
 
#     result = os_gui.get_modal(button_text=XML(btn_icon),
3184
 
#                               modal_title=modal_title,
3185
 
#                               modal_content=modal_content,
3186
 
#                               modal_class='mail_' + unicode(wsID),
3187
 
#                               modal_size='lg',
3188
 
#                               button_class='btn-sm')
3189
 
#
3190
 
#     return result
3191
 
#
3192
 
#
3193
 
# @auth.requires(auth.has_membership(group_id='Admins') or \
3194
 
#                auth.has_permission('read', 'workshops_messages'))
3195
 
# def messages_set_id():
3196
 
#     '''
3197
 
#         Set session ID used to show message in main window of messages pane
3198
 
#         This function should be called as JSON
3199
 
#     '''
3200
 
#     if request.extension == 'json':
3201
 
#         session.workshops_msgID = request.vars['msgID']
3202
 
#         status = 'success'
3203
 
#         message = T('OK')
3204
 
#     else:
3205
 
#         status = 'fail'
3206
 
#         message = T('Please call this function as "json"')
3207
 
#
3208
 
#     return dict(status=status,
3209
 
#                 message=message)
3210
 
 
3211
 
 
3212
 
@auth.requires(auth.has_membership(group_id='Admins') or \
3213
 
               auth.has_permission('read', 'tasks'))
3214
 
def tasks():
3215
 
    '''
3216
 
        Display list of tasks for a workshop
3217
 
    '''
3218
 
    wsID = request.vars['wsID']
3219
 
    # now continue settings things as usual
3220
 
    workshop = db.workshops(wsID)
3221
 
    response.title = T('Event')
3222
 
    response.subtitle = get_subtitle(wsID)
3223
 
    response.view = 'general/tabs_menu.html'
3224
 
 
3225
 
    tasks = DIV(LOAD('tasks', 'list_tasks.load',
3226
 
                     vars=request.vars,
3227
 
                     content=os_gui.get_ajax_loader()))
3228
 
 
3229
 
    content = tasks
3230
 
 
3231
 
    # Add permission
3232
 
    add = ''
3233
 
    permission = auth.has_membership(group_id='Admins') or \
3234
 
                 auth.has_permission('create', 'tasks')
3235
 
    if permission:
3236
 
        # add = os_gui.get_button('add', url_add)
3237
 
        th = TasksHelper()
3238
 
        add = th.add_get_modal({'wsID': wsID})
3239
 
 
3240
 
    back = manage_get_back()
3241
 
    menu = get_workshops_menu(request.function, wsID)
3242
 
 
3243
 
    return dict(content=content,
3244
 
                menu=menu,
3245
 
                back=back,
3246
 
                add=add)
3247
 
 
3248
 
 
3249
 
# No decorator here, permissions are checked inside the function
3250
 
def pdf():
3251
 
    '''
3252
 
        Converts a invoice to PDF
3253
 
    '''
3254
 
    import weasyprint
3255
 
 
3256
 
    wsID = request.vars['wsID']
3257
 
    workshop = Workshop(wsID)
3258
 
 
3259
 
    permission = (auth.has_membership(group_id='Admins') or
3260
 
                  auth.has_permission('read', 'workshops'))
3261
 
 
3262
 
    if not permission:
3263
 
        return T("Not authorized")
3264
 
 
3265
 
    html = pdf_template(wsID)
3266
 
 
3267
 
    fname = workshop.Startdate.strftime(DATE_FORMAT) + u'_' + workshop.Name + u'.pdf'
3268
 
    response.headers['Content-Type'] = 'application/pdf'
3269
 
    response.headers['Content-disposition'] = 'attachment; filename=' + fname
3270
 
    # return pyfpdf_from_html(html)
3271
 
 
3272
 
    stream = cStringIO.StringIO()
3273
 
    workshop = weasyprint.HTML(string=html).write_pdf(stream)
3274
 
 
3275
 
    return stream.getvalue()
3276
 
 
3277
 
 
3278
 
@auth.requires(auth.has_membership(group_id='Admins') or \
3279
 
               auth.has_permission('read', 'workshops'))
3280
 
def pdf_template_show():
3281
 
    wsID = request.vars['wsID']
3282
 
 
3283
 
    return pdf_template(wsID)
3284
 
 
3285
 
 
3286
 
def pdf_template(wsID):
3287
 
    '''
3288
 
        Print friendly display of a Workshop
3289
 
    '''
3290
 
    template = get_sys_property('branding_default_template_workshops') or 'default.html'
3291
 
    template_file = 'templates/workshops/' + template
3292
 
 
3293
 
    workshop = Workshop(wsID)
3294
 
    activities = workshop.get_activities()
3295
 
    products = workshop.get_products()
3296
 
 
3297
 
    if len(activities) == 0:
3298
 
        session.flash = T('No activities found, unble to export to PDF')
3299
 
        redirect(URL('index'))
3300
 
    elif len(products) == 0:
3301
 
        session.fash = T('No products found, unable to export to PDF')
3302
 
        redirect(URL('index'))
3303
 
 
3304
 
    price = format(products[0].Price or 0, '.2f')
3305
 
 
3306
 
    workshop_image_url = URL('default', 'download', args=workshop.picture, host=True, scheme=True)
3307
 
 
3308
 
    html = response.render(template_file,
3309
 
                           dict(workshop=workshop,
3310
 
                                workshop_image_url=workshop_image_url,
3311
 
                                dates=pdf_template_get_display_dates(workshop, activities),
3312
 
                                times=pdf_template_get_display_times(workshop, activities),
3313
 
                                activities=activities,
3314
 
                                products=products,
3315
 
                                price=price,
3316
 
                                logo=pdf_template_get_logo()))
3317
 
 
3318
 
    return html
3319
 
 
3320
 
 
3321
 
def pdf_template_get_logo(var=None):
3322
 
    '''
3323
 
        Returns logo for pdf template
3324
 
    '''
3325
 
    branding_logo = os.path.join(request.folder,
3326
 
                                 'static',
3327
 
                                 'plugin_os-branding',
3328
 
                                 'logos',
3329
 
                                 'branding_logo_invoices.png')
3330
 
    if os.path.isfile(branding_logo):
3331
 
        abs_url = '%s://%s/%s/%s' % (request.env.wsgi_url_scheme,
3332
 
                                     request.env.http_host,
3333
 
                                     'static',
3334
 
                                     'plugin_os-branding/logos/branding_logo_invoices.png')
3335
 
        logo_img = IMG(_src=abs_url)
3336
 
 
3337
 
    else:
3338
 
        logo_img = ''
3339
 
 
3340
 
    return logo_img
3341
 
 
3342
 
 
3343
 
def pdf_template_get_display_dates(workshop, activities):
3344
 
    '''
3345
 
        :param workshop: Workshop object
3346
 
        :param activities: workshop activities rows
3347
 
        :return: formatted date for workshop
3348
 
    '''
3349
 
    date_until = ''
3350
 
    if len(activities) > 0:
3351
 
        date_from = activities[0].Activitydate
3352
 
 
3353
 
    if len(activities) > 1:
3354
 
        date_until = activities[len(activities) - 1].Activitydate
3355
 
 
3356
 
    if len(activities) == 0:  # no activities
3357
 
        date_from = T("No activities found...")
3358
 
        date_until = T("No activities found...")
3359
 
 
3360
 
    return dict(date_from=date_from,
3361
 
                date_until=date_until)
3362
 
 
3363
 
 
3364
 
def pdf_template_get_display_times(workshop, activities):
3365
 
    '''
3366
 
        :param workshop: Workshop object
3367
 
        :param activities: workshop activities rows
3368
 
        :return: formatted date for workshop
3369
 
    '''
3370
 
    time_until = ''
3371
 
    if len(activities) > 0:
3372
 
        time_from = activities[0].Starttime
3373
 
 
3374
 
    if len(activities) > 1:
3375
 
        time_until = activities[len(activities) - 1].Endtime
3376
 
 
3377
 
    if len(activities) == 0:  # no activities
3378
 
        date_from = T("No activities found...")
3379
 
        date_until = T("No activities found...")
3380
 
 
3381
 
    return dict(time_from=time_from,
3382
 
                time_until=time_until)
3383
 
 
3384
 
 
3385
 
@auth.requires(auth.has_membership(group_id='Admins') or \
3386
 
               auth.has_permission('update', 'workshops_info_mail'))
3387
 
def info_mail():
3388
 
    '''
3389
 
        Information mail for workshops
3390
 
    '''
3391
 
    wsID = request.vars['wsID']
3392
 
    workshop = db.workshops(wsID)
3393
 
    response.title = T('Event')
3394
 
    response.subtitle = get_subtitle(wsID)
3395
 
    response.view = 'general/tabs_menu.html'
3396
 
 
3397
 
    ###
3398
 
    # Get ID
3399
 
    ###
3400
 
    row = db.workshops_mail(workshops_id = wsID)
3401
 
    if not row:
3402
 
        # create record
3403
 
        wsmID = db.workshops_mail.insert(
3404
 
            workshops_id = wsID,
3405
 
            MailContent = None
3406
 
        )
3407
 
    else:
3408
 
        # we have an id
3409
 
        wsmID = row.id
3410
 
 
3411
 
    crud.messages.submit_button = T("Save")
3412
 
    crud.messages.record_updated = T("Saved")
3413
 
    crud.settings.formstyle = 'bootstrap3_stacked'
3414
 
    crud.settings.update_next = URL('info_mail', vars={'wsID':wsID})
3415
 
    form = crud.update(db.workshops_mail, wsmID)
3416
 
 
3417
 
    result = set_form_id_and_get_submit_button(form, 'MainForm')
3418
 
    form = result['form']
3419
 
    submit = result['submit']
3420
 
 
3421
 
    textareas = form.elements('textarea')
3422
 
    for textarea in textareas:
3423
 
        textarea['_class'] += ' tmced'
3424
 
 
3425
 
    content = form
3426
 
 
3427
 
    # preview = os_gui.get_button('notext',
3428
 
    #                             URL('workshops', 'info_mail_preview', vars={'wsmID':wsmID}),
3429
 
    #                             title=T('Preview'),
3430
 
    #                             _class='pull-right',
3431
 
    #                             btn_size='',
3432
 
    #                             _target='_blank')
3433
 
 
3434
 
    menu = get_workshops_menu(request.function, wsID)
3435
 
    back = manage_get_back()
3436
 
 
3437
 
    return dict(content=content,
3438
 
                menu=menu,
3439
 
                back=back,
3440
 
                tools='',
3441
 
                save=submit)
 
 
b'\\ No newline at end of file'