Module api
[hide private]
[frames] | no frames]

Source Code for Module api

  1  #!/usr/bin/env python 
  2   
  3  """ 
  4  @copyright: 
  5   Copyright (c) 2014 by mTLD Top Level Domain Limited. All rights reserved.\n 
  6   Portions copyright (c) 2008 by Argo Interactive Limited.\n 
  7   Portions copyright (c) 2008 by Nokia Inc.\n 
  8   Portions copyright (c) 2008 by Telecom Italia Mobile S.p.A.\n 
  9   Portions copyright (c) 2008 by Volantis Systems Limited.\n 
 10   Portions copyright (c) 2002-2008 by Andreas Staeding.\n 
 11   Portions copyright (c) 2008 by Zandan.\n 
 12  @author: dotMobi 
 13  """ 
 14   
 15  import datetime 
 16   
 17  from mobi.mtld.da.exception.invalid_property_name_exception import InvalidPropertyNameException 
 18  from mobi.mtld.da.exception.data_file_exception import DataFileException 
 19  from mobi.mtld.da.exception.client_properties_exception import ClientPropertiesException 
 20  from mobi.mtld.da.exception.incorrect_property_type_exception import IncorrectPropertyTypeException 
 21   
 22  from mobi.mtld.da.data_type import DataType 
 23   
 24  from mobi.mtld.da.device.config import Config 
 25  from mobi.mtld.da.device.device_api import DeviceApi 
