~hsiung0911/sahana-eden/test

« back to all changes in this revision

Viewing changes to models/05_org.py

  • Committer: eliao
  • Date: 2010-08-01 09:56:45 UTC
  • Revision ID: eliao@ibm-l3bw307-20100801095645-gsq9wcmjan0o0tby
This is my change

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
"""
 
4
    Organisation Registry
 
5
"""
 
6
 
 
7
module = "org"
 
8
 
 
9
# -----------------------------------------------------------------------------
 
10
# Settings
 
11
#
 
12
resource = "setting"
 
13
tablename = "%s_%s" % (module, resource)
 
14
table = db.define_table(tablename,
 
15
                Field("audit_read", "boolean"),
 
16
                Field("audit_write", "boolean"),
 
17
                migrate=migrate)
 
18
 
 
19
# -----------------------------------------------------------------------------
 
20
# Sectors (to be renamed as Clusters)
 
21
#
 
22
resource = "sector"
 
23
tablename = "%s_%s" % (module, resource)
 
24
table = db.define_table(tablename, timestamp, uuidstamp, deletion_status,
 
25
                Field("name", length=128, notnull=True, unique=True),
 
26
                migrate=migrate)
 
27
 
 
28
# Field settings
 
29
table.uuid.requires = IS_NOT_IN_DB(db, "%s.uuid" % tablename)
 
30
table.name.requires = [IS_NOT_EMPTY(), IS_NOT_IN_DB(db, "%s.name" % tablename)]
 
31
table.name.label = T("Name")
 
32
table.name.comment = SPAN("*", _class="req")
 
33
 
 
34
# Functions
 
35
def shn_sector_represent(sector_ids):
 
36
    if not sector_ids:
 
37
        return "None"
 
38
    elif "|" in str(sector_ids):
 
39
        sectors = [db(db.org_sector.id == id).select(db.org_sector.name, limitby=(0, 1)).first().name for id in sector_ids.split("|") if id]
 
40
        return ", ".join(sectors)
 
41
    else:
 
42
        return db(db.org_sector.id == sector_ids).select(db.org_sector.name, limitby=(0, 1)).first().name
 
43
 
 
44
# Reusable field
 
45
ADD_SECTOR = T("Add Sector")
 
46
sector_id = db.Table(None, "sector_id",
 
47
                     FieldS3("sector_id", sortby="name",
 
48
                           requires = IS_NULL_OR(IS_ONE_OF(db, "org_sector.id", "%(name)s", multiple=True)),
 
49
                           represent = shn_sector_represent,
 
50
                           label = T("Sector"),
 
51
                           comment = DIV(A(ADD_SECTOR, _class="colorbox", _href=URL(r=request, c="org", f="sector", args="create", vars=dict(format="popup")), _target="top", _title=ADD_SECTOR),
 
52
                                     DIV( _class="tooltip", _title=Tstr("Add Sector") + "|" + Tstr("The Sector(s) this organization works in. Multiple values can be selected by holding down the 'Control' key."))),
 
53
                           ondelete = "RESTRICT"
 
54
                          ))
 
55
 
 
56
# -----------------------------------------------------------------------------
 
57
# Organizations
 
58
#
 
59
org_organisation_type_opts = {
 
60
    1:T("Government"),
 
61
    2:T("Embassy"),
 
62
    3:T("International NGO"),
 
63
    4:T("Donor"),               # Don't change this number without changing organisation_popup.html
 
64
    6:T("National NGO"),
 
65
    7:T("UN"),
 
66
    8:T("International Organization"),
 
67
    9:T("Military"),
 
68
    10:T("Private")
 
69
    #12:T("MINUSTAH")   Haiti-specific
 
70
}
 
71
 
 
72
resource = "organisation"
 
73
tablename = module + "_" + resource
 
74
table = db.define_table(tablename, timestamp, uuidstamp, deletion_status,
 
75
                pr_pe_fieldset,                         # Person Entity Field Set
 
76
                #Field("privacy", "integer", default=0),
 
77
                #Field("archived", "boolean", default=False),
 
78
                Field("name", length=128, notnull=True, unique=True),
 
79
                Field("acronym", length=8),
 
80
                Field("type", "integer"),
 
81
                sector_id,
 
82
                #Field("registration", label=T("Registration")),    # Registration Number
 
83
                Field("country", "integer"),
 
84
                Field("website"),
 
85
                Field("twitter"),   # deprecated by pe_contact component
 
86
                Field("donation_phone"),
 
87
                shn_comments_field,
 
88
                source_id,
 
89
                migrate=migrate)
 
90
 
 
91
# Field settings
 
92
table.uuid.requires = IS_NOT_IN_DB(db, "%s.uuid" % tablename)
 
93
table.pr_pe_label.readable = False
 
94
table.pr_pe_label.writable = False
 
95
table.name.requires = [IS_NOT_EMPTY(), IS_NOT_IN_DB(db, "%s.name" % tablename)]
 
