Package Camelot :: Package camelot :: Package model :: Module authentication
[frames] | no frames]

Source Code for Module Camelot.camelot.model.authentication

  1  #  ============================================================================
 
  2  #
 
  3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.
 
  4  #  www.conceptive.be / project-camelot@conceptive.be
 
  5  #
 
  6  #  This file is part of the Camelot Library.
 
  7  #
 
  8  #  This file may be used under the terms of the GNU General Public
 
  9  #  License version 2.0 as published by the Free Software Foundation
 
 10  #  and appearing in the file LICENSE.GPL included in the packaging of
 
 11  #  this file.  Please review the following information to ensure GNU
 
 12  #  General Public Licensing requirements will be met:
 
 13  #  http://www.trolltech.com/products/qt/opensource.html
 
 14  #
 
 15  #  If you are unsure which license is appropriate for your use, please
 
 16  #  review the following information:
 
 17  #  http://www.trolltech.com/products/qt/licensing.html or contact
 
 18  #  project-camelot@conceptive.be.
 
 19  #
 
 20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
 21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
 22  #
 
 23  #  For use of this library in commercial applications, please contact
 
 24  #  project-camelot@conceptive.be
 
 25  #
 
 26  #  ============================================================================
 
 27  
 
 28  from camelot.model import metadata 
 29  from camelot.model import entities 
 30  from camelot.view.controls import delegates 
 31  
 
 32  from elixir.entity import Entity 
 33  from elixir.options import using_options 
 34  from elixir.fields import Field 
 35  from sqlalchemy.types import Date, Unicode, Integer, DateTime, Boolean 
 36  from elixir.relationships import ManyToOne, OneToMany 
 37  from elixir.properties import ColumnProperty 
 38  from sqlalchemy.sql.expression import and_ 
 39  """Set of classes to store persons, organizations, relationships and
 
 40  permissions
 
 41  
 
 42  These structures are modeled like described in 'The Data Model Resource Book'
 
 43  by Len Silverston, Chapter 2
 
 44  """ 
 45  from sqlalchemy import sql 
 46  
 
 47  import camelot.types 
 48  
 
 49  #from camelot.model import *
 
 50  
 
 51  __metadata__ = metadata 
 52  
 
 53  from camelot.model.synchronization import is_synchronized 
 54  from camelot.core.document import documented_entity 
 55  from camelot.core.utils import ugettext_lazy as _ 
 56  
 
 57  from camelot.view.elixir_admin import EntityAdmin 
 58  from camelot.view.forms import Form, TabForm, HBoxForm, WidgetOnlyForm 
 59  from camelot.admin.form_action import FormActionFromModelFunction 
 60  import datetime 
 61  import threading 
 62  
 
 63  
 
 64  _current_authentication_ = threading.local() 
