~erobererunc/sahana-eden/tropo_xforms

« back to all changes in this revision

Viewing changes to controllers/vol.py

  • Committer: Michael
  • Date: 2010-12-16 13:45:28 UTC
  • mfrom: (1479.1.91 eden)
  • Revision ID: ero@gmail.com-20101216134528-7x3m6m3fk9uc9i69
mergeĀ fromĀ trunk...

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
""" Volunteer Management System """
4
4
 
5
 
from gluon.sql import Rows
 
5
#from gluon.sql import Rows
6
6
 
7
7
prefix = request.controller
8
8
resourcename = request.function
47
47
            menu_teams = [
48
48
                ["%s %s" % (T("Team") + ":", team_name), False, URL(r=request, f="group", args=[group_id, "read"]),[
49
49
                    [T("View On Map"), False, URL(r=request, f="view_team_map", args=[group_id])],
50
 
                    [T("Send Mail"), False, URL(r=request, f="compose_group", vars={"group_id":group_id})],
 
50
                    [T("Send Notification"), False, URL(r=request, f="compose_group", vars={"group_id":group_id})],
 
51
                    [T("Find Volunteers"), False, URL(r=request, f="showSkillOptions")],
51
52
                ]],
52
53
            ]
53
54
            menu.extend(menu_teams)
54
55
 
55
56
    menu_persons = [
56
 
        [T("Persons"), False, URL(r=request, f="person", args=["search_simple"]),[
 
57
        [T("Volunteers"), False, URL(r=request, f="person", args=["search_simple"]),[
57
58
            [T("List"), False, URL(r=request, f="person")],
58
59
            [T("Add"), False, URL(r=request, f="person", args="create")],
 
60
            # Not ready yet
 
61
            #[T("Search by Skill Types"), False, URL(r=request, f="showSkillOptions")],
59
62
        ]]
60
63
    ]
61
64
    menu.extend(menu_persons)
74
77
                    # The default tab is pr_person, which is fine here.
75
78
                    [T("Person Data"), False, URL(r=request, f="person", args=[person_id], vars={"vol_tabs":"person"})],
76
79
                    [T("View On Map"), False, URL(r=request, f="view_map", args=[person_id])],
77
 
                    [T("Send Mail"), False, URL(r=request, f="compose_person", vars={"person_id":person_id})],
 
80
                    [T("Send Notification"), False, URL(r=request, f="compose_person", vars={"person_id":person_id})],
78
81
                ]],
79
82
            ]
80
83
            menu.extend(menu_person)
102
105
 
103
106
 
104
107
# -----------------------------------------------------------------------------
 
108
# People
 
109
# -----------------------------------------------------------------------------
105
110
def person():
106
111
 
107
112
    """
108
 
    This controller produces either generic person component tabs or
109
 
    volunteer-specific person component tabs, depending on whether "vol_tabs"
110
 
    in the URL's vars is "person" or "volunteer".
 
113
        This controller produces either generic person component tabs or
 
114
        volunteer-specific person component tabs, depending on whether "vol_tabs"
 
115
        in the URL's vars is "person" or "volunteer".
111
116
    """
112
117
 
113
118
    tab_set = "person"
114
119
    if "vol_tabs" in request.vars:
115
120
        tab_set = request.vars["vol_tabs"]
116
121
    if tab_set == "person":
 
122
        #db.pr_person.pr_impact_tags.readable=False
117
123
        db.pr_person.missing.default = False
118
124
        tabs = [(T("Basic Details"), None),
119
125
                (T("Images"), "image"),
143
149
                #(T("Resources"), "resource"),
144
150
               ]
145
151
 
 
152
    # Only display active volunteers
 
153
    response.s3.filter = (db.pr_person.id == db.vol_volunteer.person_id) & (db.vol_volunteer.status == 1)
 
154
 
 
155
    db.pr_presence.presence_condition.default = vita.CONFIRMED
 
156
    db.pr_presence.presence_condition.readable = False
 
157
    db.pr_presence.presence_condition.writable = False
 
158
    db.pr_presence.orig_id.readable = False
 
159
    db.pr_presence.orig_id.writable = False
 
160
    db.pr_presence.dest_id.readable = False
 
161
    db.pr_presence.dest_id.writable = False
 
162
    db.pr_presence.proc_desc.readable = False
 
163
    db.pr_presence.proc_desc.writable = False
 
164
 
146
165
    output = s3_rest_controller("pr", resourcename,
147
166
                                rheader=lambda r: shn_pr_rheader(r, tabs))
148
167
 
149
168
    shn_menu()
150
169
    return output
151
170
 
152
 
 
153
 
# -----------------------------------------------------------------------------
154
 
def project():
155
 
 
156
 
    """ RESTful CRUD controller """
157
 
 
158
 
    tabs = [
159
 
            (T("Basic Details"), None),
160
 
            (T("Staff"), "staff"),
161
 
            (T("Tasks"), "task"),
162
 
            #(T("Donors"), "organisation"),
163
 
            #(T("Sites"), "site"),  # Ticket 195
164
 
           ]
165
 
 
166
 
    rheader = lambda r: shn_project_rheader(r, tabs)
167
 
    return s3_rest_controller("project", resourcename, rheader=rheader)
168
 
 
169
 
 
170
 
# -----------------------------------------------------------------------------
171
 
def task():
172
 
 
173
 
    """ Manage current user's tasks """
174
 
 
175
 
    tablename = "project_%s" % (resourcename)