96
table.type.requires = IS_NULL_OR(IS_IN_SET(org_organisation_type_opts))
 
97
table.type.represent = lambda opt: org_organisation_type_opts.get(opt, UNKNOWN_OPT)
 
98
table.country.requires = IS_NULL_OR(IS_IN_SET(shn_list_of_nations))
 
99
table.country.represent = lambda opt: shn_list_of_nations.get(opt, UNKNOWN_OPT)
 
100
table.website.requires = IS_NULL_OR(IS_URL())
 
101
table.donation_phone.requires = shn_phone_requires
 
102
table.name.label = T("Name")
 
103
table.name.comment = SPAN("*", _class="req")
 
104
table.acronym.label = T("Acronym")
 
105
table.type.label = T("Type")
 
106
table.donation_phone.label = T("Donation Phone #")
 
107
table.donation_phone.comment = DIV( _class="tooltip", _title=Tstr("Donation Phone #") + "|" + Tstr("Phone number to donate to this organization's relief efforts."))
 
108
table.country.label = T("Home Country")
 
109
table.website.label = T("Website")
 
110
# Should be visible to the Dashboard
 
111
table.website.represent = shn_url_represent
 
112
table.twitter.label = T("Twitter")
 
113
table.twitter.comment = DIV( _class="tooltip", _title=Tstr("Twitter") + "|" + Tstr("Twitter ID or #hashtag"))
 
114
# CRUD strings
 
115
ADD_ORGANIZATION = Tstr("Add Organization")
 
116
LIST_ORGANIZATIONS = T("List Organizations")
 
117
s3.crud_strings[tablename] = Storage(
 
118
    title_create = ADD_ORGANIZATION,
 
119
    title_display = T("Organization Details"),
 
120
    title_list = LIST_ORGANIZATIONS,
 
121
    title_update = T("Edit Organization"),
 
122
    title_search = T("Search Organizations"),
 
123
    subtitle_create = T("Add New Organization"),
 
124
    subtitle_list = T("Organizations"),
 
125
    label_list_button = LIST_ORGANIZATIONS,
 
126
    label_create_button = ADD_ORGANIZATION,
 
127
    label_delete_button = T("Delete Organization"),
 
128
    msg_record_created = T("Organization added"),
 
129
    msg_record_modified = T("Organization updated"),
 
130
    msg_record_deleted = T("Organization deleted"),
 
131
    msg_list_empty = T("No Organizations currently registered"))
 
132
 
 
133
# Reusable field
 
134
organisation_popup_url = URL(r=request, c="org", f="organisation", args="create", vars=dict(format="popup"))
 
135
shn_organisation_comment = DIV(A(ADD_ORGANIZATION,
 
136
                           _class="colorbox",
 
137
                           _href=organisation_popup_url,
 
138
                           _target="top",
 
139
                           _title=ADD_ORGANIZATION),
 
140
                         DIV(DIV(_class="tooltip",
 
141
                                 _title=ADD_ORGANIZATION + "|" + Tstr("The Organization this record is associated with."))))
 
142
organisation_id = db.Table(None, "organisation_id",
 
143
                           FieldS3("organisation_id", db.org_organisation, sortby="name",
 
144
                           requires = IS_NULL_OR(IS_ONE_OF(db, "org_organisation.id", "%(name)s")),
 
145
                           represent = lambda id: (id and [db(db.org_organisation.id == id).select(db.org_organisation.name, limitby=(0, 1)).first().name] or ["None"])[0],
 
146
                           label = T("Organization"),
 
147
                           comment = shn_organisation_comment,
 
148
                           ondelete = "RESTRICT"
 
149
                          ))
 
150
 
 
151
# Orgs as component of Clusters
 
152
s3xrc.model.add_component(module, resource,
 
153
                          multiple=True,
 
154
                          joinby=dict(org_sector="sector_id"),
 
155
                          deletable=True,
 
156
                          editable=True)
 
157
 
 
158
s3xrc.model.configure(table,
 
159
                      # Ensure that table is substituted when lambda defined not evaluated by using the default value
 
160
                      onaccept=lambda form, tab=table: shn_pentity_onaccept(form, table=tab, entity_type=5),
 
161
                      delete_onaccept=lambda form: shn_pentity_ondelete(form),
 
162
                      list_fields = ["id",
 
163
                                     "name",
 
164
                                     "acronym",
 
165
                                     "type",
 
166
                                     "country",
 
167
                                     "website"])
 
168
 
 
169
# -----------------------------------------------------------------------------
 
170
# Offices
 
171
#
 
172
org_office_type_opts = {
 
173
    1:T("Satellite Office"),
 
174
    2:T("Country"),
 
175
    3:T("Regional"),
 
176
    4:T("Headquarters")
 
177
}
 
178
 
 
179
resource = "office"
 
180
tablename = module + "_" + resource
 