26 27 -class UnknownPropertyException(InvalidPropertyNameException):
28 """ 29 UnknownPropertyException is thrown by the API class when there is an 30 attempt to fetch a property that is unknown for the supplied user agent or 31 tree. 32 33 @deprecated: Please use the new interface and exception classes through 34 DeviceApi instead. 35 """ 36 pass
37
38 -class InvalidPropertyException(InvalidPropertyNameException):
39 """ 40 InvalidPropertyException is thrown by the API class when there is an 41 attempt to fetch a property that is unknown for the supplied user agent, 42 tree and client side property set. 43 44 @deprecated: Please use the new interface and exception classes through 45 DeviceApi instead. 46 """ 47 pass
48
49 -class JsonException(DataFileException):
50 """ 51 JsonException is thrown when there is an error loading the data file. 52 53 @deprecated: Please use the new interface and exception classes through 54 DeviceApi instead. 55 """ 56 pass
57
58 -class DaApi(object):
59 """ 60 Used to load the recognition tree and perform lookups of all properties, or 61 get individual properties. Typical usage is as follows: 62 63 >>> deviceAtlas = DaApi() 64 >>> path = 'data_file/sample.json' 65 >>> tree = mobi.mtld.da.getTreeFromFile(path) 66 >>> properties = mobi.mtld.da.getProperties(tree, "Nokia6680...") 67 >>> property = mobi.mtld.da.getProperty(tree, "Nokia6680...", "displayWidth") 68 69 Note that you should normally use the user-agent that was received in 70 the device's HTTP request. In a BaseHTTPServer environment, you would do this 71 as follows: 72 73 >>> userAgent = self.headers['user-agent'] 74 >>> displayWidth = mobi.mtld.da.getPropertyAsInteger(tree, userAgent, 75 >>> "displayWidth") 76 77 Third-party Browsers: 78 79 In some contexts, the user-agent you want to recognise may have been provided 80 in a different header. Opera's mobile browser, for example, makes requests via 81 an HTTP proxy, which rewrites the headers. in that case, the original device's 82 user-agent is in the "X-OperaMini-Phone-UA" header, and the following code 83 could be used: 84 85 >>> operaHeader = "X-OperaMini-Phone-UA" 86 >>> if operaHeader in request.headers: 87 >>> userAgent = self.headers[operaHeader] 88 >>> else: 89 >>> userAgent = self.headers['user-agent'] 90 >>> displayWidth = mobi.mtld.da.getPropertyAsInteger(tree, userAgent, 91 >>> "displayWidth") 92 93 See here for more information: 94 https://mobi.mtld.da.com/resources/side-loaded-browser-handling 95 96 Client side properties: 97 98 Client side properties can be collected and merged into the results by using 99 the DeviceAtlas Javascript detection file. The results from the client side 100 are sent to the server inside a cookie. The contents of this cookie can be 101 passed to the DeviceAtlas getProperty and getProperties methods. The client 102 side properties over-ride any data file properties and also serve as an input 103 into additional logic to determine other properties such as the iPhone models 104 that are otherwise not detectable. The following code shows how this can be 105 done in BaseHTTPServer: 106 107 >>> userAgent = self.headers['user-agent'] 108 >>> cookieContents = # method call to get the 'DAPROPS' cookie content 109 >>> properties = mobi.mtld.da.getPropertiesAsTyped(tree, userAgent, 110 >>> cookieContents) 111 112 @deprecated: Please use the new interface with DeviceApi instead. 113 """ 114 115 @staticmethod
116 - def getTreeFromFile(filename, includeChangeableUserAgentProperties = True):
117 """ 118 Return a tree from a JSON file. The loaded tree is stored in a static cache 119 to avoid multiple reloads if this method is repeatedly called. To reload 120 from the JSON file set "reload" to true. 121 122 Some properties cannot be known before runtime and can change from 123 user-agent to user-agent. The most common of these are the OS Version and 124 the Browser Version. This API is able to dynamically detect these changing 125 properties but introduces a small overhead to do so. To disable returning 126 these extra properties set "includeChangeableUserAgentProperties" to false. 127 128 @param filename: is the location of the file to read in. Use an absolute path 129 name to be sure of success if the current working directory is not clear. 130 @param includeChangeableUserAgentProperties: is to detect changeable 131 user-agent properties. 132 133 @deprecated: Please use DeviceApi.load_data_from_file() instead. 134 """ 135 136 # We need to set our DeviceApi object with new settings in order to allow 137 # the detection of user-agent properties. 138 if not includeChangeableUserAgentProperties: 139 config = Config() 140 config.include_ua_props = False 141 device_api = DeviceApi(config) 142 else: 143 device_api = DeviceApi() 144 145 device_api.load_data_from_file(filename) 146 147 return {'api': device_api}
148 149 @staticmethod
150 - def getTreeFromString(json, includeChangeableUserAgentProperties = True):
151 """ 152 Return a loaded JSON tree from a string of JSON data. 153 154 Some properties cannot be known before runtime and can change from 155 user-agent to user-agent. The most common of these are the OS Version and 156 the Browser Version. This API is able to dynamically detect these changing 157 properties but introduces a small overhead to do so. To disable returning 158 these extra properties set "includeChangeableUserAgentProperties" to false. 159 160 This method does not use the built in static cache. 161 162 @param json: is the string of json data. 163 @param includeChangeableUserAgentProperties: Also detect changeable user-agent 164 properties. 165 166 @deprecated: Please use DeviceApi.load_data_from_string() instead. 167 """ 168 169 # We need to set our DeviceApi object with new settings in order to allow 170 # the detection of user-agent properties. 171 if not includeChangeableUserAgentProperties: 172 config = Config() 173 config.include_ua_props = False 174 device_api = DeviceApi(config) 175 else: 176 device_api = DeviceApi() 177 178 device_api.load_data_from_string(json) 179 180 return {'api': device_api}
181 182 @staticmethod
183 - def getTreeGeneration(tree):
184 """ 185 Get the generation date for this tree. 186 @param tree: is a dict object with the tree. 187 It returns the date and time the tree was generated. 188 189 @deprecated: Please use DeviceApi.get_data_creation_timestamp() instead. 190 """ 191 # We can ignore "tree" as the tree generation data is stored in @device_api 192 time = tree['api'].get_data_creation_timestamp() 193 return datetime.datetime.utcfromtimestamp(time).strftime("%Y-%m-%d %H:%M:%S")
194 195 @staticmethod
197 """ 198 Get the generation date for this tree as a UNIX timestamp. 199 @param tree: is a dict object with the tree. 200 It returns a timestamp the tree was generated. 201 202 @deprecated: Please use DeviceApi.get_data_creation_timestamp() instead. 203 """ 204 return tree['api'].get_data_creation_timestamp()
205 206 @staticmethod
207 - def getTreeRevision(tree):
208 """ 209 Return the revision number of the tree. 210 211 @deprecated: Please use DeviceApi.get_data_revision() instead. 212 """ 213 return DaApi.__get_revision_from_keyword(tree['api'].get_data_revision())
214 215 @staticmethod
216 - def getApiRevision():
217 """ 218 Return the revision number of the API. 219 """ 220 return DaApi.__get_revision_from_keyword("$Rev: 41874 $")
221 222 @staticmethod
223 - def listProperties(tree):
224 """ 225 Return an associative array of all property names available for all user 226 agents in this tree, with their data type names. 227 228 @deprecated: Please use DeviceApi.get_property_names() instead. 229 """ 230 # We can ignore "tree" as the tree generation data is stored in @device_api 231 properties = {} 232 property_names = tree['api'].get_property_names() 233 for property_name in property_names: 234 properties[property_name.name] = DaApi.__get_property_type_as_string( 235 property_name.data_type_id) 236 return properties
237 238 @staticmethod
239 - def getProperties(tree, userAgent, cookie = None, typedValues = False, 240 sought = None, uaPropsNeeded = True):
241 """ 242 Return a associative array of known properties merged with properties from 243 the client side JavaScript. The client side JavaScript sets a cookie with 244 collected properties. The contents of this cookie must be passed to this 245 method for it to work. The client properties over-ride any properties 246 discovered from the main JSON data file. 247 248 @param tree: is the previously generated associative array tree. 249 @param userAgent: is the device's User-Agent header string. 250 @param cookie: is the content of the cookie containing the client side 251 properties. 252 @param typedValues: whether values in the results are typed. 253 @param sought: is a set of properties to return values for. 254 @param uaPropsNeeded: whether the extra properties from the UA String are 255 needed. 256 257 @deprecated: Please use DeviceApi.get_properties() instead. 258 """ 259 return DaApi.__get_props_as_hash(tree['api'].get_properties(userAgent, cookie))
260 261 @staticmethod
262 - def getPropertiesAsTyped(tree, userAgent, cookie = None):
263 """ 264 Return an associative array of known properties merged with properties from 265 the client side JavaScript. The client side JavaScript sets a cookie with 266 collected properties. The contents of this cookie must be passed to this 267 method for it to work. The client properties over-ride any properties 268 discovered from the main JSON data file. 269 270 @param tree: is the previously generated associative array tree. 271 @param userAgent: is the device's User-Agent header string. 272 @param cookie: is the content of the cookie containing the client side 273 properties. 274 275 @deprecated: Please use DeviceApi.get_properties() instead. 276 """ 277 properties_out = {} 278 properties_in = tree['api'].get_properties(userAgent, cookie) 279 for name, property_in in properties_in.items(): 280 if property_in.data_type_id == DataType.BOOLEAN: 281 properties_out[name] = int(property_in.value) == 1 282 elif property_in.data_type_id == DataType.STRING: 283 properties_out[name] = str(property_in.value) 284 elif property_in.data_type_id == DataType.INTEGER: 285 properties_out[name] = int(property_in.value) 286 elif property_in.data_type_id == DataType.FLOAT: 287 properties_out[name] = float(property_in.value) 288 else: 289 properties_out[name] = property_in.value 290 return properties_out
291 292 @staticmethod
293 - def getProperty(tree, userAgent, property, cookie = None, typedValue = False):
294 """ 295 Return a value for the named property for this user agent. 296 297 @param tree: Previously generated associative array tree. 298 @param userAgent: The device's User-Agent header string. 299 @param property: The name of the property to return. 300 @param cookie: The contents of the cookie containing the client side 301 properties. 302 @param typedValue: Whether value in the associative array are typed. 303 304 @deprecated: Please use DeviceApi.get_properties instead. 305 """ 306 return DaApi.__get_property_object(tree, userAgent, property, cookie).value
307 308 @staticmethod
309 - def getPropertyAsBoolean(tree, userAgent, property, cookie = None):
310 """ 311 Strongly typed property access. 312 Return a boolean property (or throws an exception if the property is actually 313 of another type). 314 315 @param tree: Previously generated associative array tree. 316 @param userAgent: The device's User-Agent header string. 317 @param property: The name of the property to return. 318 319 @deprecated: Please use DeviceApi.get_properties() and bool(Property) instead. 320 """ 321 try: 322 property_obj = DaApi.__get_property_object(tree, userAgent, property, cookie) 323 if property_obj.data_type_id != DataType.BOOLEAN: 324 raise IncorrectPropertyTypeException(property + " is not of type Boolean") 325 value = property_obj.value 326 return (value == True or value == 1 or value == "1" or 327 str(value)[0].lower() == 't') 328 except UnknownPropertyException as e: 329 raise IncorrectPropertyTypeException(e.message)
330 331 @staticmethod
332 - def getPropertyAsDate(tree, userAgent, property, cookie = None):
333 """ 334 Strongly typed property access. 335 Returns a date property (or throws an exception if the property is actually of 336 another type). 337 338 @param tree: Previously generated associative array tree. 339 @param userAgent: The device's User-Agent header string. 340 @param property: The name of the property to return. 341 342 @deprecated: Date data type is not supported anymore. 343 """ 344 raise IncorrectPropertyTypeException("Date data type is not supported " + 345 "anymore as there are no Date device properties.")
346 347 @staticmethod
348 - def getPropertyAsInteger(tree, userAgent, property, cookie = None):
349 """ 350 Strongly typed property access. 351 Return an integer property (or throws an exception if the property is 352 actually of another type). 353 354 @param tree: Previously generated associative array tree. 355 @param userAgent: The device's User-Agent header string. 356 @param property: The name of the property to return. 357 358 @deprecated: Please use DeviceApi.get_properties() and int(Property) instead. 359 """ 360 try: 361 property_obj = DaApi.__get_property_object(tree, userAgent, property, cookie) 362 if property_obj.data_type_id != DataType.INTEGER: 363 raise IncorrectPropertyTypeException(property + " is not of type Integer") 364 return int(property_obj.value) 365 except UnknownPropertyException as e: 366 raise IncorrectPropertyTypeException(e.message)
367 368 @staticmethod
369 - def getPropertyAsString(tree, userAgent, property, cookie = None):
370 """ 371 Strongly typed property access. 372 Return a string property (or throws an exception if the property is 373 actually of another type). 374 375 @param tree: Previously generated associative array tree. 376 @param userAgent: The device's User-Agent header string. 377 @param property: The name of the property to return. 378 379 @deprecated: Please use DeviceApi.get_properties() and str(Property) instead. 380 """ 381 try: 382 property_obj = DaApi.__get_property_object(tree, userAgent, property, cookie) 383 if property_obj.data_type_id != DataType.STRING: 384 raise IncorrectPropertyTypeException(property + " is not of type String") 385 return str(property_obj) 386 except UnknownPropertyException as e: 387 raise IncorrectPropertyTypeException(e.message)
388 389 # Private static methods 390 391 @staticmethod
392 - def __get_props_as_hash(properties):
393 properties_hash = {} 394 for name, property in properties.items(): 395 properties_hash[name] = property.value 396 return properties_hash
397 398 @staticmethod
399 - def __get_property_type_as_string(property_type_id):
400 if property_type_id == DataType.BOOLEAN: 401 return 'b' 402 if property_type_id == DataType.STRING: 403 return 's' 404 if property_type_id == DataType.INTEGER: 405 return 'i' 406 if property_type_id == DataType.DOUBLE: 407 return 'd' 408 return 'unknown'
409 410 @staticmethod
411 - def __get_property_object(tree, user_agent, property_name, 412 client_side_properties):
413 414 properties = tree['api'].get_properties(user_agent, client_side_properties) 415 property = properties.get(property_name) 416 417 if property is not None: 418 return property 419 420 property_names = tree['api'].get_property_names() 421 for property_name_obj in property_names: 422 if property_name_obj.name == property_name: 423 raise UnknownPropertyException("The property " + property_name + 424 " does not exist for the User-Agent: " + user_agent) 425 426 if client_side_properties is None: 427 raise UnknownPropertyException("The property " + property_name + " is not " + 428 "known in this tree.") 429 else: 430 raise InvalidPropertyException("The property " + property_name + " is not " + 431 "known in this tree.")
432 433 @staticmethod
434 - def __get_revision_from_keyword(keyword):
435 return int(keyword.replace("$","")[5:].strip())
436