176
 
    table = db[tablename]
177
 
 
178
 
    my_person_id = s3_logged_in_person()
179
 
 
180
 
    if not my_person_id:
181
 
        session.error = T("No person record found for current user.")
182
 
        redirect(URL(r=request, f="index"))
183
 
 
184
 
    table.person_id.default = my_person_id
185
 
    #table.person_id.writable = False
186
 
 
187
 
    response.s3.filter = (db.project_task.person_id == my_person_id)
188
 
 
189
 
    s3.crud_strings[tablename].title_list = T("My Tasks")
190
 
    s3.crud_strings[tablename].subtitle_list = T("Task List")
191
 
 
192
 
    return s3_rest_controller("project", resourcename)
193
 
 
 
171
def skillsearch():
 
172
 
 
173
    """ tbc """
 
174
 
 
175
    item = []
 
176
    table1 = db.vol_skill
 
177
    table2 = db.vol_skill_types
 
178
    table3 = db.pr_person
 
179
    vol_id = db((table2.id == table1.skill_types_id) & \
 
180
                (table2.name == "medical")).select(table1.person_id, limitby=(0, 1)).first()
 
181
    person_details = db((table3.id == vol_id.person_id)).select(table3.first_name, table3.last_name, limitby=(0,1)).first()
 
182
 
 
183
    html = DIV(LABEL(vita.fullname(person_details)), DIV(T("Skills") + ": "), UL(skillset), _id="table-container")
 
184
    person_data = "<div>%s</div>" % (person_details)
 
185
    return dict(html=person_data)
 
186
 
 
187
def showSkillOptions():
 
188
 
 
189
    """
 
190
        Search for Volunteers by Skill Type
 
191
        - A Notification is sent to each matching volunteer
 
192
 
 
193
        @ToDo: Make into a normal search_simple? (may need minor modification)
 
194
        @ToDo: Make the Notification into a separate button (may want to search without notifications)
 
195
    """
 
196
 
 
197
    vol_skill_type_widget = CheckboxesWidget().widget(db.vol_skill.skill_types_id, None)
 
198
    search_btn = INPUT(_value = "search", _type = "submit")
 
199
    search_form = FORM(vol_skill_type_widget, search_btn)
 
200
    output = dict(search_form = search_form)
 
201
 
 
202
    output["table"] = ""
 
203
    if search_form.accepts(request.vars, session, keepvalues=True):
 
204
        search_skill_ids =  request.vars.skill_types_id
 
205
        
 
206
        table1 = db.vol_skill
 
207
        table2 = db.vol_skill_types
 
208
        table3 = db.pr_person
 
209
        #person_details = []
 
210
        # Print a list of volunteers with their skills status.
 
211
        # @ToDo: selects for only one skills right now. add displaying of skill name
 
212
        vol_id = db((table2.id == table1.skill_types_id) & \
 
213
                    (table2.id == search_skill_ids)).select(table1.person_id)
 
214
 
 
215
        vol_idset = []
 
216
        html = DIV(DIV(B(T("List of Volunteers for this skills set"))))  
 
217
        for id in vol_id:
 
218
            vol_idset.append(id.person_id)
 
219
            
 
220
                 
 
221
        for pe_id in vol_idset:
 
222
            person_details = db((table3.id == pe_id)).select(table3.first_name, table3.middle_name, table3.last_name).first()
 
223
            skillset = db(table1.person_id == pe_id).select(table1.status).first()
 
224
            html.append(DIV(LABEL(vita.fullname(person_details)),DIV(T("Skill Status") + ": "), UL(skillset.status)))
 
225
            # @ToDo: Make the notification message configurable
 
226
            msg.send_by_pe_id(pe_id, "CERT: Please Report for Duty", "We ask you to report for duty if you are available", 1, 1)
 
227
 
 
228
        html.append(DIV(B(T("Volunteers were notified!"))))        
 
229
    #for one_pr in person_details:
 
230
        #skillset = "approved"
 
231
        #html += DIV(LABEL(vita.fullname(one_pr)),DIV(T("Skill Status") + ": "), UL(skillset), _id="table-container")
 
232
        #person_data="<div>"+str(person_details)+"</div>"
 
233
        html2 = DIV(html, _id="table-container")
 
234
        output["table"] = html2
 
235
        
 
236
    return output
194
237
 
195
238
# -----------------------------------------------------------------------------
196
239
def skill_types():
201
244
 
202
245
 
203
246
# -----------------------------------------------------------------------------
204
 
def view_map():
205
 
 
206
 
    """ Map Location of Volunteer.
207
 
 
208
 
        Use most recent presence if available, else any address that's
209
 
        available.
210
 
 
211
 
    """
212
 
 
213
 
    person_id = request.args(0)
214
 
 
215
 
    presence_query = (db.pr_person.id == person_id) & \
216
 
                     (db.pr_presence.pe_id == db.pr_person.pe_id) & \
217
 
                     (db.gis_location.id == db.pr_presence.location_id)
218
 
 
219
 
    # Need sql.Rows object for show_map, so don't extract individual row.
220
 
    location = db(presence_query).select(db.gis_location.ALL,
221
 
                                         orderby=~db.pr_presence.datetime,
222
 
                                         limitby=(0, 1))
223
 
 
224
 
    if not location:
225
 
        address_query = (db.pr_person.id == person_id) & \
226
 
                        (db.pr_address.pe_id == db.pr_person.pe_id) & \
