Module api
[frames] | no frames]

Source Code for Module api

  1  #!/usr/bin/env python 
  2  ''' 
  3  $Id: api.py 27734 2013-03-13 16:30:16Z dcarlier $ 
  4   
  5  Used to load the recognition tree and perform lookups of all properties, or 
  6  individual properties. Typical usage is as follows:  
  7   
  8      >>> import api 
  9      >>> dir(api) 
 10      ['DaApi', 'IncorrectPropertyTypeException', 'InvalidPropertyException', 'JsonException', 'Mobi_Mtld_DA_UaProps', 'UnkownPropertyException', '__author__', '__builtins__', '__contributors__', '__doc__', '__file__', '__license__', '__name__', '__package__', '__url__', '__version__', 're', 'simplejson', 'string', 'sys', 'time', 'utilities'] 
 11      >>> da = api.DaApi() 
 12      >>> tree = da.getTreeFromFile('DeviceAtlas.json') 
 13      >>> da.getTreeRevision(tree) 
 14      18251 
 15      >>> ua = 'SonyEricssonW850i/R1GB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1' 
 16      >>> da.getProperties(tree, ua) 
 17      {u'drmOmaForwardLock': 1, u'umts': 1, u'displayWidth': 240, u'3gp.aac.lc': 1, u'mp3': 1, u'3gp.amr.wb': 1, u'cookieSupport': 1, u'midiPolyphonic': 1, u'image.Gif87': 1, u'3gp.h264.level10b': 1, u'wmv': 1, u'jsr118': 1, u'markup.xhtmlBasic10': 1, u'mp4.h264.level13': 0, u'mp4.h264.level11': 1, u'jqm': 0, u'vendor': u'Sony Ericsson', u'uriSchemeTel': 1, u'3gp.amr.nb': 1, u'amr': 1, u'stream.3gp.h264.level12': 0, u'stream.3gp.aac.lc': 1, u'stream.3gp.h264.level10': 1, u'stream.3gp.h264.level11': 0, u'osOsx': 0, u'midiMonophonic': 1, u'mp4.aac.lc': 1, u'gprs': 1, u'jsr30': 1, u'stream.3gp.h263': 1, u'usableDisplayHeight': 262, u'markup.xhtmlMp10': 1, u'markup.xhtmlMp12': 0, u'osAndroid': 0, u'qcelp': 0, u'osWindowsMobile': 0, u'3gpp2': 0, u'osSymbian': 0, u'mobileDevice': 1, '_unmatched': 'R1GB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1', u'stream.mp4.h264.level13': 0, u'stream.mp4.h264.level11': 0, u'developerPlatformVersion': 7, u'osBada': 0, u'yearReleased': 2002, '_matched': 'SonyEricssonW850i/', u'midp': u'2.0', u'uriSchemeSmsTo': 0, u'developerPlatform': u'JavaPlatform', u'markup.wml1': 1, u'memoryLimitMarkup': 45000, u'cldc': u'1.1', u'amrInVideo': 1, u'qcelpInVideo': 0, u'https': 1, u'hscsd': 1, u'drmOmaSeparateDelivery': 1, u'image.Jpg': 1, u'uriSchemeSms': 1, u'stream.3gp.h264.level10b': 1, u'drmOmaCombinedDelivery': 1, u'vCardDownload': 1, u'aac': 1, u'osWindowsPhone': 0, u'mpeg4InVideo': 1, u'stream.mp4.aac.lc': 0, u'displayHeight': 320, u'mpeg4': 1, u'jsr37': 1, u'isTablet': 0, u'isEReader': 0, u'displayColorDepth': 18, u'3gp.h264.level13': 0, u'3gp.h264.level12': 0, u'3gp.h264.level11': 0, u'3gp.h264.level10': 1, u'id': 205009, u'image.Gif89a': 1, u'touchScreen': 0, u'csd': 1, u'3gpp': 1, u'jsr139': 1, u'aacInVideo': 1, u'image.Png': 1, u'markup.xhtmlMp11': 1, u'stream.3gp.amr.wb': 1, u'stream.3gp.amr.nb': 1, u'osWindows': 0, u'stream.3gp.h264.level13': 0, u'isMobilePhone': 1, u'3gp.h263': 1, u'osiOs': 0, u'hsdpa': 0, u'edge': 0, u'usableDisplayWidth': 230, u'h263Type0InVideo': 1, u'model': u'W850i', u'osWebOs': 0, u'isGamesConsole': 0} 
 18      >>>  
 19      >>> da.getProperty(tree, ua, 'displayWidth') 
 20      240 
 21      >>> da.getProperty(tree, ua, 'displayHeight') 
 22      320 
 23      >>> da.getPropertiesAsTyped(tree, ua) 
 24      {u'gprs': True, u'mpeg4': True, u'drmOmaForwardLock': True, u'umts': False, u'displayWidth': 240, u'mp3': True, u'markup.xhtmlMp11': True, u'markup.xhtmlMp10': True, u'markup.xhtmlMp12': False, u'id': 205009, u'memoryLimitMarkup': 45000, u'midiPolyphonic': True, u'image.Gif87': True, u'csd': True, u'3gpp': True, u'qcelp': False, u'wmv': True, u'markup.xhtmlBasic10': True, u'https': True, u'image.Gif89a': False, u'3gpp2': False, u'hscsd': False, u'midiMonophonic': True, u'drmOmaSeparateDelivery': True, u'displayColorDepth': 18, u'vendor': 'Sony Ericsson', u'image.Jpg': True, u'uriSchemeTel': True, u'mobileDevice': True, '_unmatched': 'R1GB Browser/NetFront/3.3 Profile/MIDP-2.0 Configuration/CLDC-1.1', u'hsdpa': False, u'amr': True, u'model': 'W850i', u'drmOmaCombinedDelivery': True, u'aac': False, u'mpeg4InVideo': True, u'image.Png': True, u'edge': False, u'h263Type0InVideo': True, u'displayHeight': 320, u'aacInVideo': True, '_matched': 'SonyEricssonW850i/'} 
 25  >>> da.getApiRevision() 
 26      2426 
 27      >>> da.listProperties(tree) 
 28      {u'drmOmaForwardLock': 'boolean', u'umts': 'boolean', u'displayWidth': 'integer', u'3gp.aac.lc': 'boolean', u'mp3': 'boolean', u'3gp.amr.wb': 'boolean', u'cookieSupport': 'boolean', u'osRim': 'boolean', u'midiPolyphonic': 'boolean', u'image.Gif87': 'boolean', u'3gp.h264.level10b': 'boolean', u'wmv': 'boolean', u'jsr118': 'boolean', u'markup.xhtmlBasic10': 'boolean', u'mp4.h264.level13': 'boolean', u'mp4.h264.level11': 'boolean', u'stylesheetSupport': 'string', u'httpDirectDownload': 'boolean', u'jqm': 'boolean', u'vendor': 'string', u'uriSchemeTel': 'boolean', u'isBrowser': 'boolean', u'3gp.amr.nb': 'boolean', u'browserName': 'string', u'amr': 'boolean', u'stream.3gp.h264.level12': 'boolean', u'stream.3gp.h264.level13': 'boolean', u'stream.3gp.h264.level10': 'boolean', u'stream.3gp.h264.level11': 'boolean', u'osLinux': 'boolean', u'osOsx': 'boolean', u'aacInVideo': 'boolean', u'mp4.aac.lc': 'boolean', u'gprs': 'boolean', u'memoryLimitEmbeddedMedia': 'integer', u'osWindowsMobile': 'boolean', u'jsr30': 'boolean', u'jsr37': 'boolean', u'markup.xhtmlMp11': 'boolean', u'markup.xhtmlMp10': 'boolean', u'markup.xhtmlMp12': 'boolean', u'osAndroid': 'boolean', u'qcelp': 'boolean', u'version': 'string', u'isFeedReader': 'boolean', u'osProprietary': 'string', u'3gpp2': 'boolean', u'oma': 'boolean', u'osSymbian': 'boolean', u'mobileDevice': 'boolean', u'stream.mp4.h264.level13': 'boolean', u'stream.mp4.h264.level11': 'boolean', u'isFilter': 'boolean', u'developerPlatformVersion': 'string', u'osBada': 'boolean', u'yearReleased': 'integer', u'awbInVideo': 'boolean', u'midp': 'string', u'uriSchemeSmsTo': 'boolean', u'memoryLimitDownload': 'integer', u'developerPlatform': 'string', u'markup.wml1': 'boolean', u'isSpam': 'boolean', u'memoryLimitMarkup': 'integer', u'cldc': 'string', u'inputDevices': 'string', u'amrInVideo': 'boolean', u'qcelpInVideo': 'boolean', u'https': 'boolean', u'markupSupport': 'string', u'hscsd': 'boolean', u'drmOmaSeparateDelivery': 'boolean', u'image.Jpg': 'boolean', u'uriSchemeSms': 'boolean', u'isRobot': 'boolean', u'stream.3gp.h264.level10b': 'boolean', u'drmOmaCombinedDelivery': 'boolean', u'vCardDownload': 'boolean', u'aac': 'boolean', u'isEReader': 'boolean', u'mpeg4InVideo': 'boolean', u'osWindowsPhone': 'boolean', u'stream.mp4.aac.lc': 'boolean', u'isDownloader': 'boolean', u'displayHeight': 'integer', u'mpeg4': 'boolean', u'stream.3gp.h263': 'boolean', u'imageFormatSupport': 'string', u'marketingName': 'string', u'isTablet': 'boolean', u'usableDisplayHeight': 'integer', u'displayColorDepth': 'integer', u'3gp.h264.level13': 'boolean', u'3gp.h264.level12': 'boolean', u'3gp.h264.level11': 'boolean', u'3gp.h264.level10': 'boolean', u'id': 'integer', u'stream.3gp.aac.lc': 'boolean', u'image.Gif89a': 'boolean', u'h263Type3InVideo': 'boolean', u'touchScreen': 'boolean', u'isChecker': 'boolean', u'jqm_alpha': 'boolean', u'csd': 'boolean', u'3gpp': 'boolean', u'aacLtpInVideo': 'boolean', u'jsr139': 'boolean', u'midiMonophonic': 'boolean', u'image.Png': 'boolean', u'stream.3gp.amr.wb': 'boolean', u'stream.3gp.amr.nb': 'boolean', u'osWindows': 'boolean', u'isMobilePhone': 'boolean', u'3gp.h263': 'boolean', u'osiOs': 'boolean', u'hsdpa': 'boolean', u'osVersion': 'string', u'browserVersion': 'string', u'inputModeSupport': 'string', u'scriptSupport': 'string', u'edge': 'boolean', u'usableDisplayWidth': 'integer', u'h263Type0InVideo': 'boolean', u'model': 'string', u'osWebOs': 'boolean', u'isGamesConsole': 'boolean'} 
 29      >>> da.getPropertiesAsTyped(tree, ua)['mobileDevice'] 
 30      True 
 31      >>> da.getPropertiesAsTyped(tree, ua)['vendor'] 
 32      'Sony Ericsson' 
 33      >>> da.getPropertiesAsTyped(tree, ua)['model'] 
 34      'W850i' 
 35      >>> len(da.getPropertiesAsTyped(tree, ua)) 
 36      4 
 37   
 38  In some contexts, the user-agent you want to recognise may have been provided  
 39  in a different header. Opera's mobile browser, for example, makes requests via  
 40  an HTTP proxy, which rewrites the headers. in that case, the original device's 
 41  user-agent is in the HTTP_X_OPERAMINI_PHONE_UA header. 
 42   
 43  ''' 
 44   
 45  __version__ = '1.6 Build $Rev: 27734 $' 
 46  __url__ = 'http://deviceatlas.com' 
 47  __author__ = 'Ronan Cremin <http://deviceatlas.com/>' 
 48  __contributors__ = ['Adrian Hope-Balie <http://mobiforge.com/>','James Pearce <http://mobiforge.com/>', 'Hamish Graham <http://deviceatlas.com>', 'David Carlier <http://deviceatlas.com>'] 
 49  __license__ = '''Copyright (c) 2008, mTLD (dotMobi), All rights reserved. 
 50  Portions copyright (c) 2008 by Argo Interactive Limited. 
 51  Portions copyright (c) 2008 by Nokia Inc. 
 52  Portions copyright (c) 2008 by Telecom Italia Mobile S.p.A. 
 53  Portions copyright (c) 2008 by Volantis Systems Limited. 
 54  Portions copyright (c) 2002-2008 by Andreas Staeding. 
 55  Portions copyright (c) 2008 by Zandan. 
 56  ''' 
 57   
 58   
 59  import time, string, sys, re 
 60  from daExceptions import InvalidPropertyException, IncorrectPropertyTypeException, JsonException, UnkownPropertyException, ClientPropertiesException 
 61  from UaProps import * 
 62  from ClientProps import * 
 63  import utilities  
 64   
 65  try: 
 66      import json as simplejson 
 67  except ImportError: 
 68      import simplejson 
