123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- from collections import namedtuple
- from ua_parser import user_agent_parser
- from .compat import string_types
- MOBILE_DEVICE_FAMILIES = (
- 'iPhone',
- 'iPod',
- 'Generic Smartphone',
- 'Generic Feature Phone',
- 'PlayStation Vita',
- 'iOS-Device'
- )
- PC_OS_FAMILIES = (
- 'Windows 95',
- 'Windows 98',
- 'Windows ME',
- 'Solaris',
- )
- MOBILE_OS_FAMILIES = (
- 'Windows Phone',
- 'Windows Phone OS', # Earlier versions of ua-parser returns Windows Phone OS
- 'Symbian OS',
- 'Bada',
- 'Windows CE',
- 'Windows Mobile',
- 'Maemo',
- )
- MOBILE_BROWSER_FAMILIES = (
- 'Opera Mobile',
- 'Opera Mini',
- )
- TABLET_DEVICE_FAMILIES = (
- 'iPad',
- 'BlackBerry Playbook',
- 'Blackberry Playbook', # Earlier versions of ua-parser returns "Blackberry" instead of "BlackBerry"
- 'Kindle',
- 'Kindle Fire',
- 'Kindle Fire HD',
- 'Galaxy Tab',
- 'Xoom',
- 'Dell Streak',
- )
- TOUCH_CAPABLE_OS_FAMILIES = (
- 'iOS',
- 'Android',
- 'Windows Phone',
- 'Windows Phone OS',
- 'Windows RT',
- 'Windows CE',
- 'Windows Mobile',
- 'Firefox OS',
- 'MeeGo',
- )
- TOUCH_CAPABLE_DEVICE_FAMILIES = (
- 'BlackBerry Playbook',
- 'Blackberry Playbook',
- 'Kindle Fire',
- )
- EMAIL_PROGRAM_FAMILIES = {
- 'Outlook',
- 'Windows Live Mail',
- 'AirMail',
- 'Apple Mail',
- 'Outlook',
- 'Thunderbird',
- 'Lightning',
- 'ThunderBrowse',
- 'Windows Live Mail',
- 'The Bat!',
- 'Lotus Notes',
- 'IBM Notes',
- 'Barca',
- 'MailBar',
- 'kmail2',
- 'YahooMobileMail'
- }
- def verify_attribute(attribute):
- if isinstance(attribute, string_types) and attribute.isdigit():
- return int(attribute)
- return attribute
- def parse_version(major=None, minor=None, patch=None, patch_minor=None):
- # Returns version number tuple, attributes will be integer if they're numbers
- major = verify_attribute(major)
- minor = verify_attribute(minor)
- patch = verify_attribute(patch)
- patch_minor = verify_attribute(patch_minor)
- return tuple(
- filter(lambda x: x is not None, (major, minor, patch, patch_minor))
- )
- Browser = namedtuple('Browser', ['family', 'version', 'version_string'])
- def parse_browser(family, major=None, minor=None, patch=None, patch_minor=None):
- # Returns a browser object
- version = parse_version(major, minor, patch)
- version_string = '.'.join([str(v) for v in version])
- return Browser(family, version, version_string)
- OperatingSystem = namedtuple('OperatingSystem', ['family', 'version', 'version_string'])
- def parse_operating_system(family, major=None, minor=None, patch=None, patch_minor=None):
- version = parse_version(major, minor, patch)
- version_string = '.'.join([str(v) for v in version])
- return OperatingSystem(family, version, version_string)
- Device = namedtuple('Device', ['family', 'brand', 'model'])
- def parse_device(family, brand, model):
- return Device(family, brand, model)
- class UserAgent(object):
- def __init__(self, user_agent_string):
- ua_dict = user_agent_parser.Parse(user_agent_string)
- self.ua_string = user_agent_string
- self.os = parse_operating_system(**ua_dict['os'])
- self.browser = parse_browser(**ua_dict['user_agent'])
- self.device = parse_device(**ua_dict['device'])
- def __str__(self):
- device = self.is_pc and "PC" or self.device.family
- os = ("%s %s" % (self.os.family, self.os.version_string)).strip()
- browser = ("%s %s" % (self.browser.family, self.browser.version_string)).strip()
- return " / ".join([device, os, browser])
- def __unicode__(self):
- return unicode(str(self))
- def _is_android_tablet(self):
- # Newer Android tablets don't have "Mobile" in their user agent string,
- # older ones like Galaxy Tab still have "Mobile" though they're not
- if ('Mobile Safari' not in self.ua_string and
- self.browser.family != "Firefox Mobile"):
- return True
- return False
- def _is_blackberry_touch_capable_device(self):
- # A helper to determine whether a BB phone has touch capabilities
- # Blackberry Bold Touch series begins with 99XX
- if 'Blackberry 99' in self.device.family:
- return True
- if 'Blackberry 95' in self.device.family: # BB Storm devices
- return True
- if 'Blackberry 95' in self.device.family: # BB Torch devices
- return True
- return False
- @property
- def is_tablet(self):
- if self.device.family in TABLET_DEVICE_FAMILIES:
- return True
- if (self.os.family == 'Android' and self._is_android_tablet()):
- return True
- if self.os.family.startswith('Windows RT'):
- return True
- if self.os.family == 'Firefox OS' and 'Mobile' not in self.browser.family:
- return True
- return False
- @property
- def is_mobile(self):
- # First check for mobile device and mobile browser families
- if self.device.family in MOBILE_DEVICE_FAMILIES:
- return True
- if self.browser.family in MOBILE_BROWSER_FAMILIES:
- return True
- # Device is considered Mobile OS is Android and not tablet
- # This is not fool proof but would have to suffice for now
- if ((self.os.family == 'Android' or self.os.family == 'Firefox OS')
- and not self.is_tablet):
- return True
- if self.os.family == 'BlackBerry OS' and self.device.family != 'Blackberry Playbook':
- return True
- if self.os.family in MOBILE_OS_FAMILIES:
- return True
- # TODO: remove after https://github.com/tobie/ua-parser/issues/126 is closed
- if 'J2ME' in self.ua_string or 'MIDP' in self.ua_string:
- return True
- # This is here mainly to detect Google's Mobile Spider
- if 'iPhone;' in self.ua_string:
- return True
- if 'Googlebot-Mobile' in self.ua_string:
- return True
- # Mobile Spiders should be identified as mobile
- if self.device.family == 'Spider' and 'Mobile' in self.browser.family:
- return True
- # Nokia mobile
- if 'NokiaBrowser' in self.ua_string and 'Mobile' in self.ua_string:
- return True
- return False
- @property
- def is_touch_capable(self):
- # TODO: detect touch capable Nokia devices
- if self.os.family in TOUCH_CAPABLE_OS_FAMILIES:
- return True
- if self.device.family in TOUCH_CAPABLE_DEVICE_FAMILIES:
- return True
- if self.os.family.startswith('Windows 8') and 'Touch' in self.ua_string:
- return True
- if 'BlackBerry' in self.os.family and self._is_blackberry_touch_capable_device():
- return True
- return False
- @property
- def is_pc(self):
- # Returns True for "PC" devices (Windows, Mac and Linux)
- if 'Windows NT' in self.ua_string or self.os.family in PC_OS_FAMILIES:
- return True
- # TODO: remove after https://github.com/tobie/ua-parser/issues/127 is closed
- if self.os.family == 'Mac OS X' and 'Silk' not in self.ua_string:
- return True
- # Maemo has 'Linux' and 'X11' in UA, but it is not for PC
- if 'Maemo' in self.ua_string:
- return False
- if 'Chrome OS' in self.os.family:
- return True
- if 'Linux' in self.ua_string and 'X11' in self.ua_string:
- return True
- return False
- @property
- def is_bot(self):
- return True if self.device.family == 'Spider' else False
- @property
- def is_email_client(self):
- if self.browser.family in EMAIL_PROGRAM_FAMILIES:
- return True
- return False
- def parse(user_agent_string):
- return UserAgent(user_agent_string)
|