From baf880148dd0daefd445431229b0fbc1db78f7b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D1=82=D0=B8=D0=BC=D0=B0=D1=82=20=D0=9D=D0=B0?= =?UTF-8?q?=D0=B6=D0=BC=D1=83=D0=B4=D0=B8=D0=BD=D0=BE=D0=B2=D0=B0?= Date: Thu, 20 May 2021 11:45:23 +0300 Subject: [PATCH 1/8] divide --- user_sessions/templatetags/user_sessions.py | 70 ++++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/user_sessions/templatetags/user_sessions.py b/user_sessions/templatetags/user_sessions.py index df68fa9..928d6c1 100644 --- a/user_sessions/templatetags/user_sessions.py +++ b/user_sessions/templatetags/user_sessions.py @@ -40,43 +40,79 @@ @register.filter -def device(value): +def platform(value): """ Transform a User Agent into human readable text. Example output: - * Safari on iPhone - * Chrome on Windows 8.1 - * Safari on macOS - * Firefox + * iPhone + * Windows 8.1 + * macOS * Linux * None """ + platform = None + for regex, name in DEVICES: + if regex.search(value): + platform = name + break + + return platform + + +@register.filter +def browser(value): + """ + Transform a User Agent into human readable text. + Example output: + * Safari + * Chrome + * Safari + * Firefox + * None + """ + browser = None for regex, name in BROWSERS: if regex.search(value): browser = name break - device = None - for regex, name in DEVICES: - if regex.search(value): - device = name - break + return browser + + +@register.filter +def device(value): + """ + Transform a User Agent into human readable text. + + Example output: + + * Safari on iPhone + * Chrome on Windows 8.1 + * Safari on macOS + * Firefox + * Linux + * None + """ + + browser_ = browser(value) + + platform_ = platform(value) - if browser and device: + if browser_ and platform_: return _('%(browser)s on %(device)s') % { - 'browser': browser, - 'device': device + 'browser': browser_, + 'device': platform_ } - if browser: - return browser + if browser_: + return browser_ - if device: - return device + if platform_: + return platform_ return None From 0194ecdd1cbab3459962422ab3608e9f72d9096a Mon Sep 17 00:00:00 2001 From: Akhmed Gaziev Date: Fri, 21 May 2021 16:44:17 +0300 Subject: [PATCH 2/8] Update user_sessions.py --- user_sessions/templatetags/user_sessions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/user_sessions/templatetags/user_sessions.py b/user_sessions/templatetags/user_sessions.py index 928d6c1..238ff31 100644 --- a/user_sessions/templatetags/user_sessions.py +++ b/user_sessions/templatetags/user_sessions.py @@ -15,7 +15,7 @@ (re.compile('Firefox'), _('Firefox')), (re.compile('IE'), _('Internet Explorer')), ) -DEVICES = ( +PLATFORMS = ( (re.compile('Windows Mobile'), _('Windows Mobile')), (re.compile('Android'), _('Android')), (re.compile('Linux'), _('Linux')), @@ -54,7 +54,7 @@ def platform(value): """ platform = None - for regex, name in DEVICES: + for regex, name in PLATFORMS: if regex.search(value): platform = name break @@ -66,7 +66,9 @@ def platform(value): def browser(value): """ Transform a User Agent into human readable text. + Example output: + * Safari * Chrome * Safari @@ -99,7 +101,6 @@ def device(value): """ browser_ = browser(value) - platform_ = platform(value) if browser_ and platform_: From 20f959da0d0a770326f79508b71c9bf31d39f9c7 Mon Sep 17 00:00:00 2001 From: blag Date: Mon, 18 Sep 2023 14:44:23 -0700 Subject: [PATCH 3/8] Add tests for new templatetags --- tests/tests.py | 272 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 271 insertions(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index c60ffcd..fd31a3e 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -15,7 +15,7 @@ from user_sessions.backends.db import SessionStore from user_sessions.models import Session -from user_sessions.templatetags.user_sessions import device, location +from user_sessions.templatetags.user_sessions import browser, device, location, platform from user_sessions.utils.tests import Client try: @@ -322,6 +322,276 @@ def test_locations(self): self.assertEqual('San Diego, United States', location('44.55.66.77')) +class PlatformTemplateFilterTest(TestCase): + def test_windows(self): + # Generic Windows + self.assertEqual("Windows XP", platform("NT 5.1 not a real browser/10.3")) + self.assertEqual("Windows Vista", platform("NT 6.0 not a real browser/10.3")) + self.assertEqual("Windows 7", platform("NT 6.1 not a real browser/10.3")) + self.assertEqual("Windows 8", platform("NT 6.2 not a real browser/10.3")) + self.assertEqual("Windows 8.1", platform("NT 6.3 not a real browser/10.3")) + self.assertEqual("Windows", platform("Windows not a real browser/10.3")) + + # IE + self.assertEqual( + 'Windows XP', + platform('Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.1; SV1; ' + '.NET CLR 2.0.50727)') + ) + self.assertEqual( + 'Windows Vista', + platform('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; ' + 'Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322;' + ' InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; ' + 'MS-RTC LM 8; OfficeLiveConnector.1.4; OfficeLivePatch.1.3;' + ' .NET CLR 3.0.30729)') + ) + self.assertEqual( + 'Windows 7', + platform('Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; ' + 'Trident/6.0)') + ) + self.assertEqual( + 'Windows 8', + platform('Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ' + 'Win64; x64; Trident/6.0)') + ) + self.assertEqual( + 'Windows 8.1', + platform('Mozilla/5.0 (IE 11.0; Windows NT 6.3; Trident/7.0; ' + '.NET4.0E; .NET4.0C; rv:11.0) like Gecko') + ) + + # Edge + self.assertEqual( + 'Windows 10', + platform('Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, ' + 'like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136') + ) + self.assertEqual( + 'Windows Mobile', + platform('Mozilla/5.0 (Windows Mobile 10; Android 8.0.0; Microsoft; Lumia ' + '950XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 ' + 'Mobile Safari/537.36 Edge/40.15254.369') + ) + + # Edge Chromium + self.assertEqual( + 'Windows 10', + platform('Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 ' + 'Safari/537.36 Edg/81.0.416.62') + ) + + # Firefox + self.assertEqual( + 'Windows 7', + platform('Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:22.0) ' + 'Gecko/20130328 Firefox/22.0') + ) + + # Chrome + self.assertEqual( + 'Windows 8.1', + platform('Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (' + 'KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36') + ) + + def test_apple(self): + # Generic iPad + self.assertEqual("iPad", platform("iPad not a real browser/10.3")) + + # Generic iPhone + self.assertEqual("iPhone", platform("iPhone not a real browser/10.3")) + + self.assertEqual( + 'iPad', + platform('Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) ' + 'AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 ' + 'Mobile/8C148 Safari/6533.18.5') + ) + + self.assertEqual( + 'iPhone', + platform('Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) ' + 'AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 ' + 'Mobile/11A465 Safari/9537.53') + ) + + self.assertEqual( + 'macOS Mojave', + platform('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/85.0.4178.0 Safari/537.36') + ) + self.assertEqual( + 'macOS Catalina', + platform('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) ' + 'Gecko/20100101 Firefox/77.0') + ) + self.assertEqual( + 'macOS', + platform('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) ' + 'AppleWebKit/536.26.17 (KHTML, like Gecko) Version/6.0.2 ' + 'Safari/536.26.17') + ) + + # Edge Chromium + self.assertEqual( + 'macOS Catalina', + platform('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 ' + 'Safari/537.36 Edg/85.0.564.51') + ) + + def test_android(self): + # androids identify themselves as Safari to get the good stuff + self.assertEqual( + 'Android', + platform('Mozilla/5.0 (Linux; U; Android 1.5; de-de; HTC Magic ' + 'Build/CRB17) AppleWebKit/528.5+ (KHTML, like Gecko) ' + 'Version/3.1.2 Mobile Safari/525.20.1') + ) + + # Edge Chromium + self.assertEqual( + 'Android', + platform('Mozilla/5.0 (Linux; Android 11; Pixel 3 XL) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.116 ' + 'Mobile Safari/537.36 EdgA/45.07.4.5059') + ) + + def test_linux_only(self): + self.assertEqual("Linux", platform("Linux not a real browser/10.3")) + + +class BrowserTemplateFilterTest(TestCase): + def test_ie(self): + self.assertEqual( + 'Internet Explorer', + browser('Mozilla/4.0 (Windows; MSIE 6.0; Windows NT 5.1; SV1; ' + '.NET CLR 2.0.50727)') + ) + self.assertEqual( + 'Internet Explorer', + browser('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; ' + 'Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322;' + ' InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; ' + 'MS-RTC LM 8; OfficeLiveConnector.1.4; OfficeLivePatch.1.3;' + ' .NET CLR 3.0.30729)') + ) + self.assertEqual( + 'Internet Explorer', + browser('Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; ' + 'Trident/6.0)') + ) + self.assertEqual( + 'Internet Explorer', + browser('Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ' + 'Win64; x64; Trident/6.0)') + ) + self.assertEqual( + 'Internet Explorer', + browser('Mozilla/5.0 (IE 11.0; Windows NT 6.3; Trident/7.0; ' + '.NET4.0E; .NET4.0C; rv:11.0) like Gecko') + ) + + def test_edge(self): + self.assertEqual( + 'Edge', + browser('Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, ' + 'like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136') + ) + self.assertEqual( + 'Edge', + browser('Mozilla/5.0 (Windows Mobile 10; Android 8.0.0; Microsoft; Lumia ' + '950XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 ' + 'Mobile Safari/537.36 Edge/40.15254.369') + ) + + def test_edge_chromium(self): + self.assertEqual( + 'Edge', + browser('Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.113 ' + 'Safari/537.36 Edg/81.0.416.62') + ) + self.assertEqual( + 'Edge', + browser('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 ' + 'Safari/537.36 Edg/85.0.564.51') + ) + self.assertEqual( + 'Edge', + browser('Mozilla/5.0 (Linux; Android 11; Pixel 3 XL) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.116 ' + 'Mobile Safari/537.36 EdgA/45.07.4.5059') + ) + + def test_safari(self): + self.assertEqual( + 'Safari', + browser('Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; ja-jp) ' + 'AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 ' + 'Mobile/8C148 Safari/6533.18.5') + ) + self.assertEqual( + 'Safari', + browser('Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) ' + 'AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 ' + 'Mobile/11A465 Safari/9537.53') + ) + self.assertEqual( + 'Safari', + browser('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) ' + 'AppleWebKit/536.26.17 (KHTML, like Gecko) Version/6.0.2 ' + 'Safari/536.26.17') + ) + + self.assertEqual("Safari", browser("Not a legit OS Safari/5.2")) + + def test_chrome(self): + self.assertEqual( + 'Chrome', + browser('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/85.0.4178.0 Safari/537.36') + ) + + self.assertEqual( + 'Chrome', + browser('Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (' + 'KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36') + ) + + self.assertEqual("Chrome", browser("Not a legit OS Chrome/54.0.32")) + + def test_firefox(self): + self.assertEqual( + 'Firefox', + browser('Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:77.0) ' + 'Gecko/20100101 Firefox/77.0') + ) + + self.assertEqual( + 'Firefox', + browser('Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:22.0) ' + 'Gecko/20130328 Firefox/22.0') + ) + + self.assertEqual("Firefox", browser("Not a legit OS Firefox/51.0")) + + def test_android(self): + # androids identify themselves as Safari to get the good stuff + self.assertEqual( + 'Safari', + browser('Mozilla/5.0 (Linux; U; Android 1.5; de-de; HTC Magic ' + 'Build/CRB17) AppleWebKit/528.5+ (KHTML, like Gecko) ' + 'Version/3.1.2 Mobile Safari/525.20.1') + ) + + class DeviceTemplateFilterTest(TestCase): def test_ie(self): self.assertEqual( From 79e3ca9745507bea782b765fe4802534bd7a9e74 Mon Sep 17 00:00:00 2001 From: blag Date: Mon, 18 Sep 2023 14:46:05 -0700 Subject: [PATCH 4/8] Update docstrings --- user_sessions/templatetags/user_sessions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/user_sessions/templatetags/user_sessions.py b/user_sessions/templatetags/user_sessions.py index 238ff31..b7c0b44 100644 --- a/user_sessions/templatetags/user_sessions.py +++ b/user_sessions/templatetags/user_sessions.py @@ -42,7 +42,7 @@ @register.filter def platform(value): """ - Transform a User Agent into human readable text. + Transform the platform from a User Agent into human readable text. Example output: @@ -65,10 +65,10 @@ def platform(value): @register.filter def browser(value): """ - Transform a User Agent into human readable text. - + Transform the browser from a User Agent into human readable text. + Example output: - + * Safari * Chrome * Safari From d4ad4e0b60339255a26c41d666f196a044bdf08c Mon Sep 17 00:00:00 2001 From: blag Date: Mon, 18 Sep 2023 14:54:29 -0700 Subject: [PATCH 5/8] Document new 'browser' and 'platform' template tags --- docs/usage.rst | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 69c7a1b..84cf120 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -33,14 +33,26 @@ the box: Template tags ~~~~~~~~~~~~~ -Two template tags are included -:meth:`~user_sessions.templatetags.user_sessions.device` and -:meth:`~user_sessions.templatetags.user_sessions.location`. These can be used -for respectively humanizing the user agent string and showing an approximate -location of the IP address:: +Four template tags are included. + +The first three are +:meth:`~user_sessions.templatetags.user_sessions.browser`, which can be used +to get just the browser from a session, and +:meth:`~user_sessions.templatetags.user_sessions.platform`, which can be used +to get just the hardware or operating system from a session, and +:meth:`~user_sessions.templatetags.user_sessions.device`, which can be used to +get both from a sesesion:: {% load user_sessions %} {{ session.user_agent|device }} -> Safari on macOS + {{ session.user_agent|browser }} -> Safari + {{ session.user_agent|platform }} -> macOS + +The last one is +:meth:`~user_sessions.templatetags.user_sessions.location`, which can be used +to show an approximate location of the last IP address for a session:: + + {% load user_sessions %} {{ session.ip|location }} -> Zwolle, The Netherlands From fe1e1a5ed19cc4ec5addc4325e5309b2a43494ac Mon Sep 17 00:00:00 2001 From: blag Date: Mon, 18 Sep 2023 19:08:49 -0700 Subject: [PATCH 6/8] Use importlib.metadata instead of the deprecated/remove pkg_resources.get_distribution --- user_sessions/__init__.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/user_sessions/__init__.py b/user_sessions/__init__.py index 862f33f..ee12b74 100644 --- a/user_sessions/__init__.py +++ b/user_sessions/__init__.py @@ -1,7 +1,12 @@ -from pkg_resources import DistributionNotFound, get_distribution - try: - __version__ = get_distribution("django-user-sessions").version -except DistributionNotFound: - # package is not installed - __version__ = None + from importlib.metadata import version +except ImportError: + from pkg_resources import DistributionNotFound, get_distribution + + try: + __version__ = get_distribution("django-user-sessions").version + except DistributionNotFound: + # package is not installed + __version__ = None +else: + __version__ = version("django-user-sessions") From 9b69afffbb2cb266cda48a043a68d0ac2c213221 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 02:16:11 +0000 Subject: [PATCH 7/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/tests.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/tests.py b/tests/tests.py index fd31a3e..9f020de 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -15,7 +15,9 @@ from user_sessions.backends.db import SessionStore from user_sessions.models import Session -from user_sessions.templatetags.user_sessions import browser, device, location, platform +from user_sessions.templatetags.user_sessions import ( + browser, device, location, platform, +) from user_sessions.utils.tests import Client try: From 2130fe576765cbedb71935547c2dca64de9c7984 Mon Sep 17 00:00:00 2001 From: blag Date: Wed, 4 Oct 2023 20:20:39 -0700 Subject: [PATCH 8/8] Improve usage formatting --- docs/usage.rst | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 84cf120..50f2254 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -33,27 +33,28 @@ the box: Template tags ~~~~~~~~~~~~~ -Four template tags are included. - -The first three are -:meth:`~user_sessions.templatetags.user_sessions.browser`, which can be used -to get just the browser from a session, and -:meth:`~user_sessions.templatetags.user_sessions.platform`, which can be used -to get just the hardware or operating system from a session, and -:meth:`~user_sessions.templatetags.user_sessions.device`, which can be used to -get both from a sesesion:: - - {% load user_sessions %} - {{ session.user_agent|device }} -> Safari on macOS - {{ session.user_agent|browser }} -> Safari - {{ session.user_agent|platform }} -> macOS - -The last one is -:meth:`~user_sessions.templatetags.user_sessions.location`, which can be used -to show an approximate location of the last IP address for a session:: - - {% load user_sessions %} - {{ session.ip|location }} -> Zwolle, The Netherlands + +- ``browser`` - used to get just + the browser from a session +- ``platform`` - used to get just + the operating system from a session +- ``device`` - used to get both + the user's browser and the operating system from a session + + .. code-block:: html+django + + {% load user_sessions %} + {{ session.user_agent|device }} -> Safari on macOS + {{ session.user_agent|browser }} -> Safari + {{ session.user_agent|platform }} -> macOS + +- ``location`` - used to show an + approximate location of the last IP address for a session + + .. code-block:: html+django + + {% load user_sessions %} + {{ session.ip|location }} -> Zwolle, The Netherlands Admin views