181
table = db.define_table(tablename, timestamp, uuidstamp, deletion_status,
 
182
                pr_pe_fieldset,                         # Person Entity Field Set
 
183
                Field("name", notnull=True),
 
184
                organisation_id,
 
185
                Field("type", "integer"),
 
186
                location_id,
 
187
                Field("parent", "reference org_office"),   # This form of hierarchy may not work on all Databases
 
188
                Field("address", "text"),
 
189
                Field("postcode"),
 
190
                Field("phone1"),
 
191
                Field("phone2"),
 
192
                Field("email"),
 
193
                Field("fax"),
 
194
                Field("national_staff", "integer"),
 
195
                Field("international_staff", "integer"),
 
196
                Field("number_of_vehicles", "integer"),
 
197
                Field("vehicle_types"),
 
198
                Field("equipment"),
 
199
                source_id,
 
200
                shn_comments_field,
 
201
                migrate=migrate)
 
202
 
 
203
# Field settings
 
204
table.uuid.requires = IS_NOT_IN_DB(db, "%s.uuid" % tablename)
 
205
table.pr_pe_label.readable = False
 
206
table.pr_pe_label.writable = False
 
207
#db[table].name.requires = IS_NOT_EMPTY()   # Office names don't have to be unique
 
208
table.name.requires = [IS_NOT_EMPTY(), IS_NOT_IN_DB(db, "%s.name" % tablename)]
 
209
table.type.requires = IS_NULL_OR(IS_IN_SET(org_office_type_opts))
 
210
table.type.represent = lambda opt: org_office_type_opts.get(opt, UNKNOWN_OPT)
 
211
table.parent.requires = IS_NULL_OR(IS_ONE_OF(db, "org_office.id", "%(name)s"))
 
212
table.parent.represent = lambda id: (id and [db(db.org_office.id == id).select(db.org_office.name, limitby=(0, 1)).first().name] or ["None"])[0]
 
213
table.phone1.requires = shn_phone_requires
 
214
table.phone2.requires = shn_phone_requires
 
215
table.fax.requires = shn_phone_requires
 
216
table.email.requires = IS_NULL_OR(IS_EMAIL())
 
217
table.national_staff.requires = IS_NULL_OR(IS_INT_IN_RANGE(0, 99999))
 
218
table.international_staff.requires = IS_NULL_OR(IS_INT_IN_RANGE(0, 9999))
 
219
table.number_of_vehicles.requires = IS_NULL_OR(IS_INT_IN_RANGE(0, 9999))
 
220
table.name.label = T("Name")
 
221
table.name.comment = SPAN("*", _class="req")
 
222
table.parent.label = T("Parent")
 
223
table.type.label = T("Type")
 
224
table.address.label = T("Address")
 
225
table.postcode.label = T("Postcode")
 
226
table.phone1.label = T("Phone 1")
 
227
table.phone2.label = T("Phone 2")
 
228
table.email.label = T("Email")
 
229
table.fax.label = T("FAX")
 
230
table.national_staff.label = T("National Staff")
 
231
table.international_staff.label = T("International Staff")
 
232
table.number_of_vehicles.label = T("Number of Vehicles")
 
233
table.vehicle_types.label = T("Vehicle Types")
 
234
table.equipment.label = T("Equipment")
 
235
# CRUD strings
 
236
ADD_OFFICE = Tstr("Add Office")
 
237
LIST_OFFICES = T("List Offices")
 
238
s3.crud_strings[tablename] = Storage(
 
239
    title_create = ADD_OFFICE,
 
240
    title_display = T("Office Details"),
 
241
    title_list = LIST_OFFICES,
 
242
    title_update = T("Edit Office"),
 
243
    title_search = T("Search Offices"),
 
244
    subtitle_create = T("Add New Office"),
 
245
    subtitle_list = T("Offices"),
 
246
    label_list_button = LIST_OFFICES,
 
247
    label_create_button = ADD_OFFICE,
 
248
    label_delete_button = T("Delete Office"),
 
249
    msg_record_created = T("Office added"),
 
250
    msg_record_modified = T("Office updated"),
 
251
    msg_record_deleted = T("Office deleted"),
 
252
    msg_list_empty = T("No Offices currently registered"))
 
253
 
 
254
# Reusable field for other tables to reference
 
255
office_id = db.Table(None, "office_id",
 
256
            FieldS3("office_id", db.org_office, sortby="name",
 
257
                requires = IS_NULL_OR(IS_ONE_OF(db, "org_office.id", "%(name)s")),
 
258
                represent = lambda id: (id and [db(db.org_office.id == id).select(db.org_office.name, limitby=(0, 1)).first().name] or ["None"])[0],
 
259
                label = T("Office"),
 
260
                comment = DIV(A(ADD_OFFICE, _class="colorbox", _href=URL(r=request, c="org", f="office", args="create", vars=dict(format="popup")), _target="top", _title=ADD_OFFICE),
 
261
                          DIV( _class="tooltip", _title=ADD_OFFICE + "|" + Tstr("The Office this record is associated with."))),
 
262
                ondelete = "RESTRICT"
 
263
                ))
 