69 70 71 -class DaApi:
72 API_ID = '6' 73 74 CLIENT_PROPS_HANDLER = "_cprops" 75 UA_PROPS_HANDLER = "_uaprops" 76 77 ID_TO_PROPERTIES_WITH_TYPE = "p" 78 ID_TO_PROPERTY_WITHOUT_TYPE = "pnr" 79 PROPERTIES_WITH_TYPE_TO_ID = "pr" 80 PROPERTIES_WITHOUT_TYPE_TO_ID = "pn" 81 82 ID_TO_VALUES = "v" 83 MAIN_TREE_BRANCH = "t" 84 REGEX = "r" 85 COMPILED_REGEX = "creg" 86 87 @staticmethod
88 - def getTreeFromString(json, includeChangeableUserAgentProperties = True):
89 '''Returns a tree from a JSON string return array tree 90 91 Some properties cannot be known before runtime and can change from user-agent to 92 user-agent. The most common of these are the OS Version and the Browser Version. This 93 API is able to dynamically detect these changing properties but introduces a small 94 overhead to do so. To disable returning these extra properties set 95 <i>includeChangeableUserAgentProperties</i> to <b>false</b>. 96 97 Arguments: 98 json -- a string of json data 99 includeChangeableUserAgentProperties -- also detect changeable user-agent properties 100 101 Returns: 102 The loaded JSON Trr 103 104 Throws: 105 RunTimeError 106 ''' 107 108 if list(sys.version_info[:2]) < [2, 3]: 109 raise ImportError('Version 2.3 or later of Python is required') 110 111 tree = simplejson.loads(json) 112 113 if tree == {}: 114 raise JsonException('Unable to load JSON data') 115 elif ( '$' not in tree ): 116 raise JsonException('Bad data loaded into the tree') 117 elif float(tree['$']['Ver']) < 0.7: 118 raise JsonException('DeviceAtlas JSON file must be version 0.7 or later. Please download a more recent version') 119 120 pr = {} 121 pn = {} 122 idToProp = {} 123 for value in tree[DaApi.ID_TO_PROPERTIES_WITH_TYPE]: 124 key = tree[DaApi.ID_TO_PROPERTIES_WITH_TYPE].index(value) 125 name = value[1:] # knock off property type char 126 127 pr[value] = key 128 pn[name] = key 129 idToProp[key] = name 130 131 tree[DaApi.PROPERTIES_WITH_TYPE_TO_ID] = pr # propertytype+name => propertyid 132 tree[DaApi.PROPERTIES_WITHOUT_TYPE_TO_ID] = pn # propertyname => propertyid 133 tree[DaApi.ID_TO_PROPERTY_WITHOUT_TYPE] = idToProp # property names without the type char 134 135 if DaApi.REGEX not in tree: 136 tree[DaApi.REGEX] = {} 137 138 # prepare the user-agent rules branch before we start recognition. 139 # To maintain backwards compatibility - only do this if we have the ua 140 # rules branch 141 142 if Mobi_Mtld_DA_UaProps.UA_RULES in tree: 143 #if tree[Mobi_Mtld_DA_UaProps.UA_RULES]: 144 if not includeChangeableUserAgentProperties: 145 # remove the UAR branch to avoid it being used later on... 146 del tree[Mobi_Mtld_DA_UaProps.UA_RULES] 147 else: 148 tree[DaApi.UA_PROPS_HANDLER] = Mobi_Mtld_DA_UaProps(tree) 149 150 if Mobi_Mtld_DA_ClientProps.CP_RULES in tree: 151 tree[DaApi.CLIENT_PROPS_HANDLER] = Mobi_Mtld_DA_ClientProps(tree) 152 153 return tree
154 155 156 @staticmethod
157 - def getTreeFromFile(filep, includeChangeableUserAgentProperties=True):
158 '''Returns a tree from a JSON file. 159 Use absolute path name to be sure of success if the current working directory is not clear 160 161 Some properties cannot be known before runtime and can change from user-agent to 162 user-agent. The most common of these are the OS Version and the Browser Version. This 163 API is able to dynamically detect these changing properties but introduces a small 164 overhead to do so. 165 166 Arguments: 167 file -- the location of the file to read 168 includeChangeableUserAgentProperties -- set to False to disable returning extra properties set 169 170 Returns: 171 Hash tree 172 ''' 173 174 json = open(filep, 'r').read() 175 if json == '': 176 raise JsonException('Unable to load file: %s' % filep) 177 178 return DaApi.getTreeFromString(json, includeChangeableUserAgentProperties)
179 180 181 @staticmethod
182 - def getTreeGeneration(tree):
183 '''Get the generation date for this tree 184 185 Arguments: 186 tree -- previously loaded tree 187 188 Returns: 189 The date/time when the tree was generated 190 ''' 191 192 return tree['$']['Gen']
193 194 195 @staticmethod
197 '''Returns revision number of the tree 198 199 Arguments: 200 tree -- previously loaded tree 201 202 Returns: 203 Integer revision number 204 ''' 205 206 return tree['$']['Utc']
207 208 209 @staticmethod
210 - def getTreeRevision(tree):
211 '''Returns revision number of the tree 212 213 Arguments: 214 tree -- previously loaded tree 215 216 Returns: 217 Integer revision number 218 ''' 219 220 return DaApi._getRevisionFromKeyword(tree['$']['Rev'])
221 222 223 @staticmethod
224 - def getApiRevision():
225 '''Returns revision number of the API 226 227 Arguments: 228 tree -- previously loaded tree 229 230 Returns: 231 Integer revision number 232 ''' 233 234 return DaApi._getRevisionFromKeyword('$Rev: 27734 $')
235 236 237 @staticmethod
238 - def listProperties(tree):
239 '''Returns array of known property names. Returns all properties 240 available for all user agents in this tree, with their data type 241 names 242 243 Arguments: 244 tree -- previously generated tree 245 246 Returns: 247 Dictionary of properties 248 ''' 249 250 types = { 251 's': 'string', 252 'b': 'boolean', 253 'i': 'integer', 254 'd': 'date', 255 'u': 'unknown' 256 } 257 listProperties = {} 258 259 for prop in tree[DaApi.ID_TO_PROPERTIES_WITH_TYPE]: 260 listProperties[prop[1:]] = types[prop[0]] 261 262 return listProperties
263 264 @staticmethod
265 - def getProperties(tree, userAgent, cookie = None, typedValues = False, sought = None, uaPropsNeeded = False):
266 '''Returns an array of known properties (as strings) for the UA 267 268 Arguments: 269 tree -- previously generated tree 270 userAgent -- string from devices User-Agent header 271 272 Returns: 273 Dictionary of properties 274 ''' 275 276 if cookie == None: 277 return DaApi._getProperties(tree, userAgent, typedValues, None, uaPropsNeeded) 278 else: 279 if DaApi.CLIENT_PROPS_HANDLER in tree: 280 clientProps = tree[DaApi.CLIENT_PROPS_HANDLER] 281 treeWalkProperties = DaApi._getProperties(tree, userAgent, typedValues, None, uaPropsNeeded) 282 return clientProps.getProperties(treeWalkProperties, cookie, typedValues) 283 else: 284 raise ClientPropertiesException("JSON file does not support client properties.")
285 286 287 @staticmethod
288 - def getPropertiesAsTyped(tree, userAgent, cookie = None):
289 '''Returns an array of known properties (as typed) for the UA 290 291 Arguments: 292 tree --- previously generated tree 293 userAgent -- string from devices User-Agent header 294 295 Returns: 296 String property 297 ''' 298 299 return DaApi.getProperties(tree, userAgent, cookie, True, None)
300 301 302 @staticmethod
303 - def getProperty(tree, userAgent, prop, cookie = None):
304 '''Returns a value for the named property of this user agent 305 306 Arguments: 307 tree -- previously generated tree 308 userAgent -- string from devices User-Agent header 309 property -- the name of the property to return 310 311 Returns: 312 String property 313 ''' 314 315 return DaApi._getProperty(tree, userAgent, prop, cookie, False)
316 317 318 @staticmethod
319 - def getPropertyAsBoolean(tree, userAgent, prop, cookie = None):
320 '''Strongly-typed property accessor. Returns boolean property. 321 Throws exception of property is actually a different type 322 323 Arguments: 324 tree -- previously generated tree 325 userAgent -- string from devices User-Agent header 326 property -- the name of the property to return 327 328 Returns: 329 Boolean property 330 ''' 331 332 DaApi._propertyTypeCheck(tree, prop, 'b', 'boolean') 333 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
334 335 336 @staticmethod
337 - def getPropertyAsDate(tree, userAgent, prop, cookie = None):
338 '''Strongly-typed property accessor. Returns date property. 339 Throws exception of property is actually a different type 340 341 Arguments: 342 tree is previously generated tree 343 userAgent is string from devices User-Agent header 344 property is the name of the property to return 345 346 Returns: 347 String property 348 ''' 349 DaApi._propertyTypeCheck(tree, prop, 'd', 'string') 350 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
351 352 353 @staticmethod
354 - def getPropertyAsInteger(tree, userAgent, prop, cookie = None):
355 '''Strongly-typed property accessor. Returns integer property. 356 Throws exception of property is actually a different type 357 358 Arguments: 359 tree -- previously generated tree 360 userAgent -- string from devices User-Agent header 361 property -- the name of the property to return 362 363 Returns: 364 Integer property 365 ''' 366 367 DaApi._propertyTypeCheck(tree, prop, 'i', 'integer') 368 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
369 370 371 @staticmethod
372 - def getPropertyAsString(tree, userAgent, prop, cookie = None):
373 '''Strongly-typed property accessor. Returns string property. 374 Throws exception of property is actually a different type 375 376 Arguments: 377 tree -- previously generated tree 378 userAgent -- string from devices User-Agent header 379 property -- the name of the property to return 380 381 Returns: 382 String property 383 ''' 384 DaApi._propertyTypeCheck(tree, prop, 's', 'string') 385 return DaApi._getProperty(tree, userAgent, prop, cookie, True)
386 387 388 # PRIVATE METHODS 389 # 390 391 @staticmethod
392 - def _getRevisionFromKeyword(keyword):
393 '''Returns cleaned up version of SVN revision string as integer 394 keyword is string 395 ''' 396 return int(keyword[6:].replace('$', '').strip())
397 398 399 @staticmethod
400 - def _getProperties(tree, userAgent, typedValues, sought, uaPropsNeeded):
401 '''Returns the properties for a given User-Agent by first walking the tree 402 and then supplementing the tree properties with properties from the 403 User-Agent string itself. 404 405 - tree previously generated HashMap tree 406 - userAgent the device's User-Agent header string 407 - typedValues whether values in the hashmap are typed 408 - sought a set of any specific properties to find, if null then all properties are returned 409 - uaPropsNeeded whether the extra properties from the UA String are needed 410 the found properties 411 ''' 412 413 uaPropsHandler = None 414 415 if sought: 416 uaPropsNeeded = False 417 418 uaPropsHandler = tree[DaApi.UA_PROPS_HANDLER] 419 420 if uaPropsHandler: 421 for prop in sought: 422 if uaPropsHandler.propIsOutput(prop): 423 uaPropsNeeded = True 424 break 425 426 if uaPropsNeeded: 427 sought = sought + uaPropsHandler.getRequiredProperties() 428 429 430 idProperties = {} 431 matched = '' 432 433 userAgent = userAgent.strip() 434 rules = tree[DaApi.REGEX][DaApi.API_ID] 435 436 sought, matched = DaApi._seekProperties(tree[DaApi.MAIN_TREE_BRANCH], userAgent.strip(), idProperties, sought, matched, rules) 437 438 properties = DaApi._lookupNameValue(tree, idProperties, typedValues) 439 440 if sought == None: 441 properties['_matched'] = matched 442 properties['_unmatched'] = userAgent[len(matched):] 443 444 if uaPropsNeeded: 445 if not uaPropsHandler: 446 uaPropsHandler = tree[DaApi.UA_PROPS_HANDLER] 447 448 if uaPropsHandler: 449 uaProps = uaPropsHandler.getProperties(userAgent, idProperties, sought, typedValues) 450 utilities.mergeProperties(properties, uaProps) 451 452 return properties
453 454 455 456 @staticmethod
457 - def _lookupNameValue(tree, idProperties, typedValues):
458 '''Lookup the property names and values from the holder dictionaries 459 460 Arguments: 461 tree -- previously instantiated tree 462 idProperties -- 463 typedValues -- 464 ''' 465 466 properties = {} 467 arr = [] 468 try: 469 arr = idProperties.itertems() 470 except AttributeError: 471 arr = idProperties.items() 472 473 for propId, propValueId in arr: 474 propName = utilities.propertyFromId(tree, propId) 475 properties[propName] = utilities.getValue(tree, propId, propValueId, typedValues) 476 477 return properties
478 479 480 @staticmethod
481 - def _getProperty(tree, userAgent, prop, cookie, typedValues):
482 '''Get the property from the tree walk and User-Agent string. 483 ''' 484 485 if cookie == None: 486 propertyId = DaApi._idFromProperty(tree, prop) 487 sought = [propertyId] 488 489 properties = DaApi.getProperties(tree, userAgent, None, typedValues, sought, True) 490 491 if prop not in properties: 492 raise InvalidPropertyException("The property \"%s\" does not exist for the User-Agent:\"%s\"" % (prop, userAgent)) 493 494 return properties[prop] 495 else: 496 clientPropsHandler = tree[DaApi.CLIENT_PROPS_HANDLER] 497 498 if not clientPropsHandler: 499 raise ClientPropertiesException("JSON file does not support client properties") 500 501 502 parsedCookie = None 503 propertyId = DaApi._idFromProperty(tree, prop) 504 505 properties = None 506 507 if cookie: 508 isRuleProp = clientPropsHandler.propIsOutput(propertyId) 509 510 if not isRuleProp: 511 parsedCookie = clientPropsHandler.parseClientSideProperties(cookie, typedValues) 512 513 if parsedCookie: 514 properties = clientPropsHandler.getClientProperty(propertyId, prop, parsedCookie, typedValues) 515 516 if not properties or prop not in properties: 517 sought = [propertyId] 518 sought = sought + clientPropsHandler.getRequiredProperties() 519 properties = DaApi.getProperties(tree, userAgent, None, typedValues, sought, True) 520 properties = clientPropsHandler.getProperties(properties, parsedCookie, typedValues) 521 522 if prop not in properties: 523 raise InvalidPropertyException("The property \"%s\" does not exist for the User-Agent:\"%s\"" % (prop, userAgent)) 524 525 return properties[prop]
526 527 @staticmethod
528 - def _idFromProperty(tree, prop):
529 '''Return the coded ID for a property's name 530 531 Arguments: 532 tree -- a previously generated tree 533 property -- string 534 535 Raises UnkownPropertyException if property not found 536 ''' 537 538 if prop in tree[DaApi.PROPERTIES_WITHOUT_TYPE_TO_ID]: 539 return tree[DaApi.PROPERTIES_WITHOUT_TYPE_TO_ID][prop] 540 else: 541 raise UnkownPropertyException('The property %s is not known in this tree' % prop)
542 543 544 @staticmethod
545 - def propertyFromId(tree, pid):
546 '''Return the name for a property's coded id 547 548 Arguments: 549 tree -- previously generated tree 550 ''' 551 552 # TODO should I be casting to int here? (not in PHP) 553 propName = tree[DaApi.ID_TO_PROPERTY_WITHOUT_TYPE][int(pid)] 554 return propName
555 556 557 @staticmethod
558 - def _propertyTypeCheck(tree, prop, prefix, typeName):
559 '''Checks that the property is of the supplied type or 560 throws exception 561 562 array tree Previously generated HashMap tree 563 array property The name of the property to return 564 string prefix The type prefix (i for integer) 565 string typeName Easy readable type name 566 ''' 567 568 if (prefix + prop) not in tree[DaApi.PROPERTIES_WITH_TYPE_TO_ID]: 569 raise IncorrectPropertyTypeException('The property %s is not of type %s' % (prop, typeName))
570 571 572 @staticmethod
573 - def _seekProperties(node, string, properties, sought, matched, rules):
574 '''Seek properties for a UA within a node 575 This is designed to be recursed, and only externally called with the node representing the top of the tree 576 577 Arguments: 578 node -- dictionary representing JSON tree 579 string -- string 580 properties -- properties found thus far 581 sought -- properties being sought 582 matched -- portion of UA that has been matched thus far 583 ''' 584 585 unmatched = string 586 if 'd' in node: 587 if (sought != None and len(sought) == 0): 588 return sought, matched 589 590 nodes = {} 591 592 try: 593 nodes = node['d'].iteritems() 594 except AttributeError: 595 nodes = node['d'].items() 596 597 for prop, value in nodes: 598 599 if sought == None or prop in sought: 600 properties[prop] = value 601 602 # if we're looking for particular properties and either: 603 # node doesn't have mask key 604 # or it does have it but not with key of current property 605 if sought != None and ( 606 'm' not in node 607 or 608 ('m' in node and (prop not in node['m'])) 609 ): 610 611 if prop in sought: 612 del sought[prop] 613 614 if 'c' in node: 615 616 # rules - strip out parts of the UA 617 if 'r' in node: 618 for i in range(0, len(node['r'])): 619 ruleId = node['r'][i] 620 string = re.sub(rules[ruleId], '', string) 621 for c in range(1, len(string)+1): 622 seek = string[0:c] 623 if seek in node['c']: 624 matched += seek 625 sought, matched = DaApi._seekProperties(node['c'][seek], string[c:], properties, sought, matched, rules) 626 break 627 return sought, matched
628 629 630 if __name__ == '__main__': 631 print(__doc__) 632 print('Version:', __version__) 633 print('License:', __license__) 634