Package Camelot :: Package camelot :: Package types |
[frames] | no frames]

# Source Code for Package Camelot.camelot.types

      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  """

     29  Camelot extends the SQLAlchemy field types with a number of its own field

     30  types. Those field types are automatically mapped to a specific delegate taking

     31  care of the visualisation.

     32

     33  Those fields are stored in the :mod:`camelot.types` module.

     34  """
     35

     36  import logging
     37  logger = logging.getLogger('camelot.types')
     38

     39  from sqlalchemy import types
     40

     41  from camelot.core.files.storage import StoredFile, StoredImage, Storage
     42

     43 -class VirtualAddress(types.TypeDecorator):
     44      """A single field that can be used to enter phone numbers, fax numbers, email

     45      addresses, im addresses.  The editor provides soft validation of the data

     46      entered.  The address or number is stored as a string in the database.

     47

     48      This column type accepts and returns tuples of strings, the first string is

     49      the :attr:`virtual_address_type`, and the second the address itself.

     50

     51      eg: ``('email','project-camelot@conceptive.be')`` is stored as

     52      ``mail://project-camelot@conceptive.be``

     53

     54      .. image:: ../_static/virtualaddress_editor.png

     55      """
     56

     57      impl = types.Unicode
     58      virtual_address_types = ['phone', 'fax', 'mobile', 'email', 'im', 'pager',]
     59

     60 -    def bind_processor(self, dialect):
     61

     62          impl_processor = self.impl.bind_processor(dialect)
     63          if not impl_processor:
     64              impl_processor = lambda x:x
     65

     66          def processor(value):
     67              if value is not None:
     68                  if value[1]:
     69                      value = u'://'.join(value)
     70                  else:
     71                      value = None
     72              return impl_processor(value)
     73

     74          return processor
     75

     76 -    def result_processor(self, dialect):
     77

     78          impl_processor = self.impl.result_processor(dialect)
     79          if not impl_processor:
     80              impl_processor = lambda x:x
     81

     82          def processor(value):
     83

     84              if value:
     85                  split = value.split('://')
     86                  if len(split)>1:
     87                      return tuple(split)
     88              return (u'phone',u'')
     89

     90          return processor
     91

     92

     93 -class Code(types.TypeDecorator):
     94      """SQLAlchemy column type to store codes.  Where a code is a list of strings

     95      on which a regular expression can be enforced.

     96

     97      This column type accepts and returns a list of strings and stores them as a

     98      string joined with points.

     99

    100      eg: ``['08', 'AB']`` is stored as ``08.AB``

    101

    102      .. image:: ../_static/editors/CodeEditor_editable.png

    103      """
    104

    105      impl = types.Unicode
    106

    107 -    def __init__(self, parts, separator=u'.', **kwargs):
    108          """

    109          :param parts: a list of input masks specifying the mask for each part,

    110          eg ``['99', 'AA']``. For valid input masks, see

    111          `QLineEdit <http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qlineedit.html>`_

    112          """
    113          import string
    114          translator = string.maketrans('', '')
    115          self.parts = parts
    116          self.separator = separator
    117          max_length = sum(len(part.translate(translator, '<>!')) for part in parts) + len(parts)*len(self.separator)
    118          types.TypeDecorator.__init__(self, length=max_length, **kwargs)
    119

    120 -    def bind_processor(self, dialect):
    121

    122          impl_processor = self.impl.bind_processor(dialect)
    123          if not impl_processor:
    124              impl_processor = lambda x:x
    125

    126          def processor(value):
    127              if value is not None:
    128                  value = self.separator.join(value)
    129              return impl_processor(value)
    130

    131          return processor
    132

    133 -    def result_processor(self, dialect):
    134

    135          impl_processor = self.impl.result_processor(dialect)
    136          if not impl_processor:
    137              impl_processor = lambda x:x
    138

    139          def processor(value):
    140

    141              if value:
    142                  return value.split(self.separator)
    143              return ['' for _p in self.parts]
    144

    145          return processor
    146

    147 -class IPAddress(Code):
    148 -    def __init__(self, **kwargs):
    149          super(IPAddress, self).__init__(parts=['900','900','900','900'])
    150

    151 -class Rating(types.TypeDecorator):
    152      """The rating field is an integer field that is visualized as a number of stars that

    153    can be selected::

    154

    155      class Movie(Entity):

    156        title = Field(Unicode(60), required=True)

    157        rating = Field(camelot.types.Rating())

    158

    159    .. image:: ../_static/editors/StarEditor_editable.png

    160  """
    161

    162      impl = types.Integer
    163

    164 -class RichText(types.TypeDecorator):
    165      """RichText fields are unlimited text fields which contain html.  The html will be

    166    rendered in a rich text editor.

    167

    168      .. image:: ../_static/editors/RichTextEditor_editable.png

    169  """
    170

    171      impl = types.UnicodeText
    172

    173 -class Language(types.TypeDecorator):
    174      """The languages are stored as ISO codes in the database

    175      """
    176

    177      impl = types.Unicode
    178      choices = []
    179

    180      _localedata_list = []
    181

    182      try:
    183          import babel
    184          _localedata_list = babel.localedata.list()
    185      except Exception, e:
    186          logger.warn(u'Could not load list with languages from babel, list will be empty', exc_info=e)
    187      for code in _localedata_list:
    188          locale = babel.Locale(code)
    189          display_name = locale.get_display_name()
    190          if display_name:
    191              choices.append((code, u'%s (%s)'%(code, display_name)))
    192          else:
    193              choices.append((code, code))
    194

    195 -    def __init__(self):
    196          types.TypeDecorator.__init__(self, length=20)
    197

    198 -class Color(types.TypeDecorator):
    199      """The Color field returns and accepts tuples of the form (r,g,b,a) where

    200    r,g,b,a are integers between 0 and 255. The color is stored as an hexadecimal

    201    string of the form AARRGGBB into the database, where AA is the transparency,

    202    RR is red, GG is green BB is blue::

    203

    204      class MovieType(Entity):

    205        color = Field(camelot.types.Color())

    206

    207    .. image:: ../_static/editors/ColorEditor_editable.png

    208

    209    The colors are stored in the database as strings

    210      """
    211

    212      impl = types.Unicode
    213

    214 -    def __init__(self):
    215          types.TypeDecorator.__init__(self, length=8)
    216

    217 -    def bind_processor(self, dialect):
    218

    219          impl_processor = self.impl.bind_processor(dialect)
    220          if not impl_processor:
    221              impl_processor = lambda x:x
    222

    223          def processor(value):
    224              if value is not None:
    225                  assert len(value) == 4
    226                  for i in range(4):
    227                      assert value[i] >= 0
    228                      assert value[i] <= 255
    229                  return '%02X%02X%02X%02X'%(value[3], value[0], value[1], value[2])
    230              return impl_processor(value)
    231

    232          return processor
    233

    234 -    def result_processor(self, dialect):
    235

    236          impl_processor = self.impl.result_processor(dialect)
    237          if not impl_processor:
    238              impl_processor = lambda x:x
    239

    240          def processor(value):
    241

    242              if value:
    243                  return (int(value[2:4],16), int(value[4:6],16), int(value[6:8],16), int(value[0:2],16))
    244

    245          return processor
    246

    247 -class Enumeration(types.TypeDecorator):
    248      """The enumeration field stores integers in the database, but represents them as

    249    strings.  This allows efficient storage and querying while preserving readable code.

    250

    251    Typical use of this field would be a status field.

    252

    253    Enumeration fields are visualized as a combo box, where the labels in the combo

    254    box are the capitalized strings::

    255

    256      class Movie(Entity):

    257        title = Field(Unicode(60), required=True)

    258        state = Field(camelot.types.Enumeration([(1,'planned'), (2,'recording'), (3,'finished'), (4,'canceled')]),

    259                                                index=True, required=True, default='planning')

    260

    261    .. image:: ../_static/editors/ChoicesEditor_editable.png

    262    """
    263

    264      impl = types.Integer
    265

    266 -    def __init__(self, choices=[], **kwargs):
    267          """

    268          @param param: choices is a list of tuples.  each tuple contains an integer and its

    269          associated string.  eg. : choices = [(1,'draft'), (2,'approved')]

    270          """
    271          types.TypeDecorator.__init__(self, **kwargs)
    272          self._int_to_string = dict(choices)
    273          self._string_to_int = dict((v,k) for (k,v) in choices)
    274          self.choices = [v for (k,v) in choices]
    275

    276 -    def bind_processor(self, dialect):
    277

    278          impl_processor = self.impl.bind_processor(dialect)
    279          if not impl_processor:
    280              impl_processor = lambda x:x
    281

    282          def processor(value):
    283              if value is not None:
    284                  try:
    285                      value = self._string_to_int[value]
    286                      return impl_processor(value)
    287                  except KeyError, e:
    288                      logger.error('could not process enumeration value %s, possible values are %s'%(value, u', '.join(list(self._string_to_int.keys()))), exc_info=e)
    289

    290          return processor
    291

    292 -    def result_processor(self, dialect):
    293

    294          impl_processor = self.impl.result_processor(dialect)
    295          if not impl_processor:
    296              impl_processor = lambda x:x
    297

    298          def processor(value):
    299              if value is not None:
    300                  value = impl_processor(value)
    301                  try:
    302                      return self._int_to_string[value]
    303                  except KeyError, e:
    304                      logger.error('could not process %s'%value, exc_info=e)
    305

    306          return processor
    307

    308 -class File(types.TypeDecorator):
    309      """Sqlalchemy column type to store files.  Only the location of the file is stored

    310

    311    This column type accepts and returns a StoredFile, and stores them in the directory

    312    specified by settings.MEDIA_ROOT.  The name of the file is stored as a string in

    313    the database.  A subdirectory upload_to can be specified::

    314

    315      class Movie(Entity):

    316        script = Field(camelot.types.File(upload_to='script'))

    317

    318    .. image:: ../_static/editors/FileEditor_editable.png

    319      """
    320

    321      impl = types.Unicode
    322      stored_file_implementation = StoredFile
    323

    324 -    def __init__(self, max_length=100, upload_to='', storage=Storage, **kwargs):
    325          self.max_length = max_length
    326          self.storage = storage(upload_to, self.stored_file_implementation)
    327          types.TypeDecorator.__init__(self, length=max_length, **kwargs)
    328

    329 -    def bind_processor(self, dialect):
    330

    331          impl_processor = self.impl.bind_processor(dialect)
    332          if not impl_processor:
    333              impl_processor = lambda x:x
    334

    335          def processor(value):
    336              if value is not None:
    337                  assert isinstance(value, (self.stored_file_implementation))
    338                  return impl_processor(value.name)
    339              return impl_processor(value)
    340

    341          return processor
    342

    343 -    def result_processor(self, dialect):
    344

    345          impl_processor = self.impl.result_processor(dialect)
    346          if not impl_processor:
    347              impl_processor = lambda x:x
    348

    349          def processor(value):
    350

    351              if value:
    352                  value = impl_processor(value)
    353                  return self.stored_file_implementation(self.storage, value)
    354

    355          return processor
    356

    357 -class Image(File):
    358      """Sqlalchemy column type to store images

    359

    360    This column type accepts and returns a StoredImage, and stores them in the directory

    361    specified by settings.MEDIA_ROOT.  The name of the file is stored as a string in

    362    the database.

    363

    364    The Image field type provides the same functionallity as the File field type, but

    365    the files stored should be images.

    366

    367    .. image:: ../_static/editors/ImageEditor_editable.png

    368      """
    369

    370      stored_file_implementation = StoredImage
    371