264
 
 
265
# Offices as component of Orgs & Locations
 
266
s3xrc.model.add_component(module, resource,
 
267
                          multiple=True,
 
268
                          joinby=dict(org_organisation="organisation_id", gis_location="location_id"),
 
269
                          deletable=True,
 
270
                          editable=True)
 
271
 
 
272
s3xrc.model.configure(table,
 
273
                      # Ensure that table is substituted when lambda defined not evaluated by using the default value
 
274
                      onaccept=lambda form, tab=table: shn_pentity_onaccept(form, table=tab, entity_type=6),
 
275
                      delete_onaccept=lambda form: shn_pentity_ondelete(form),
 
276
                      list_fields=["id",
 
277
                                   "name",
 
278
                                   "organisation_id",   # Filtered in Component views
 
279
                                   "location_id",
 
280
                                   "phone1",
 
281
                                   "email"])
 
282
 
 
283
# Donors are a type of Organisation
 
284
def shn_donor_represent(donor_ids):
 
285
    if not donor_ids:
 
286
        return "None"
 
287
    elif "|" in str(donor_ids):
 
288
        donors = [db(db.org_donor.id == id).select(db.org_donor.name, limitby=(0, 1)).first().name for id in donor_ids.split("|") if id]
 
289
        return ", ".join(donors)
 
290
    else:
 
291
        return db(db.org_donor.id == donor_ids).select(db.org_donor.name, limitby=(0, 1)).first().name
 
292
 
 
293
ADD_DONOR = Tstr("Add Donor")
 
294
donor_id = db.Table(None, "donor_id",
 
295
            FieldS3("donor_id", db.org_organisation, sortby="name",
 
296
                requires = IS_NULL_OR(IS_ONE_OF(db, "org_organisation.id", "%(name)s", multiple=True, filterby="type", filter_opts=[4])),
 
297
                represent = shn_donor_represent,
 
298
                label = T("Donor"),
 
299
                comment = DIV(A(ADD_DONOR, _class="colorbox", _href=URL(r=request, c="org", f="organisation", args="create", vars=dict(format="popup", child="donor_id")), _target="top", _title=ADD_DONOR),
 
300
                          DIV( _class="tooltip", _title=ADD_DONOR + "|" + Tstr("The Donor(s) for this project. Multiple values can be selected by holding down the 'Control' key."))),
 
301
                ondelete = "RESTRICT"
 
302
                ))
 
303
 
 
304
# -----------------------------------------------------------------------------
 
305
# Projects:
 
306
#   the projects which each organization is engaged in
 
307
#
 
308
org_project_status_opts = {
 
309
    1: T('active'),
 
310
    2: T('completed'),
 
311
    99: T('inactive')
 
312
    }
 
313
resource = "project"
 
314
tablename = module + "_" + resource
 
315
table = db.define_table(tablename, timestamp, deletion_status,
 
316
                Field("code"),
 
317
                Field("name"),
 
318
                organisation_id,
 
319
                location_id,
 
320
                sector_id,
 
321
                Field('status', 'integer',
 
322
                        requires = IS_IN_SET(org_project_status_opts, zero=None),
 
323
                        # default = 99,
 
324
                        label = T('Project Status'),
 
325
                        represent = lambda opt: org_project_status_opts.get(opt, UNKNOWN_OPT)),
 
326
                Field("description", "text"),
 
327
                Field("beneficiaries", "integer"),
 
328
                Field("start_date", "date"),
 
329
                Field("end_date", "date"),
 
330
                Field("funded", "boolean"),
 
331
                donor_id,
 
332
                Field("budgeted_cost", "double"),
 
333
                migrate=migrate)
 
334
 
 
335
# Field settings
 
336
table.code.requires = [IS_NOT_EMPTY(error_message=T("Please fill this!")),
 
337
                         IS_NOT_IN_DB(db, "org_project.code")]
 
338
table.start_date.requires = IS_NULL_OR(IS_DATE())
 
339
table.end_date.requires = IS_NULL_OR(IS_DATE())
 
340
table.budgeted_cost.requires = IS_NULL_OR(IS_FLOAT_IN_RANGE(0, 999999999))
 
341
 
 
342
# Project Resource called from multiple controllers
 
343
# - so we define strings in the model
 
344
table.code.label = T("Code")
 
345
table.code.comment = SPAN("*", _class="req")
 
346
table.name.label = T("Title")
 
347
table.start_date.label = T("Start date")
 
348
table.end_date.label = T("End date")
 
349
table.description.label = T("Description")
 
350
#table.description.comment = SPAN("*", _class="req")
 
351
table.status.label = T("Status")
 
352
table.status.comment = SPAN("*", _class="req")
 
353
 
 
354
ADD_PROJECT = Tstr("Add Project")
 