65 66 -def end_of_times():
67 return datetime.date( year = 2400, month = 12, day = 31 )
68 69 from camelot.model.type_and_status import type_3_status
70 71 -def getCurrentAuthentication():
72 """Get the currently logged in person""" 73 global _current_authentication_ 74 if not hasattr( _current_authentication_, 'mechanism' ): 75 import getpass 76 _current_authentication_.mechanism = UsernameAuthenticationMechanism.getOrCreateAuthentication( unicode( getpass.getuser() ) ) 77 return _current_authentication_.mechanism
78
79 -def updateLastLogin():
80 """Update the last login of the current person to now""" 81 from elixir import session 82 authentication = getCurrentAuthentication() 83 authentication.last_login = datetime.datetime.now() 84 session.flush( [authentication] )
85
86 -class PartyRelationship( Entity ):
87 using_options( tablename = 'party_relationship' ) 88 from_date = Field( Date(), default = datetime.date.today, required = True, index = True ) 89 thru_date = Field( Date(), default = end_of_times, required = True, index = True ) 90 comment = Field( camelot.types.RichText() ) 91 is_synchronized( 'synchronized', lazy = True ) 92
93 - class Admin( EntityAdmin ):
94 verbose_name = _('Relationship') 95 verbose_name_plural = _('Relationships') 96 list_display = ['established_from', 'established_to', 'from_date', 'thru_date']
97
98 -class EmployerEmployee( PartyRelationship ):
99 """Relation from employer to employee""" 100 using_options( tablename = 'party_relationship_empl', inheritance = 'multi' ) 101 established_from = ManyToOne( 'Organization', required = True, ondelete = 'cascade', onupdate = 'cascade' ) # the employer 102 established_to = ManyToOne( 'Person', required = True, ondelete = 'cascade', onupdate = 'cascade' ) # the employee 103 104 @ColumnProperty
105 - def first_name( self ):
106 return sql.select( [Person.first_name], Person.party_id == self.established_to_party_id )
107 108 @ColumnProperty
109 - def last_name( self ):
110 return sql.select( [Person.last_name], Person.party_id == self.established_to_party_id )
111 112 @ColumnProperty
113 - def social_security_number( self ):
114 return sql.select( [Person.social_security_number], Person.party_id == self.established_to_party_id )
115
116 - def __unicode__( self ):
117 return u'%s %s %s' % ( unicode( self.established_to ), _('Employed by'),unicode( self.established_from ) )
118
119 - class Admin( PartyRelationship.Admin ):
120 verbose_name = _('Employment relation') 121 verbose_name_plural = _('Employment relations') 122 list_filter = ['established_from.name'] 123 list_search = ['established_from.name', 'established_to.first_name', 'established_to.last_name']
124
125 - class EmployeeAdmin( EntityAdmin ):
126 verbose_name = _('Employee') 127 list_display = ['established_to', 'from_date', 'thru_date'] 128 form_display = ['established_to', 'comment', 'from_date', 'thru_date'] 129 field_attributes = {'established_to':{'name':_( 'Name' )}}
130
131 - class EmployerAdmin( EntityAdmin ):
132 verbose_name = _('Employer') 133 list_display = ['established_from', 'from_date', 'thru_date'] 134 form_display = ['established_from', 'comment', 'from_date', 'thru_date'] 135 field_attributes = {'established_from':{'name':_( 'Name' )}}
136
137 -class DirectedDirector( PartyRelationship ):
138 """Relation from a directed organization to a director""" 139 using_options( tablename = 'party_relationship_dir', inheritance = 'multi' ) 140 established_from = ManyToOne( 'Organization', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 141 established_to = ManyToOne( 'Party', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 142 title = Field( Unicode( 256 ) ) 143 represented_by = OneToMany( 'RepresentedRepresentor', inverse = 'established_to' ) 144
145 - class Admin( PartyRelationship.Admin ):
146 verbose_name = _('Direction structure') 147 verbose_name_plural = _('Direction structures') 148 list_display = ['established_from', 'established_to', 'title', 'represented_by'] 149 list_search = ['established_from.full_name', 'established_to.full_name'] 150 field_attributes = {'established_from':{'name':_('Organization')}, 151 'established_to':{'name':_('Director')}}
152
153 - class DirectorAdmin( Admin ):
154 verbose_name = _('Director') 155 list_display = ['established_to', 'title', 'from_date', 'thru_date'] 156 form_display = ['established_to', 'title', 'from_date', 'thru_date', 'represented_by', 'comment']
157
158 - class DirectedAdmin( Admin ):
159 verbose_name = _('Directed organization') 160 list_display = ['established_from', 'title', 'from_date', 'thru_date'] 161 form_display = ['established_from', 'title', 'from_date', 'thru_date', 'represented_by', 'comment']
162
163 -class RepresentedRepresentor( Entity ):
164 """Relation from a representing party to the person representing the party""" 165 using_options( tablename = 'party_representor' ) 166 from_date = Field( Date(), default = datetime.date.today, required = True, index = True ) 167 thru_date = Field( Date(), default = end_of_times, required = True, index = True ) 168 comment = Field( camelot.types.RichText() ) 169 established_from = ManyToOne( 'Person', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 170 established_to = ManyToOne( 'DirectedDirector', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 171
172 - class Admin( EntityAdmin ):
173 verbose_name = _('Represented by') 174 list_display = ['established_from', 'from_date', 'thru_date'] 175 form_display = ['established_from', 'from_date', 'thru_date', 'comment'] 176 field_attributes = {'established_from':{'name':_( 'Name' )}}
177
178 -class SupplierCustomer( PartyRelationship ):
179 """Relation from supplier to customer""" 180 using_options( tablename = 'party_relationship_suppl', inheritance = 'multi' ) 181 established_from = ManyToOne( 'Party', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 182 established_to = ManyToOne( 'Party', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 183
184 - class Admin( PartyRelationship.Admin ):
185 verbose_name = _('Supplier - Customer') 186 list_display = ['established_from', 'established_to', 'from_date', 'thru_date']
187
188 - class CustomerAdmin( EntityAdmin ):
189 verbose_name = _('Customer') 190 list_display = ['established_to', ] 191 form_display = ['established_to', 'comment', 'from_date', 'thru_date'] 192 field_attributes = {'established_to':{'name':_( 'Name' )}}
193
194 - class SupplierAdmin( EntityAdmin ):
195 verbose_name = _('Supplier') 196 list_display = ['established_from', ] 197 form_display = ['established_from', 'comment', 'from_date', 'thru_date'] 198 field_attributes = {'established_from':{'name':_( 'Name' )}}
199
200 -class SharedShareholder( PartyRelationship ):
201 """Relation from a shared organization to a shareholder""" 202 using_options( tablename = 'party_relationship_shares', inheritance = 'multi' ) 203 established_from = ManyToOne( 'Organization', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 204 established_to = ManyToOne( 'Party', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 205 shares = Field( Integer() ) 206
207 - class Admin( PartyRelationship.Admin ):
208 verbose_name = _('Shareholder structure') 209 verbose_name_plural = _('Shareholder structures') 210 list_display = ['established_from', 'established_to', 'shares',] 211 list_search = ['established_from.full_name', 'established_to.full_name'] 212 field_attributes = {'established_from':{'name':_('Organization')}, 213 'established_to':{'name':_('Shareholder')}}
214
215 - class ShareholderAdmin( Admin ):
216 verbose_name = _('Shareholder') 217 list_display = ['established_to', 'shares', 'from_date', 'thru_date'] 218 form_display = ['established_to', 'shares', 'from_date', 'thru_date', 'comment'] 219 form_size = (500, 300)
220
221 - class SharedAdmin( Admin ):
222 verbose_name = _('Shares') 223 verbose_name_plural = _('Shares') 224 list_display = ['established_from', 'shares', 'from_date', 'thru_date'] 225 form_display = ['established_from', 'shares', 'from_date', 'thru_date', 'comment'] 226 form_size = (500, 300)
227
228 -class AddressAdmin( EntityAdmin ):
229 """Admin with only the Address information and not the Party information""" 230 list_display = ['address', 'comment'] 231 form_display = ['address', 'comment', 'from_date', 'thru_date']
232
233 -class PartyContactMechanismAdmin( EntityAdmin ):
234 form_size = ( 700, 200 ) 235 verbose_name = _('Contact mechanism') 236 verbose_name_plural = _('Contact mechanisms') 237 list_search = ['party_name', 'mechanism'] 238 list_display = ['party_name', 'mechanism', 'comment', 'from_date', ] 239 form_display = Form( ['contact_mechanism', 'comment', 'from_date', 'thru_date', ] ) 240 field_attributes = {'party_name':{'minimal_column_width':25, 'editable':False}, 241 'mechanism':{'minimal_column_width':25,'editable':False}}
242
243 -class PartyPartyContactMechanismAdmin( PartyContactMechanismAdmin ):
244 list_search = ['party_name', 'mechanism'] 245 list_display = ['contact_mechanism', 'comment', 'from_date', ]
246
247 -class Party( Entity ):
248 """Base class for persons and organizations. Use this base class to refer to either persons or 249 organisations in building authentication systems, contact management or CRM""" 250 using_options( tablename = 'party' ) 251 is_synchronized( 'synchronized', lazy = True ) 252 addresses = OneToMany( 'PartyAddress', lazy = True, cascade="all, delete, delete-orphan" ) 253 contact_mechanisms = OneToMany( 'PartyContactMechanism', lazy = True, cascade='all, delete, delete-orphan' ) 254 shares = OneToMany( 'SharedShareholder', inverse = 'established_to', cascade='all, delete, delete-orphan' ) 255 directed_organizations = OneToMany( 'DirectedDirector', inverse = 'established_to', cascade='all, delete, delete-orphan' ) 256 status = OneToMany( type_3_status( 'Party', metadata, entities ), cascade='all, delete, delete-orphan' ) 257 258 @property
259 - def name( self ):
260 return ''
261 262 @ColumnProperty
263 - def email( self ):
264 265 cm = ContactMechanism 266 pcm = PartyContactMechanism 267 268 return sql.select( [cm.mechanism], 269 whereclause = and_( pcm.table.c.party_id == self.id, 270 cm.table.c.mechanism.like( ( u'email', u'%' ) ) ), 271 from_obj = [cm.table.join( pcm.table )] ).limit(1)
272 273 @ColumnProperty
274 - def phone( self ):
275 276 cm = ContactMechanism 277 pcm = PartyContactMechanism 278 279 return sql.select( [cm.mechanism], 280 whereclause = and_( pcm.table.c.party_id == self.id, 281 cm.table.c.mechanism.like( ( u'phone', u'%' ) ) ), 282 from_obj = [cm.table.join( pcm.table )] ).limit(1)
283 284 @ColumnProperty
285 - def full_name( self ):
286 aliased_organisation = Organization.table.alias( 'organisation_alias' ) 287 aliased_person = Person.table.alias( 'person_alias' ) 288 aliased_party = Party.table.alias( 'party_alias' ) 289 return sql.functions.coalesce( sql.select( [sql.functions.coalesce(aliased_person.c.first_name,'') + ' ' + sql.functions.coalesce(aliased_person.c.last_name, '')], 290 whereclause = and_( aliased_party.c.id == self.id ), 291 from_obj = [aliased_party.join( aliased_person, aliased_person.c.party_id == aliased_party.c.id )] ).limit( 1 ).as_scalar(), 292 sql.select( [aliased_organisation.c.name], 293 whereclause = and_( aliased_party.c.id == self.id ), 294 from_obj = [aliased_party.join( aliased_organisation, aliased_organisation.c.party_id == aliased_party.c.id )] ).limit( 1 ).as_scalar() )
295
296 - class Admin( EntityAdmin ):
297 verbose_name = _('Party') 298 verbose_name_plural = _('Parties') 299 list_display = ['name', 'email', 'phone'] # don't use full name, since it might be None for new objects 300 list_search = ['full_name'] 301 form_display = ['addresses', 'contact_mechanisms', 'shares', 'directed_organizations'] 302 field_attributes = dict(addresses = {'admin':AddressAdmin}, 303 contact_mechanisms = {'admin':PartyPartyContactMechanismAdmin}, 304 suppliers = {'admin':SupplierCustomer.SupplierAdmin}, 305 customers = {'admin':SupplierCustomer.CustomerAdmin}, 306 employers = {'admin':EmployerEmployee.EmployerAdmin}, 307 employees = {'admin':EmployerEmployee.EmployeeAdmin}, 308 directed_organizations = {'admin':DirectedDirector.DirectedAdmin}, 309 directors = {'admin':DirectedDirector.DirectorAdmin}, 310 shares = {'admin':SharedShareholder.SharedAdmin}, 311 shareholders = {'admin':SharedShareholder.ShareholderAdmin}, 312 sex = dict( choices = lambda obj:[( u'M', _('male') ), ( u'F', _('female') )], ), 313 name = dict( minimal_column_width = 50 ), 314 email = dict( editable = False, minimal_column_width = 20 ), 315 phone = dict( editable = False, minimal_column_width = 20 ) 316 )
317
318 -class Organization( Party ):
319 """An organization represents any internal or external organization. Organizations can include 320 businesses and groups of individuals""" 321 using_options( tablename = 'organization', inheritance = 'multi' ) 322 name = Field( Unicode( 50 ), required = True, index = True ) 323 logo = Field( camelot.types.Image( upload_to = 'organization-logo' ), deferred = True ) 324 tax_id = Field( Unicode( 20 ) ) 325 directors = OneToMany( 'DirectedDirector', inverse = 'established_from', cascade='all, delete, delete-orphan' ) 326 employees = OneToMany( 'EmployerEmployee', inverse = 'established_from', cascade='all, delete, delete-orphan' ) 327 suppliers = OneToMany( 'SupplierCustomer', inverse = 'established_to', cascade='all, delete, delete-orphan' ) 328 customers = OneToMany( 'SupplierCustomer', inverse = 'established_from', cascade='all, delete, delete-orphan' ) 329 shareholders = OneToMany( 'SharedShareholder', inverse = 'established_from', cascade='all, delete, delete-orphan' ) 330
331 - def __unicode__( self ):
332 return self.name
333 334 @property
335 - def number_of_shares_issued( self ):
336 return sum( ( shareholder.shares for shareholder in self.shareholders ), 0 )
337
338 - class Admin( Party.Admin ):
339 verbose_name = _( 'Organization' ) 340 verbose_name_plural = _( 'Organizations' ) 341 list_display = ['name', 'tax_id', 'email', 'phone'] 342 form_display = TabForm( [( _('Basic'), Form( ['name', 'tax_id', 'addresses', 'contact_mechanisms'] ) ), 343 ( _('Employment'), Form( ['employees'] ) ), 344 ( _('Customers'), Form( ['customers'] ) ), 345 ( _('Suppliers'), Form( ['suppliers'] ) ), 346 ( _('Corporate'), Form( ['directors', 'shareholders', 'shares'] ) ), 347 ( _('Branding'), Form( ['logo'] ) ), 348 ( _('Status'), Form( ['status'] ) ), 349 ] )
350 351 Organization = documented_entity()( Organization )
352 353 -class AuthenticationMechanism( Entity ):
354 using_options( tablename = 'authentication_mechanism' ) 355 last_login = Field( DateTime() ) 356 is_active = Field( Boolean, default = True, index = True ) 357
358 - class Admin( EntityAdmin ):
359 verbose_name = _('Authentication mechanism') 360 list_display = ['last_login', 'is_active']
361
362 -class UsernameAuthenticationMechanism( AuthenticationMechanism ):
363 using_options( tablename = 'authentication_mechanism_username', inheritance = 'multi' ) 364 username = Field( Unicode( 40 ), required = True, index = True, unique = True ) 365 password = Field( Unicode( 200 ), required = False, index = False, default = None ) 366 367 @classmethod
368 - def getOrCreateAuthentication( cls, username ):
369 authentication = cls.query.filter_by( username = username ).first() 370 if not authentication: 371 authentication = cls( username = username ) 372 from elixir import session 373 session.flush( [authentication] ) 374 return authentication
375
376 - def __unicode__( self ):
377 return self.username
378
379 - class Admin( EntityAdmin ):
380 verbose_name = _('Authentication mechanism') 381 list_display = ['username', 'last_login', 'is_active']
382
383 -class Person( Party ):
384 """Person represents natural persons 385 """ 386 using_options( tablename = 'person', inheritance = 'multi' ) 387 first_name = Field( Unicode( 40 ), required = True ) 388 last_name = Field( Unicode( 40 ), required = True ) 389 middle_name = Field( Unicode( 40 ) ) 390 personal_title = Field( Unicode( 10 ) ) 391 suffix = Field( Unicode( 3 ) ) 392 sex = Field( Unicode( 1 ), default = u'M' ) 393 birthdate = Field( Date() ) 394 martial_status = Field( Unicode( 1 ) ) 395 social_security_number = Field( Unicode( 12 ) ) 396 passport_number = Field( Unicode( 20 ) ) 397 passport_expiry_date = Field( Date() ) 398 is_staff = Field( Boolean, default = False, index = True ) 399 is_superuser = Field( Boolean, default = False, index = True ) 400 picture = Field( camelot.types.Image( upload_to = 'person-pictures' ), deferred = True ) 401 comment = Field( camelot.types.RichText() ) 402 employers = OneToMany( 'EmployerEmployee', inverse = 'established_to', cascade='all, delete, delete-orphan' ) 403 404 @property
405 - def note(self):
406 for person in self.__class__.query.filter_by(first_name=self.first_name, last_name=self.last_name): 407 if person!=self: 408 return _('A person with the same name allready exists')
409 410 @property
411 - def name( self ):
412 # we don't use full name in here, because for new objects, full name will be None, since 413 # it needs to be fetched from the db first 414 return u'%s %s' % ( self.first_name, self.last_name )
415
416 - def __unicode__( self ):
417 return self.name
418
419 - class Admin( Party.Admin ):
420 verbose_name = _( 'Person' ) 421 verbose_name_plural = _( 'Persons' ) 422 list_display = ['first_name', 'last_name', 'email', 'phone'] 423 form_display = TabForm( [( _('Basic'), Form( [HBoxForm( [Form( [WidgetOnlyForm('note'), 'first_name', 'last_name', 'sex'] ), 424 Form( ['picture', ] ), 425 ] ), 426 'contact_mechanisms', 'comment', ], scrollbars = False ) ), 427 ( _('Official'), Form( ['birthdate', 'social_security_number', 'passport_number', 428 'passport_expiry_date', 'addresses', ], scrollbars = False ) ), 429 ( _('Work'), Form( ['employers', 'directed_organizations', 'shares'], scrollbars = False ) ), 430 ( _('Status'), Form( ['status'] ) ), 431 ] ) 432 field_attributes = dict( Party.Admin.field_attributes ) 433 field_attributes['note'] = {'delegate':delegates.NoteDelegate}
434 435 Person = documented_entity()( Person )
436 437 -class GeographicBoundary( Entity ):
438 using_options( tablename = 'geographic_boundary' ) 439 code = Field( Unicode( 10 ) ) 440 name = Field( Unicode( 40 ), required = True ) 441 442 @ColumnProperty
443 - def full_name( self ):
444 return self.code + ' ' + self.name
445
446 - def __unicode__( self ):
447 return u'%s %s' % ( self.code, self.name )
448
449 -class Country( GeographicBoundary ):
450 using_options( tablename = 'geographic_boundary_country', inheritance = 'multi' ) 451 452 @classmethod
453 - def getOrCreate( cls, code, name ):
454 country = Country.query.filter_by( code = code ).first() 455 if not country: 456 from elixir import session 457 country = Country( code = code, name = name ) 458 session.flush( [country] ) 459 return country
460
461 - class Admin( EntityAdmin ):
462 form_size = ( 700, 150 ) 463 verbose_name = _('Country') 464 verbose_name_plural = _('Countries') 465 list_display = ['name', 'code']
466
467 -class City( GeographicBoundary ):
468 using_options( tablename = 'geographic_boundary_city', inheritance = 'multi' ) 469 country = ManyToOne( 'Country', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 470 471 @classmethod
472 - def getOrCreate( cls, country, code, name ):
473 city = City.query.filter_by( code = code, country = country ).first() 474 if not city: 475 from elixir import session 476 city = City( code = code, name = name, country = country ) 477 session.flush( [city] ) 478 return city
479
480 - class Admin( EntityAdmin ):
481 verbose_name = _('City') 482 verbose_name_plural = _('Cities') 483 form_size = ( 700, 150 ) 484 list_display = ['code', 'name', 'country']
485
486 -class Address( Entity ):
487 using_options( tablename = 'address' ) 488 street1 = Field( Unicode( 128 ), required = True ) 489 street2 = Field( Unicode( 128 ) ) 490 city = ManyToOne( 'City', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 491 is_synchronized( 'synchronized', lazy = True ) 492 493 @ColumnProperty
494 - def name( self ):
495 return sql.select( [self.street1 + ', ' + GeographicBoundary.full_name], 496 whereclause = (GeographicBoundary.id==self.city_geographicboundary_id))
497 498 @classmethod
499 - def getOrCreate( cls, street1, street2, city ):
500 address = cls.query.filter_by( street1 = street1, street2 = street2, city = city ).first() 501 if not address: 502 from elixir import session 503 address = cls( street1 = street1, street2 = street2, city = city ) 504 session.flush( [address] ) 505 return address
506
507 - def __unicode__( self ):
508 return u'%s, %s' % ( self.street1 or '', self.city or '' )
509
510 - def showMap( self ):
511 from PyQt4 import QtGui, QtCore 512 QtGui.QDesktopServices.openUrl ( QtCore.QUrl( 'http://www.google.be/maps?f=q&source=s_q&geocode=%s&q=%s+%s' % ( self.city.country.code, self.street1, self.city.name ) ) )
513
514 - class Admin( EntityAdmin ):
515 verbose_name = _('Address') 516 verbose_name_plural = _('Addresses') 517 list_display = ['street1', 'street2', 'city'] 518 form_size = ( 700, 150 ) 519 field_attributes = {'street1':{'minimal_column_width':30}} 520 form_actions = [FormActionFromModelFunction( 'Show on map', lambda address:address.showMap() )]
521 522 Address = documented_entity()( Address )
523 524 -class PartyAddress( Entity ):
525 using_options( tablename = 'party_address' ) 526 party = ManyToOne( 'Party', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 527 address = ManyToOne( 'Address', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 528 from_date = Field( Date(), default = datetime.date.today, required = True, index = True ) 529 thru_date = Field( Date(), default = end_of_times, required = True, index = True ) 530 comment = Field( Unicode( 256 ) ) 531 532 @ColumnProperty
533 - def party_name( self ):
534 return sql.select( [Party.full_name], 535 whereclause = (Party.id==self.party_id))
536 537 @ColumnProperty
538 - def address_name( self ):
539 return sql.select( [Address.name], 540 whereclause = (Address.id==self.address_id))
541
542 - def __unicode__( self ):
543 return '%s : %s' % ( unicode( self.party ), unicode( self.address ) )
544
545 - def showMap( self ):
546 if self.address: 547 self.address.showMap()
548
549 - class Admin( EntityAdmin ):
550 verbose_name = _('Address') 551 verbose_name_plural = _('Addresses') 552 list_search = ['party_name', 'address_name'] 553 list_display = ['party_name', 'address_name', 'comment'] 554 form_display = ['party', 'address', 'comment', 'from_date', 'thru_date'] 555 form_size = ( 700, 200 ) 556 form_actions = [FormActionFromModelFunction( 'Show on map', lambda address:address.showMap() )] 557 field_attributes = dict(party_name=dict(editable=False, name='Party', minimal_column_width=30), 558 address_name=dict(editable=False, name='Address', minimal_column_width=30))
559
560 -class PartyAddressRoleType( Entity ):
561 using_options( tablename = 'party_address_role_type' ) 562 code = Field( Unicode( 10 ) ) 563 description = Field( Unicode( 40 ) ) 564
565 - class Admin( EntityAdmin ):
566 verbose_name = _('Address role type') 567 list_display = ['code', 'description']
568
569 -class PartyAuthentication( Entity ):
570 using_options( tablename = 'party_authentication' ) 571 address = ManyToOne( 'AuthenticationMechanism', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 572 from_date = Field( Date(), default = datetime.date.today, required = True, index = True ) 573 thru_date = Field( Date(), default = end_of_times, required = True, index = True ) 574 comment = Field( Unicode( 256 ) )
575
576 -class ContactMechanism( Entity ):
577 using_options( tablename = 'contact_mechanism' ) 578 mechanism = Field( camelot.types.VirtualAddress( 256 ), required = True ) 579 party_address = ManyToOne( 'PartyAddress', ondelete = 'set null', onupdate = 'cascade' ) 580
581 - def __unicode__( self ):
582 if self.mechanism: 583 return u'%s : %s' % ( self.mechanism[0], self.mechanism[1] )
584
585 - class Admin( EntityAdmin ):
586 form_size = ( 700, 150 ) 587 verbose_name = _('Contact mechanism') 588 list_display = ['mechanism'] 589 form_display = Form( ['mechanism', 'party_address'] ) 590 field_attributes = {'mechanism':{'minimal_column_width':25}}
591 592 ContactMechanism = documented_entity()( ContactMechanism )
593 594 -class PartyContactMechanism( Entity ):
595 using_options( tablename = 'party_contact_mechanism' ) 596 party = ManyToOne( 'Party', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 597 contact_mechanism = ManyToOne( 'ContactMechanism', required = True, ondelete = 'cascade', onupdate = 'cascade' ) 598 from_date = Field( Date(), default = datetime.date.today, required = True, index = True ) 599 thru_date = Field( Date(), default = end_of_times, index = True ) 600 comment = Field( Unicode( 256 ) ) 601 602 @ColumnProperty
603 - def mechanism( self ):
604 return sql.select( [ContactMechanism.mechanism], 605 whereclause = (ContactMechanism.id==self.contact_mechanism_id))
606 607 @ColumnProperty
608 - def party_name( self ):
609 return sql.select( [Party.full_name], 610 whereclause = (Party.id==self.party_id))
611
612 - def __unicode__( self ):
613 return unicode( self.contact_mechanism )
614 615 Admin = PartyContactMechanismAdmin
616