227
 
                        (db.gis_location.id == db.pr_address.location_id)
228
 
        # TODO: If there are multiple addresses, which should we choose?
229
 
        # For now, take whichever address is supplied first.
230
 
        location = db(address_query).select(db.gis_location.ALL, limitby=(0, 1))
231
 
 
232
 
    if location:
233
 
        # Center and zoom the map.
234
 
        location_row = location.first()  # location is a sql.Rows
235
 
        lat = location_row.lat
236
 
        lon = location_row.lon
237
 
        bounds = gis.get_bounds(features=location)
238
 
 
239
 
        volunteer = {"feature_group" : "People"}
240
 
        html = gis.show_map(
241
 
            feature_queries = [{"name" : "Volunteer",
242
 
                                "query" : location,
243
 
                                "active" : True,
244
 
                                "marker" : db(db.gis_marker.name == "volunteer").select().first().id}],
245
 
            feature_groups = [volunteer],
246
 
            wms_browser = {"name" : "Risk Maps",
247
 
                           "url" : "http://preview.grid.unep.ch:8080/geoserver/ows?service=WMS&request=GetCapabilities"},
248
 
            catalogue_overlays = True,
249
 
            catalogue_toolbar = True,
250
 
            toolbar = True,
251
 
            search = True,
252
 
            lat = lat,
253
 
            lon = lon,
254
 
            bbox = bounds,
255
 
            window = True,
256
 
        )
257
 
        return dict(map=html)
258
 
 
259
 
    # Redirect to person details if no location is available
260
 
    response.error=T("Add location")
261
 
    redirect(URL(r=request, c="vol", f="person", args=[person_id,"address"]))
262
 
 
263
 
 
 
247
def skill():
 
248
 
 
249
    """ Select skills a volunteer has. """
 
250
 
 
251
    return s3_rest_controller(prefix, "skill")
 
252
 
 
253
 
 
254
# -----------------------------------------------------------------------------
 
255
# TODO: Is resource a bad name, due to possible confusion with other usage?
 
256
def resource():
 
257
 
 
258
    """ Select resources a volunteer has """
 
259
 
 
260
    return s3_rest_controller(prefix, "resource")
 
261
 
 
262
# -----------------------------------------------------------------------------
 
263
# Teams
264
264
# -----------------------------------------------------------------------------
265
265
def group():
266
266
 
267
 
    """ Team controller -- teams use the group table from PR """
 
267
    """
 
268
        Team controller
 
269
        - uses the group table from PR
 
270
    """
268
271
 
269
272
    tablename = "pr_group"
270
273
    table = db[tablename]
312
315
 
313
316
    response.s3.filter = (db.pr_group.system == False) # do not show system groups
314
317
 
 
318
    # Redirect to member list when a new group has been created
 
319
    s3xrc.model.configure(db.pr_group,
 
320
        create_next = URL(r=request, c="vol", f="group", args=["[id]", "group_membership"]))