355
s3.crud_strings[tablename] = Storage(
 
356
    title_create = ADD_PROJECT,
 
357
    title_display = T("Project Details"),
 
358
    title_list = T("List Projects"),
 
359
    title_update = T("Edit Project"),
 
360
    title_search = T("Search Projects"),
 
361
    subtitle_create = T("Add New Project"),
 
362
    subtitle_list = T("Projects"),
 
363
    label_list_button = T("List Projects"),
 
364
    label_create_button = ADD_PROJECT,
 
365
    label_delete_button = T("Delete Project"),
 
366
    msg_record_created = T("Project added"),
 
367
    msg_record_modified = T("Project updated"),
 
368
    msg_record_deleted = T("Project deleted"),
 
369
    msg_list_empty = T("No Projects currently registered"))
 
370
 
 
371
# Reusable field
 
372
project_id = db.Table(None, "project_id",
 
373
                        FieldS3("project_id", db.org_project, sortby="name",
 
374
                        requires = IS_NULL_OR(IS_ONE_OF(db, "org_project.id", "%(code)s")),
 
375
                        represent = lambda id: (id and [db.org_project[id].code] or ["None"])[0],
 
376
                        comment = DIV(A(ADD_PROJECT, _class="colorbox", _href=URL(r=request, c="org", f="project", args="create", vars=dict(format="popup")), _target="top", _title=ADD_PROJECT),
 
377
                                  DIV( _class="tooltip", _title=ADD_PROJECT + "|" + Tstr("Add new project."))),
 
378
                        label = "Project",
 
379
                        ondelete = "RESTRICT"
 
380
                        ))
 
381
 
 
382
# Projects as component of Orgs & Locations
 
383
s3xrc.model.add_component(module, resource,
 
384
                          multiple=True,
 
385
                          joinby=dict(org_organisation="organisation_id", gis_location="location_id"),
 
386
                          deletable=True,
 
387
                          editable=True)
 
388
 
 
389
s3xrc.model.configure(table,
 
390
                      list_fields=["id",
 
391
                                   "organisation_id",
 
392
                                   "location_id",
 
393
                                   "sector_id",
 
394
                                   "code",
 
395
                                   "name",
 
396
                                   "status",
 
397
                                   "start_date",
 
398
                                   "end_date",
 
399
                                   "budgeted_cost"])
 
400
 
 
401
# -----------------------------------------------------------------------------
 
402
# Staff
 
403
# Many-to-Many Persons to Offices & Projects with also the Title & Manager that the person has in this context
 
404
# ToDo: Build an Organigram out of this data?
 
405
#
 
406
resource = "staff"
 
407
tablename = module + "_" + resource
 
408
table = db.define_table(tablename, timestamp, deletion_status,
 
409
                person_id,
 
410
                organisation_id,
 
411
                office_id,
 
412
                project_id,
 
413
                Field("title"),
 
414
                Field("manager_id", db.pr_person),
 
415
                Field("focal_point", "boolean"),
 
416
                #Field("slots", "integer", default=1),
 
417
                #Field("payrate", "double", default=0.0), # Wait for Bugeting integration
 
418
                shn_comments_field,
 
419
                migrate=migrate)
 
420
 
 
421
# Field settings
 
422
# Over-ride the default IS_NULL_OR as Staff doesn't make sense without an associated Organisation
 
423
table.organisation_id.requires = IS_ONE_OF(db, "org_organisation.id", "%(name)s")
 
424
table.manager_id.requires = IS_NULL_OR(IS_ONE_OF(db, "pr_person.id", shn_pr_person_represent))
 
425
table.manager_id.represent = lambda id: (id and [shn_pr_person_represent(id)] or ["None"])[0]
 
426
 
 
427
# Staff Resource called from multiple controllers
 
428
# - so we define strings in the model
 
429
table.person_id.label = T("Person")
 
430
#table.person_id.comment = DIV(SPAN("*", _class="req"), shn_person_comment)
 
431
table.organisation_id.comment = DIV(SPAN("*", _class="req"), shn_organisation_comment)
 
432
table.title.label = T("Job Title")
 
433
table.title.comment = DIV( _class="tooltip", _title=Tstr("Title") + "|" + Tstr("The Role this person plays within this Office/Project."))
 
434
table.manager_id.label = T("Manager")
 
435
table.manager_id.comment = DIV( _class="tooltip", _title=Tstr("Manager") + "|" + Tstr("The person's manager within this Office/Project."))
 
436
table.focal_point.comment = DIV( _class="tooltip", _title=Tstr("Focal Point") + "|" + Tstr("The contact person for this organization."))
 
437
# CRUD strings
 
438
ADD_STAFF = T("Add Staff")
 
439
LIST_STAFF = T("List Staff")
 
440
s3.crud_strings[tablename] = Storage(
 
441
    title_create = ADD_STAFF,
 
442
    title_display = T("Staff Details"),
 
443
    title_list = LIST_STAFF,
 
444
    title_update = T("Edit Staff"),
 
445
    title_search = T("Search Staff"),
 
446
    subtitle_create = T("Add New Staff"),
 
447
    subtitle_list = T("Staff"),
 
448
    label_list_button = LIST_STAFF,
 
449
    label_create_button = ADD_STAFF,
 
450
    msg_record_created = T("Staff added"),
 
451
    msg_record_modified = T("Staff updated"),
 
452
    msg_record_deleted = T("Staff deleted"),
 
453
    msg_list_empty = T("No Staff currently registered"))
 
454
 
 
455
# Functions
 
456
def represent_focal_point(is_focal_point):
 
457
    if is_focal_point:
 
458
        return "Focal Point"
 
459
    else:
 
460
        return "-"
 
461
 
 
462
def shn_org_staff_represent(staff_id):
 
463
    person = db((db.org_staff.id == staff_id) &
 
464
                (db.pr_person.id == db.org_staff.person_id)).select(db.pr_person.ALL)
 
465
    if person:
 
466
        return vita.fullname(person[0])
 
467
    else:
 
468
        return None
 
469
 
 
470
table.focal_point.represent = lambda focal_point: represent_focal_point(focal_point)
 
471
 
 
472
def shn_orgs_to_person(person_id):
 
473
    orgs = []
 
474
    if person_id:
 
475
        staff = db((db.org_staff.person_id == person_id) &
 
476
                      (db.org_staff.deleted == False)).select(db.org_staff.organisation_id)
 
477
        if staff:
 
478
            for s in staff:
 
479
                orgs.append(s.organisation_id)
 
480
    return orgs
 
481
 
 
482
# Staff as component of Orgs, Offices & Projects
 
483
s3xrc.model.add_component(module, resource,
 
484
                          multiple=True,
 
485
                          joinby=dict(org_organisation="organisation_id", org_office="office_id", org_project="project_id"),
 
486
                          deletable=True,
 
487
                          editable=True)
 
488
 
 
489
# May wish to over-ride this in controllers
 
490
s3xrc.model.configure(table,
 
491
                      list_fields=["id",
 
492
                                   "person_id",
 
493
                                   "organisation_id",
 
494
                                   "office_id",
 
495
                                   "project_id",
 
496
                                   "title",
 
497
                                   "manager_id",
 
498
                                   "focal_point"
 
499
                                   #"description",
 
500
                                   #"slots",
 
501
                                   #"payrate"
 
502
                                   ])
 
503
 
 
504
# org_position (component of org_project)
 
505
#   describes a position in a project
 
506
#
 
507
# Deprecated - replaced by staff
 
508
#
 
509
#org_position_type_opts = {
 
510
#    1: T("Site Manager"),
 
511
#    2: T("Team Leader"),
 
512
#    3: T("Assistant"),
 
513
#    99: T("Other")
 
514
#}
 
515
#resource = "position"
 
516
#tablename = module + "_" + resource
 
517
#table = db.define_table(tablename, timestamp, uuidstamp, deletion_status,
 
518
#                project_id,
 
519
#                Field("type", "integer",
 
520
#                    requires = IS_IN_SET(org_position_type_opts, zero=None),
 
521
#                    # default = 99,
 
522
#                    label = T("Position type"),
 
523
#                    represent = lambda opt: org_position_type_opts.get(opt, UNKNOWN_OPT)),
 
524
#                Field("title", length=30),
 
525
#                Field("description", "text"),
 
526
#                Field("slots", "integer", default=1),
 
527
#                Field("payrate", "double", default=0.0),
 
528
#                #Field("status")?
 
529
#                migrate=migrate)
 
530
 
 
531
# CRUD Strings
 
532
#ADD_POSITION = Tstr("Add Position")
 
533
#POSITIONS = T("Positions")
 
534
#s3.crud_strings[tablename] = Storage(
 
535
#    title_create = ADD_POSITION,
 
536
#    title_display = T("Position Details"),
 
537
#    title_list = POSITIONS,
 
538
#    title_update = T("Edit Position"),
 
539
#    title_search = T("Search Positions"),
 
540
#    subtitle_create = T("Add New Position"),
 
541
#    subtitle_list = POSITIONS,
 
542
#    label_list_button = T("List Positions"),
 
543
#    label_create_button = ADD_POSITION,
 
544
#    msg_record_created = T("Position added"),
 
545
#    msg_record_modified = T("Position updated"),
 
546
#    msg_record_deleted = T("Position deleted"),
 
547
#    msg_list_empty = T("No positions currently registered"))
 
548
 
 
549
# Reusable field
 
550
#org_position_id = db.Table(None, "org_position_id",
 
551
#                        FieldS3("org_position_id", db.org_position, sortby="title",
 
552
#                        requires = IS_NULL_OR(IS_ONE_OF(db, "org_position.id", "%(title)s")),
 