315
321
    s3xrc.model.configure(db.pr_group_membership,
316
322
                          list_fields=["id",
317
323
                                       "person_id",
331
337
 
332
338
 
333
339
# -----------------------------------------------------------------------------
334
 
def skill():
335
 
 
336
 
    """ Select skills a volunteer has. """
337
 
 
338
 
    return s3_rest_controller(prefix, "skill")
339
 
 
 
340
# Projects & Tasks
 
341
# -----------------------------------------------------------------------------
 
342
def project():
 
343
 
 
344
    """ RESTful CRUD controller """
 
345
 
 
346
    tabs = [
 
347
            (T("Basic Details"), None),
 
348
            #(T("Staff"), "staff"),
 
349
            (T("Tasks"), "task"),
 
350
            #(T("Donors"), "organisation"),
 
351
            #(T("Sites"), "site"),  # Ticket 195
 
352
           ]
 
353
 
 
354
    rheader = lambda r: shn_project_rheader(r, tabs)
 
355
    return s3_rest_controller("project", resourcename, rheader=rheader)
 
356
 
 
357
 
 
358
# -----------------------------------------------------------------------------
 
359
def task():
 
360
 
 
361
    """ Manage current user's tasks """
 
362
 
 
363
    tablename = "project_%s" % (resourcename)
 
364
    table = db[tablename]
 
365
 
 
366
    my_person_id = s3_logged_in_person()
 
367
 
 
368
    if not my_person_id:
 
369
        session.error = T("No person record found for current user.")
 
370
        redirect(URL(r=request, f="index"))
 
371
 
 
372
    table.person_id.default = my_person_id
 
373
    #@ToDo: if not a team leader then:
 
374
    #   can only assign themselves tasks
 
375
 
 
376
    response.s3.filter = (db.project_task.person_id == my_person_id)
 
377
 
 
378
    s3.crud_strings[tablename].title_list = T("My Tasks")
 
379
    s3.crud_strings[tablename].subtitle_list = T("Task List")
 
380
 
 
381
    return s3_rest_controller("project", resourcename)
 
382
 
 
383
 
 
384
# -----------------------------------------------------------------------------
 
385
# Maps
 
386
# -----------------------------------------------------------------------------
 
387
def view_map():
 
388
 
 
389
    """
 
390
        Show Location of a Volunteer on the Map
 
391
        
 
392
        Use most recent presence if available, else any address that's available.
 
393
        
 
394
        @ToDo: Convert to a custom method of the person resource
 
395
    """
 
396
 
 
397
    person_id = request.args(0)
 
398
 
 
399
    # Shortcuts
 
400
    persons = db.pr_person
 
401
    presences = db.pr_presence
 
402
    locations = db.gis_location
 
403
    
 
404
    # Include the person's last verified location, assuming that they're not missing
 
405
    presence_query = (persons.id == person_id) & \
 
406
                     (persons.missing == False) & \
 
407
                     (presences.pe_id == persons.pe_id) & \
 
408
                     (presences.presence_condition.belongs(vita.PERSISTANT_PRESENCE)) & \
 
409
                     (presences.closed == False) & \
 
410
                     (locations.id == presences.location_id)
 
411
 
 
412
    # Need sql.Rows object for show_map, so don't extract individual row yet.
 
413
    features = db(presence_query).select(locations.id,
 
414
                                         locations.lat,
 
415
                                         locations.lon,
 
416
                                         persons.id,
 
417
                                         limitby=(0, 1))
 
418
 
 
419
    if not features:
 
420
        # Use their Address
 
421
        address_query = (persons.id == person_id) & \
 
422
                        (db.pr_address.pe_id == persons.pe_id) & \
 
423
                        (locations.id == db.pr_address.location_id)
 
424
        # @ToDo: Lookup their schedule to see whether they should be at Work, Home or Holiday & lookup the correct address
 
425
        # For now, take whichever address is supplied first.
 
426
        features = db(address_query).select(locations.id,
 
427
                                            locations.lat,
 
428
                                            locations.lon,
 
429
                                            persons.id,
 
430
                                            limitby=(0, 1))
 
431
 
 
432
    if features:
 
433
        # Center and zoom the map.
 
434
        record = features.first()
 
435
        lat = record.gis_location.lat
 
436
        lon = record.gis_location.lon
 
437
        zoom = 15
 
438
 
 
439
        config = gis.get_config()
 
440
 
 
441
        if not deployment_settings.get_security_map() or shn_has_role("MapAdmin"):
 
442
            catalogue_toolbar = True
 
443
        else:
 
444
            catalogue_toolbar = False
 
445
 
 
446
        # Standard Feature Layers
 
447
        feature_queries = []
 
448
        feature_layers = db(db.gis_layer_feature.enabled == True).select()
 
449
        for layer in feature_layers:
 
450
            _layer = gis.get_feature_layer(layer.module, layer.resource, layer.name, layer.popup_label, config=config, marker_id=layer.marker_id, active=False, polygons=layer.polygons)
 
451
            if _layer:
 
452
                feature_queries.append(_layer)
 
453
        
 
454
        # Add the Volunteer layer
 
455
        try:
 
456
            marker_id = db(db.gis_marker.name == "volunteer").select().first().id
 
457
        except:
 
458
            marker_id = 1
 
459
        
 
460
        # Can't use this since the location_id link is via pr_presence not pr_person
 
461
        #_layer = gis.get_feature_layer("pr", "person", "Volunteer", "Volunteer", config=config, marker_id=marker_id, active=True, polygons=False)
 
462
        #if _layer:
 
463
        #    feature_queries.append(_layer)
 
464
        
 
465
        # Insert the name into the query & replace the location_id with the person_id
 
466
        for i in range(0, len(features)):
 
467
            features[i].gis_location.name = vita.fullname(db(db.pr_person.id == features[i].pr_person.id).select(limitby=(0, 1)).first())
 
468
            features[i].gis_location.id = features[i].pr_person.id
 
469
        
 
470
        feature_queries.append({"name" : "Volunteer",
 
471
                                "query" : features,
 
472
                                "active" : True,
 
473
                                "popup_label" : "Volunteer",
 
474
                                "popup_url" : URL(r=request, c="vol", f="popup") + "/<id>/read.plain",
 
475
                                "marker" : marker_id})
 
476
 
 
477
        html = gis.show_map(
 
478
            feature_queries = feature_queries,
 
479
            catalogue_toolbar = catalogue_toolbar,
 
480
            catalogue_overlays = True,
 
481
            toolbar = True,
 
482
            search = True,
 
483
            lat = lat,
 
484
            lon = lon,
 
485
            zoom = zoom,
 
486
            window = False  # We should provide a button within the map to make it go full-screen (ideally without reloading the page!)
 
487
        )
 
488
        response.view = "vol/view_map.html"
 
489
        return dict(map=html)
 
490
 
 
491
    # Redirect to person details if no location is available
 
492
    session.error = T("No location found")
 
493
    redirect(URL(r=request, c="vol", f="person", args=[person_id, "presence"]))
 
494
 
 
495
def popup(): 
 
496
 
 
497
    """
 
498
        Controller that returns a person's data
 
499
        To be used to populate map popup
 
500
    """
 
501
 
 
502
    person_id = request.args(0)
 
503
 
 
504
    vol_query = (db.pr_person.id == person_id) 
 
505
    vol = db(vol_query).select(db.pr_person.first_name, db.pr_person.middle_name, db.pr_person.last_name, limitby=(0, 1)).first()
 
506
 
 
507
    skill_query = (db.vol_skill.person_id == person_id) & (db.vol_skill.skill_types_id == db.vol_skill_types.id)
 
508
    skills = db(skill_query).select(db.vol_skill_types.name)
 
509
 
 
510
    skillset = []
 
511
 
 
512
    for s in skills:
 
513
        skillset.append(s.name)
 
514
 
 
515
    if len(skillset) == 0:
 
516
        skillset.append(T("n/a"))
 
517
 
 
518
    html = DIV(LABEL(vita.fullname(vol)), DIV(T("Skills") + ": "), UL(skillset), _id="table-container")
 
519
 
 
520
    return dict(html=html)
340
521
 
341
522
# -----------------------------------------------------------------------------
342
523
def view_team_map():
343
524
 
344
 
    """ Map Location of Volunteer in a Team.
345
 
        Use most recent presence if available, else any address that's available.
346
 
 
 
525
    """
 
526
        Show Location of a Team of Volunteers on the Map
 
527
 
 
528
        Use most recent presence if available
 
529
 
 
530
        @ToDo: Fallback to addresses
 
531
        
 
532
        @ToDo: Convert to a custom method of the group resource
347
533
    """
348
534
 
349
535
    group_id = request.args(0)
350
536
 
351
537
    members_query = (db.pr_group_membership.group_id == group_id)
352
 
    members = db(members_query).select(db.pr_group_membership.person_id) #members of a team aka group
353
 
    member_person_ids = [ x.person_id for x in members ] #list of members
354
 
 
355
 
    #Presence Data of the members with Presence Logs
356
 
    presence_rows = db(db.pr_person.id.belongs(member_person_ids) & \
357
 
                      (db.pr_presence.pe_id == db.pr_person.pe_id) & \
358
 
                      (db.gis_location.id ==  db.pr_presence.location_id)).select(db.gis_location.ALL, db.pr_person.id, orderby=~db.pr_presence.datetime)
359
 
    #Get Latest Presence Data
360
 
    person_location_sort = presence_rows.sort(lambda row:row.pr_person.id)
361
 
    previous_person_id = None
362
 
    locations_list = []
363
 
    for row in person_location_sort:
364
 
        if row.pr_person.id != previous_person_id:
365
 
            locations_list.append(row["gis_location"])
366
 
            member_person_ids.remove(row.pr_person.id)
367
 
            previous_person_id = row.pr_person.id
368
 
 
369
 
    #Address of those members without Presence data
370
 
    address = db(db.pr_person.id.belongs(member_person_ids) & \
371
 
                (db.pr_address.pe_id == db.pr_person.pe_id) & \
372
 
                (db.gis_location.id ==  db.pr_address.location_id)).select(db.gis_location.ALL)
373
 
 
374
 
    locations_list.extend(address)
375
 
 
376
 
    if locations_list:
377
 
 
378
 
        bounds = gis.get_bounds(features=locations_list)
379
 
 
380
 
        volunteer = {"feature_group" : "People"}
381
 
        html = gis.show_map(
382
 
            feature_queries = [{"name" : "Volunteer", "query" : locations_list, "active" : True, "marker" : db(db.gis_marker.name == "volunteer").select().first().id}],
383
 
            feature_groups = [volunteer],
384
 
            wms_browser = {"name" : "Risk Maps", "url" : "http://preview.grid.unep.ch:8080/geoserver/ows?service=WMS&request=GetCapabilities"},
385
 
            catalogue_overlays = True,
386
 
            catalogue_toolbar = True,
387
 
            toolbar = True,
388
 
            search = True,
389
 
            bbox = bounds,
390
 
            window = True,
391
 
        )
 
538
    members = db(members_query).select(db.pr_group_membership.person_id) # Members of a team (aka group)
 
539
    member_person_ids = [ x.person_id for x in members ] # List of members
 
540
 
 
541
    # Shortcuts
 
542
    persons = db.pr_person
 
543
    presences = db.pr_presence
 
544
    locations = db.gis_location
 
545
    
 
546
    # Presence Data for Members who aren't Missing & have a Verified Presence
 
547
    features = db(persons.id.belongs(member_person_ids) & \
 
548
                 (persons.missing == False) & \
 
549
                 (presences.pe_id == persons.pe_id) & \
 
550
                 (presences.presence_condition.belongs(vita.PERSISTANT_PRESENCE)) & \
 
551
                 (presences.closed == False) & \
 
552
                 (locations.id == presences.location_id)).select(locations.id,
 
553
                                                                 locations.lat,
 
554
                                                                 locations.lon,
 
555
                                                                 locations.lat_min,
 
556
                                                                 locations.lat_max,
 
557
                                                                 locations.lon_min,
 
558
                                                                 locations.lon_max,
 
559
                                                                 persons.id)
 
560
 
 
561
    # Address of those members without Presence data
 
562
    #address = db(persons.id.belongs(member_person_ids) & \
 
563
    #            (db.pr_address.pe_id == persons.pe_id) & \
 
564
    #            (locations.id ==  db.pr_address.location_id)).select(locations.id,
 
565
    #                                                                 locations.lat,
 
566
    #                                                                 locations.lon,
 
567
    #                                                                 persons.id)
 
568
    #locations_list.extend(address)
 
569
 
 
570
    if features:
 
571
 
 
572
        if len(features) > 1:
 
573
            # Set the viewport to the appropriate area to see everyone
 
574
            bounds = gis.get_bounds(features=features)
 
575
        else:
 
576
            # A 1-person bounds zooms in too far for many tilesets
 
577
            lat = features.first().gis_location.lat
 
578
            lon = features.first().gis_location.lon
 
579
            zoom = 15
 
580
 
 
581
        config = gis.get_config()
 
582
 
 
583
        if not deployment_settings.get_security_map() or shn_has_role("MapAdmin"):
 
584
            catalogue_toolbar = True
 
585
        else:
 
586
            catalogue_toolbar = False
 
587
 
 
588
        # Standard Feature Layers
 
589
        feature_queries = []
 
590
        feature_layers = db(db.gis_layer_feature.enabled == True).select()
 
591
        for layer in feature_layers:
 
592
            _layer = gis.get_feature_layer(layer.module, layer.resource, layer.name, layer.popup_label, config=config, marker_id=layer.marker_id, active=False, polygons=layer.polygons)
 
593
            if _layer:
 
594
                feature_queries.append(_layer)
 
595
        
 
596
        # Add the Volunteer layer
 
597
        try:
 
598
            marker_id = db(db.gis_marker.name == "volunteer").select().first().id
 
599
        except:
 
600
            marker_id = 1
 
601
        
 
602
        # Can't use this since the location_id link is via pr_presence not pr_person
 
603
        #_layer = gis.get_feature_layer("pr", "person", "Volunteer", "Volunteer", config=config, marker_id=marker_id, active=True, polygons=False)
 
604
        #if _layer:
 
605
        #    feature_queries.append(_layer)
 
606
        
 
607
        # Insert the name into the query & replace the location_id with the person_id
 
608
        for i in range(0, len(features)):
 
609
            features[i].gis_location.name = vita.fullname(db(db.pr_person.id == features[i].pr_person.id).select(limitby=(0, 1)).first())
 
610
            features[i].gis_location.id = features[i].pr_person.id
 
611
        
 
612
        feature_queries.append({"name" : "Volunteers",
 
613
                                "query" : features,
 
614
                                "active" : True,
 
615
                                "popup_label" : "Volunteer",
 
616
                                "popup_url" : URL(r=request, c="vol", f="popup") + "/<id>/read.plain",
 
617
                                "marker" : marker_id})
 
618
 
 
619
        try:
 
620
            html = gis.show_map(
 
621
                feature_queries = feature_queries,
 
622
                catalogue_toolbar = catalogue_toolbar,
 
623
                catalogue_overlays = True,
 
624
                toolbar = True,
 
625
                search = True,
 
626
                bbox = bounds,
 
627
                window = True,  # @ToDo: Change to False & create a way to convert an embedded map to a full-screen one without a screen refresh
 
628
            )
 
629
        except:
 
630
            html = gis.show_map(
 
631
                feature_queries = feature_queries,
 
632
                catalogue_toolbar = catalogue_toolbar,
 
633
                catalogue_overlays = True,
 
634
                toolbar = True,
 
635
                search = True,
 
636
                lat = lat,
 
637
                lon = lon,
 
638
                zoom = zoom,
 
639
                window = True,  # @ToDo: Change to False & create a way to convert an embedded map to a full-screen one without a screen refresh
 
640
            )
 
641
        response.view = "vol/view_map.html"
392
642
        return dict(map=html)
393
643
 
394
644
    # Redirect to team details if no location is available
395
 
    response.error=T("Add Location")
396
 
    redirect(URL(r=request, c="vol", f="group", args=[group_id,"address"]))
397
 
 
398
 
 
 
645
    session.error=T("Add Location")
 
646
    redirect(URL(r=request, c="vol", f="group", args=[group_id, "address"]))
 
647
 
 
648
# -----------------------------------------------------------------------------
 
649
def view_project_map():
 
650
 
 
651
    """
 
652
        Show Location of all Tasks on the Map
 
653
        
 
654
        @ToDo: Different Colours for Status
 
655
            Green for Complete
 
656
            Red for Urgent/Incomplete
 
657
            Amber for Non-Urgent/Incomplete
 
658
            
 
659
        @ToDo: A single map with both Tasks & Volunteers displayed on it
 
660
        
 
661
        @ToDo: Convert to a custom method of the project resource
 
662
    """
 
663
 
 
664
    project_id = request.args(0)
 
665
 
 
666
    # Shortcuts
 
667
    tasks = db.project_task
 
668
    locations = db.gis_location
 
669
 
 
670
    features = db((tasks.project_id == project_id) & \
 
671
                  (locations.id == tasks.location_id)).select(locations.id,
 
672
                                                              locations.lat,
 
673
                                                              locations.lon,
 
674
                                                              locations.lat_min,
 
675
                                                              locations.lat_max,
 
676
                                                              locations.lon_min,
 
677
                                                              locations.lon_max,
 
678
                                                              tasks.subject,
 
679
                                                              tasks.status,
 
680
                                                              tasks.urgent,
 
681
                                                              tasks.id)
 
682
 
 
683
    if features:
 
684
 
 
685
        if len(features) > 1:
 
686
            # Set the viewport to the appropriate area to see all the tasks
 
687
            bounds = gis.get_bounds(features=features)
 
688
        else:
 
689
            # A 1-task bounds zooms in too far for many tilesets
 
690
            lat = features.first().gis_location.lat
 
691
            lon = features.first().gis_location.lon
 
692
            zoom = 15
 
693
 
 
694
        config = gis.get_config()
 
695
 
 
696
        if not deployment_settings.get_security_map() or shn_has_role("MapAdmin"):
 
697
            catalogue_toolbar = True
 
698
        else:
 
699
            catalogue_toolbar = False
 
700
 
 
701
        # Standard Feature Layers
 
702
        feature_queries = []
 
703
        feature_layers = db(db.gis_layer_feature.enabled == True).select()
 
704
        for layer in feature_layers:
 
705
            _layer = gis.get_feature_layer(layer.module, layer.resource, layer.name, layer.popup_label, config=config, marker_id=layer.marker_id, active=False, polygons=layer.polygons)
 
706
            if _layer:
 
707
                feature_queries.append(_layer)
 
708
        
 
709
        # Add the Tasks layer
 
710
        # Can't use this since we want to use different colours, not markers
 
711
        #_layer = gis.get_feature_layer("project", "task", "Tasks", "Task", config=config, marker_id=marker_id, active=True, polygons=False)
 
712
        #if _layer:
 
713
        #    feature_queries.append(_layer)
 
714
        
 
715
        # Insert the name into the query & replace the location_id with the task_id
 
716
        for i in range(0, len(features)):
 
717
            features[i].gis_location.name = features[i].project_task.subject
 
718
            features[i].gis_location.id = features[i].project_task.id
 
719
            features[i].gis_location.shape = "circle"
 
720
            if features[i].project_task.status in [3, 4, 6]:
 
721
                # Green for 'Completed', 'Postponed' or 'Cancelled'
 
722
                features[i].gis_location.color = "green"
 
723
            elif features[i].project_task.status == 1 and features[i].project_task.urgent == True:
 
724
                # Red for 'Urgent' and 'New' (i.e. Unassigned)
 
725
                features[i].gis_location.color = "red"
 
726
            else:
 
727
                # Amber for 'Feedback' or 'non-urgent'
 
728
                features[i].gis_location.color = "      #FFBF00"
 
729
            
 
730
        
 
731
        feature_queries.append({
 
732
                                "name" : "Tasks",
 
733
                                "query" : features,
 
734
                                "active" : True,
 
735
                                "popup_label" : "Task",
 
736
                                "popup_url" : URL(r=request, c="project", f="task") + "/<id>/read.plain"
 
737
                                })
 
738
 
 
739
        try:
 
740
            # bbox
 
741
            html = gis.show_map(
 
742
                feature_queries = feature_queries,
 
743
                catalogue_toolbar = catalogue_toolbar,
 
744
                catalogue_overlays = True,
 
745
                toolbar = True,
 
746
                search = True,
 
747
                bbox = bounds,
 
748
                window = True,  # @ToDo Change to False & create a way to convert an embedded map to a full-screen one without a screen refresh
 
749
            )
 
750
        except:
 
751
            # lat/lon/zoom
 
752
            html = gis.show_map(
 
753
                feature_queries = feature_queries,
 
754
                catalogue_toolbar = catalogue_toolbar,
 
755
                catalogue_overlays = True,
 
756
                toolbar = True,
 
757
                search = True,
 
758
                lat = lat,
 
759
                lon = lon,
 
760
                zoom = zoom,
 
761
                window = True,  # @ToDo Change to False & create a way to convert an embedded map to a full-screen one without a screen refresh
 
762
            )
 
763
        response.view = "vol/view_map.html"
 
764
        return dict(map=html)
 
765
 
 
766
    # Redirect to tasks if no task location is available
 
767
    session.error=T("No Tasks with Location Data!")
 
768
    redirect(URL(r=request, c="vol", f="project", args=[project_id, "task"]))
 
769
 
 
770
# -----------------------------------------------------------------------------
 
771
def view_offices_map():
 
772
 
 
773
    """
 
774
        Show Location of all Offices on the Map
 
775
        - optionally filter by those within a radius of a specific Event (Project)
 
776
    """
 
777
 
 
778
    project_id = None
 
779
    radius = None
 
780
 
 
781
    if "project_id" in request.vars:
 
782
        project_id = request.vars.project_id
 
783
 
 
784
    if "radius" in request.vars:
 
785
        radius = request.vars.radius
 
786
 
 
787
    # Shortcuts
 
788
    projects = db.project_project
 
789
    offices = db.org_office
 
790
    locations = db.gis_location
 
791
 
 
792
    if project_id and radius:
 
793
        # @ToDo: Optimise by doing a single SQL query with the Spatial one
 
794
        project_locations = db((projects.id == project_id) & (locations.id == projects.location_id)).select(locations.id,
 
795
                                                                                                            locations.lat,
 
796
                                                                                                            locations.lon,
 
797
                                                                                                            locations.lat_min,
 
798
                                                                                                            locations.lat_max,
 
799
                                                                                                            locations.lon_min,
 
800
                                                                                                            locations.lon_max,
 
801
                                                                                                            projects.code,
 
802
                                                                                                            projects.id,
 
803
                                                                                                            limitby=(0, 1))
 
804
        project_location = project_locations.first()
 
805
        lat = project_location.gis_location.lat
 
806
        lon = project_location.gis_location.lon
 
807
        
 
808
        if (lat is None) or (lon is None):
 
809
            # Zero is allowed
 
810
            session.error = T("Project has no Lat/Lon")
 
811
            redirect(URL(r=request, c="vol", f="project", args=[project_id]))
 
812
 
 
813
        # Perform the Spatial query
 
814
        features = gis.get_features_in_radius(lat, lon, radius, tablename="org_office")
 
815
 
 
816
        # @ToDo: we also want the Project to show (with different Icon): project_locations set ready
 
817
    
 
818
    else:
 
819
        features = db((offices.id > 0) & \
 
820
                      (locations.id == offices.location_id)).select(locations.id,
 
821
                                                                    locations.lat,
 
822
                                                                    locations.lon,
 
823
                                                                    locations.lat_min,
 
824
                                                                    locations.lat_max,
 
825
                                                                    locations.lon_min,
 
826
                                                                    locations.lon_max,
 
827
                                                                    offices.name,
 
828
                                                                    offices.id)
 
829
 
 
830
    if features:
 
831
 
 
832
        if len(features) > 1:
 
833
            # Set the viewport to the appropriate area to see all the tasks
 
834
            bounds = gis.get_bounds(features=features)
 
835
        else:
 
836
            # A 1-task bounds zooms in too far for many tilesets
 
837
            lat = features[0].gis_location.lat
 
838
            lon = features[0].gis_location.lon
 
839
            zoom = 15
 
840
 
 
841
        config = gis.get_config()
 
842
 
 
843
        if not deployment_settings.get_security_map() or shn_has_role("MapAdmin"):
 
844
            catalogue_toolbar = True
 
845
        else:
 
846
            catalogue_toolbar = False
 
847
 
 
848
        # Standard Feature Layers
 
849
        feature_queries = []
 
850
        feature_layers = db(db.gis_layer_feature.enabled == True).select()
 
851
        for layer in feature_layers:
 
852
            _layer = gis.get_feature_layer(layer.module, layer.resource, layer.name, layer.popup_label, config=config, marker_id=layer.marker_id, active=False, polygons=layer.polygons)
 
853
            if _layer:
 
854
                feature_queries.append(_layer)
 
855
        
 
856
        # Add the Offices layer
 
857
        # Can't use this since we may have a custom spatial query
 
858
        #_layer = gis.get_feature_layer("org", "office", "Offices", "Office", config=config, marker_id=marker_id, active=True, polygons=False)
 
859
        #if _layer:
 
860
        #    feature_queries.append(_layer)
 
861
 
 
862
        try:
 
863
            office_marker_id = db(db.gis_marker.name == "office").select().first().id
 
864
        except:
 
865
            office_marker_id = 1
 
866
 
 
867
        # Insert the name into the query & replace the location_id with the office_id
 
868
        for i in range(0, len(features)):
 
869
            features[i].gis_location.name = features[i].org_office.name
 
870
            features[i].gis_location.id = features[i].org_office.id
 
871
            # If a Project
 
872
            #    features[i].gis_location.shape = "circle"
 
873
            #    if features[i].project_task.status in [3, 4, 6]:
 
874
            #        # Green for 'Completed', 'Postponed' or 'Cancelled'
 
875
            #        features[i].gis_location.color = "green"
 
876
            #    elif features[i].project_task.status == 1 and features[i].project_task.urgent == True:
 
877
            #        # Red for 'Urgent' and 'New' (i.e. Unassigned)
 
878
            #        features[i].gis_location.color = "red"
 
879
            #    else:
 
880
            #        # Amber for 'Feedback' or 'non-urgent'
 
881
            #        features[i].gis_location.color = " #FFBF00"
 
882
        
 
883
        feature_queries.append({
 
884
                                "name" : "Tasks",
 
885
                                "query" : features,
 
886
                                "active" : True,
 
887
                                "popup_label" : "Task",
 
888
                                "popup_url" : URL(r=request, c="org", f="office") + "/<id>/read.plain",
 
889
                                "marker" : office_marker_id
 
890
                                })
 
891
 
 
892
        try:
 
893
            # Are we using bbox?
 
894
            html = gis.show_map(
 
895
                feature_queries = feature_queries,
 
896
                catalogue_toolbar = catalogue_toolbar,
 
897
                catalogue_overlays = True,
 
898
                toolbar = True,
 
899
                search = True,
 
900
                bbox = bounds,
 
901
                window = True,  # @ToDo: Change to False & create a way to convert an embedded map to a full-screen one without a screen refresh
 
902
            )
 
903
        except:
 
904
            # No: Lat/Lon/Zoom
 
905
            html = gis.show_map(
 
906
                feature_queries = feature_queries,
 
907
                catalogue_toolbar = catalogue_toolbar,
 
908
                catalogue_overlays = True,
 
909
                toolbar = True,
 
910
                search = True,
 
911
                lat = lat,
 
912
                lon = lon,
 
913
                zoom = zoom,
 
914
                window = True,  # @ToDo: Change to False & create a way to convert an embedded map to a full-screen one without a screen refresh
 
915
            )
 
916
 
 
917
        response.view = "vol/view_map.html"
 
918
        return dict(map=html)
 
919
    
 
920
    else:
 
921
        # Redirect to offices if none found
 
922
        session.error=T("No Offices found!")
 
923
        redirect(URL(r=request, c="org", f="office"))
 
924
 
 
925
# -----------------------------------------------------------------------------
 
926
# Messaging
399
927
# -----------------------------------------------------------------------------
400
928
def compose_person():
401
929
 
425
953
                           redirect_vars={"group_id":request.vars.group_id},
426
954
                           title_name="Send a message to a team of volunteers")
427
955
 
428
 
 
429
 
# -----------------------------------------------------------------------------
430
 
# TODO: Is resource a bad name, due to possible confusion with other usage?
431
 
def resource():
432
 
 
433
 
    """ Select resources a volunteer has """
434
 
 
435
 
    return s3_rest_controller(prefix, "resource")
436
 
 
437
 
# -----------------------------------------------------------------------------
 
956
# -----------------------------------------------------------------------------
 
 
b'\\ No newline at end of file'