553
#                        represent = lambda id: lambda id: (id and [db.org_position[id].title] or ["None"])[0],
 
554
#                        comment = DIV(A(ADD_POSITION, _class="colorbox", _href=URL(r=request, c="org", f="project", args="create", vars=dict(format="popup")), _target="top", _title=ADD_POSITION),
 
555
#                                  DIV( _class="tooltip", _title=ADD_POSITION + "|" + Tstr("Add new position."))),
 
556
#                        ondelete = "RESTRICT"
 
557
#                        ))
 
558
 
 
559
# -----------------------------------------------------------------------------
 
560
# org_task:
 
561
#   a task within a project
 
562
#
 
563
org_task_status_opts = {
 
564
    1: T("new"),
 
565
    2: T("assigned"),
 
566
    3: T("completed"),
 
567
    4: T("postponed"),
 
568
    5: T("feedback"),
 
569
    6: T("cancelled"),
 
570
    99: T("unspecified")
 
571
}
 
572
 
 
573
org_task_priority_opts = {
 
574
    4: T("normal"),
 
575
    1: T("immediately"),
 
576
    2: T("urgent"),
 
577
    3: T("high"),
 
578
    5: T("low")
 
579
}
 
580
 
 
581
resource = "task"
 
582
tablename = module + "_" + resource
 
583
table = db.define_table(tablename, timestamp, uuidstamp, deletion_status,
 
584
                project_id,
 
585
                office_id,
 
586
                Field("priority", "integer",
 
587
                    requires = IS_IN_SET(org_task_priority_opts, zero=None),
 
588
                    # default = 4,
 
589
                    label = T("Priority"),
 
590
                    represent = lambda opt: org_task_priority_opts.get(opt, UNKNOWN_OPT)),
 
591
                Field("subject", length=80),
 
592
                Field("description", "text"),
 
593
                person_id,
 
594
                Field("status", "integer",
 
595
                    requires = IS_IN_SET(org_task_status_opts, zero=None),
 
596
                    # default = 1,
 
597
                    label = T("Status"),
 
598
                    represent = lambda opt: org_task_status_opts.get(opt, UNKNOWN_OPT)),
 
599
                migrate=migrate)
 
600
 
 
601
# Task Resource called from multiple controllers
 
602
# - so we define strings in the model
 
603
table.person_id.label = T("Assigned to")
 
604
# CRUD Strings
 
605
ADD_TASK = T("Add Task")
 
606
LIST_TASKS = T("List Tasks")
 
607
s3.crud_strings[tablename] = Storage(
 
608
    title_create = ADD_TASK,
 
609
    title_display = T("Task Details"),
 
610
    title_list = LIST_TASKS,
 
611
    title_update = T("Edit Task"),
 
612
    title_search = T("Search Tasks"),
 
613
    subtitle_create = T("Add New Task"),
 
614
    subtitle_list = T("Tasks"),
 
615
    label_list_button = LIST_TASKS,
 
616
    label_create_button = ADD_TASK,
 
617
    msg_record_created = T("Task added"),
 
618
    msg_record_modified = T("Task updated"),
 
619
    msg_record_deleted = T("Task deleted"),
 
620
    msg_list_empty = T("No tasks currently registered"))
 
621
 
 
622
# Task as Component of Project, Office, (Organisation to come? via Project? Can't rely on that as multi-Org projects)
 
623
s3xrc.model.add_component(module, resource,
 
624
    multiple=True,
 
625
    joinby=dict(org_project="project_id", org_office="office_id"),
 
626
    deletable=True,
 
627
    editable=True,
 
628
    main="subject", extra="description")
 
629
 
 
630
s3xrc.model.configure(table,
 
631
                      list_fields=["id",
 
632
                                   "project_id",
 
633
                                   "office_id",
 
634
                                   "priority",
 
635
                                   "subject",
 
636
                                   "person_id",
 
637
                                   "status"])
 
638
                                   
 
639
# -----------------------------------------------------------------------------
 
640
# shn_project_search_location:
 
641
#   form function to search projects by location
 
642
#
 
643
def shn_project_search_location(xrequest, **attr):
 
644
 
 
645
    if attr is None:
 
646
        attr = {}
 
647
 
 
648
    if not shn_has_permission("read", db.org_project):
 
649
        session.error = UNAUTHORISED
 
650
        redirect(URL(r=request, c="default", f="user", args="login", vars={"_next":URL(r=request, args="search_location", vars=request.vars)}))
 
651
 
 
652
    if xrequest.representation == "html":
 
653
        # Check for redirection
 
654
        if request.vars._next:
 
655
            next = str.lower(request.vars._next)
 
656
        else:
 
657
            next = str.lower(URL(r=request, c="org", f="project", args="[id]"))
 
658
 
 
659
        # Custom view
 
660
        response.view = "%s/project_search.html" % xrequest.prefix
 
661
 
 
662
        # Title and subtitle
 
663
        title = T("Search for a Project")
 
664
        subtitle = T("Matching Records")
 
665
 
 
666
        # Select form:
 
667
        l_opts = [OPTION(_value="")]
 
668
        l_opts += [OPTION(location.name, _value=location.id)
 
669
                for location in db(db.gis_location.deleted == False).select(db.gis_location.ALL, cache=(cache.ram, 3600))]
 
670
        form = FORM(TABLE(
 
671
                TR(T("Location: "),
 
672
                SELECT(_name="location", *l_opts, **dict(name="location", requires=IS_NULL_OR(IS_IN_DB(db, "gis_location.id"))))),
 
673
                TR("", INPUT(_type="submit", _value="Search"))
 
674
                ))
 
675
 
 
676
        output = dict(title=title, subtitle=subtitle, form=form, vars=form.vars)
 
677
 
 
678
        # Accept action
 
679
        items = None
 
680
        if form.accepts(request.vars, session):
 
681
 
 
682
            table = db.org_project
 
683
            query = (table.deleted == False)
 
684
 
 
685
            if form.vars.location is None:
 
686
                results = db(query).select(table.ALL)
 
687
            else:
 
688
                query = query & (table.location_id == form.vars.location)
 
689
                results = db(query).select(table.ALL)
 
690
 
 
691
            if results and len(results):
 
692
                records = []
 
693
                for result in results:
 
694
                    href = next.replace("%5bid%5d", "%s" % result.id)
 
695
                    records.append(TR(
 
696
                        A(result.name, _href=href),
 
697
                        result.start_date or "None",
 
698
                        result.end_date or "None",
 
699
                        result.description or "None",
 
700
                        result.status and org_project_status_opts[result.status] or "unknown",
 
701
                        ))
 
702
                items=DIV(TABLE(THEAD(TR(
 
703
                    TH("ID"),
 
704
                    TH("Organization"),
 
705
                    TH("Location"),
 
706
                    TH("Sector(s)"),
 
707
                    TH("Code"),
 
708
                    TH("Name"),
 
709
                    TH("Status"),
 
710
                    TH("Start date"),
 
711
                    TH("End date"),
 
712
                    TH("Budgeted Cost"))),
 
713
                    TBODY(records), _id="list", _class="display"))
 
714
            else:
 
715
                    items = T("None")
 
716
 
 
717
        try:
 
718
            label_create_button = s3.crud_strings["org_project"].label_create_button
 
719
        except:
 
720
            label_create_button = s3.crud_strings.label_create_button
 
721
 
 
722
        add_btn = A(label_create_button, _href=URL(r=request, f="project", args="create"), _class="action-btn")
 
723
 
 
724
        output.update(dict(items=items, add_btn=add_btn))
 
725
 
 
726
        return output
 
727
 
 
728
    else:
 
729
        session.error = BADFORMAT
 
730
        redirect(URL(r=request))
 
731
 
 
732
# Plug into REST controller
 
733
s3xrc.model.set_method(module, "project", method="search_location", action=shn_project_search_location )
 
734
 
 
735
# -----------------------------------------------------------------------------
 
736
def shn_project_rheader(jr, tabs=[]):
 
737
 
 
738
    if jr.representation == "html":
 
739
        
 
740
        rheader_tabs = shn_rheader_tabs(jr, tabs)
 
741
        
 
742
        if jr.name == "project":
 
743
 
 
744
            _next = jr.here()
 
745
            _same = jr.same()
 
746
 
 
747
            project = jr.record
 
748
            
 
749
            sectors = TABLE()
 
750
            if project.sector_id:
 
751
                _sectors = re.split("\|", project.sector_id)[1:-1]
 
752
                for sector in _sectors:
 
753
                    sectors.append(TR(db(db.org_sector.id == sector).select(db.org_sector.name, limitby=(0, 1)).first().name))
 
754
 
 
755
            if project:
 
756
                rheader = DIV(TABLE(
 
757
                    TR(
 
758
                        TH(T("Code: ")),
 
759
                        project.code,
 
760
                        TH(A(T("Clear Selection"),
 
761
                            _href=URL(r=request, f="project", args="clear", vars={"_next": _same})))
 
762
                        ),
 
763
                    TR(
 
764
                        TH(T("Name: ")),
 
765
                        project.name,
 
766
                        TH(T("Location: ")),
 
767
                        location_id.location_id.represent(project.location_id),
 
768
                        ),
 
769
                    TR(
 
770
                        TH(T("Status: ")),
 
771
                        "%s" % org_project_status_opts[project.status],
 
772
                        TH(T("Sector(s): ")),
 
773
                        sectors
 
774
                        #TH(A(T("Edit Project"),
 
775
                        #    _href=URL(r=request, f="project", args=[jr.id, "update"], vars={"_next": _next})))
 
776
                        )
 
777
                ), rheader_tabs)
 
778
                return rheader
 
779
 
 
780
    return None
 
781
 
 
782
# -----------------------------------------------------------------------------