\r\n

51Degrees Device Detection Python  4.4

Device Detection services for 51Degrees Pipeline

onpremise/match_metrics.py

The example illustrates the various metrics that can be obtained about the device detection process, for example, the degree of certainty about the result. Running the example outputs those properties and values.

1 # *********************************************************************
2 # This Original Work is copyright of 51 Degrees Mobile Experts Limited.
3 # Copyright 2022 51 Degrees Mobile Experts Limited, Davidson House,
4 # Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
5 #
6 # This Original Work is licensed under the European Union Public Licence
7 # (EUPL) v.1.2 and is subject to its terms as set out below.
8 #
9 # If a copy of the EUPL was not distributed with this file, You can obtain
10 # one at https://opensource.org/licenses/EUPL-1.2.
11 #
12 # The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
13 # amended by the European Commission) shall be deemed incompatible for
14 # the purposes of the Work and the provisions of the compatibility
15 # clause in Article 5 of the EUPL shall not apply.
16 #
17 # If using the Work as, or as part of, a network application, by
18 # including the attribution notice(s) required under Article 5 of the EUPL
19 # in the end user terms of the application under an appropriate heading,
20 # such notice(s) shall fulfill the requirements of that article.
21 # *********************************************************************
22 
23 
24 
29 
30 # The example also illustrates controlling properties that are returned from the detection
31 # process - reducing the number of components required to return the properties requested reduces
32 # the overall time taken.
33 #
34 # There is a (discussion)[https://51degrees.com/documentation/_device_detection__hash.html#DeviceDetection_Hash_DataSetProduction_Performance]
35 # of metrics and controlling performance on our web site. See also the (performance options)
36 # [https://51degrees.com/documentation/_device_detection__features__performance_options.html]
37 # page.
38 # # Location
39 # This example is available in full on (GitHub)[https://github.com/51Degrees/device-detection-python/blob/master/fiftyone_devicedetection_examples/fiftyone_devicedetection_examples/onpremise/match_metrics.py].
40 
41 from itertools import groupby
42 from pathlib import Path
43 import sys
44 from fiftyone_devicedetection.devicedetection_pipelinebuilder import DeviceDetectionPipelineBuilder
45 from fiftyone_pipeline_core.logger import Logger
46 from fiftyone_pipeline_core.basiclist_evidence_keyfilter import BasicListEvidenceKeyFilter
47 from ..example_utils import ExampleUtils
48 from fiftyone_devicedetection_shared.example_constants import EVIDENCE_VALUES
49 from fiftyone_devicedetection_shared.example_constants import LITE_DATAFILE_NAME
50 
51 class MatchMetricsConsole():
52 
53  def run(self, data_file, show_descs, logger, output):
54  # Build a new Pipeline to use an on-premise Hash engine with the
55  # low memory performance profile.
56  pipeline = DeviceDetectionPipelineBuilder(
57  data_file_path = data_file,
58  # Inhibit auto-update of the data file for this example
59  auto_update = False,
60  licence_keys = "",
61  # Prefer low memory profile where all data streamed from disk
62  # on-demand. Experiment with other profiles.
63  performance_profile = "LowMemory",
64  #performance_profile = "HighPerformance",
65  #performance_profile = "Balanced",
66  # Disable share usage for this example.
67  usage_sharing = False,
68  # You can improve matching performance by specifying only those
69  # properties you wish to use. If you don't specify any properties
70  # you will get all those available in the data file tier that
71  # you have used. The free "Lite" tier contains fewer than 20.
72  # Since we are specifying properties here, we will only see
73  # those properties, along with the match metric properties
74  # in the output.
75  restricted_properties=["ismobile", "hardwarename"],
76  # Uncomment BrowserName to include Browser component profile ID
77  # in the device ID value.
78  #restricted_properties=["ismobile", "hardwarename", "browsername"],
79  # If using the full on-premise data file this property will be
80  # present in the data file. See https://51degrees.com/pricing
81  # Only use the predictive graph to better handle variances
82  # between the training data and the target User-Agent string.
83  # For a more detailed description of the differences between
84  # performance and predictive, see
85  # https://51degrees.com/documentation/_device_detection__hash.html#DeviceDetection_Hash_DataSetProduction_Performance
86  use_predictive_graph = True,
87  use_performance_graph = False,
88  # We want to show the matching evidence characters as part of this example, so we have to set
89  # this flag to true.
90  update_matched_useragent = True).add_logger(logger).build()
91 
92  ExampleUtils.check_data_file(pipeline, logger)
93 
94  data = pipeline.create_flowdata()
95 
96  # Process a single evidence to retrieve the values
97  # associated with the user-agent and other evidence such as sec-ch-* for the
98  # selected properties.
99  data.evidence.add_from_dict(self.Evidence)
100  data.process()
101 
102  device = data.device
103 
104  output("--- Compare evidence with what was matched ---\n")
105  output("Evidence")
106  # output the evidence in reverse value length order
107  for entry in sorted(self.Evidence.items(), key=lambda item: len(item[1]), reverse=True):
108  output(f" {entry[0]}: {entry[1]}")
109  # Obtain the matched User-Agents: the matched substrings in the
110  # User-Agents are separated with underscores - output in forward length order.
111  output("Matches")
112  for entry in sorted(device.useragents.value(), key=lambda item: len(item)):
113  output(f" Matched User-Agent: {entry}")
114 
115  output("")
116 
117 
118  output("--- Listing all available properties, by component, by property name ---")
119  output("For a discussion of what the match properties mean, see: https://51degrees.com/documentation/_device_detection__hash.html#DeviceDetection_Hash_DataSetProduction_Performance\n")
120 
121  # get the properties available from the DeviceDetection engine
122  # which has the key "device". For the sake of illustration we will
123  # retrieve it indirectly.
124  hashEngineElementKey = pipeline.get_element("device").datakey
125 
126  # retrieve the available properties from the hash engine. The properties
127  # available depends on
128  # a) the use of restricted_properties in the builder (see above)
129  # which controls which properties will be extracted, and also affects
130  # the performance of extraction
131  # b) the tier of data file being used. The Lite data file contains fewer
132  # than 20 of the >200 available properties
133  availableProperties = dict(pipeline.get_properties()[hashEngineElementKey])
134 
135  # create a Map keyed on the component name of the properties available
136  # components being hardware, browser, OS and Crawler.
137  def get_component(property):
138  return property[1]["component"]
139 
140  availableProperties = dict(sorted(availableProperties.items(),key=lambda item: get_component(item)))
141  categoryMap = groupby(availableProperties.items(), get_component)
142  # iterate the map created above
143  for component, properties in categoryMap:
144  output(component)
145  for propertyKey, property in properties:
146  propertyName = property["name"]
147  propertyDescription = property["description"]
148 
149  # while we get the available properties and their metadata from the
150  # pipeline we get the values for the last detection from flowData
151  value = device[propertyKey]
152 
153  # output property names, values and descriptions
154  # some property values are lists.
155  if value.has_value() and isinstance(value.value(), list):
156  output(f" {propertyName}: {len(value.value())} Values")
157  for item in value.value():
158  output(f" : {item}")
159 
160  else:
161  output(f" {propertyName}: {value.value()}")
162 
163  if (show_descs == True):
164  output(f" {propertyDescription}")
165 
166  output()
167  logger.log("info", "Finished Match Metrics Example")
168 
169  # Evidence values from a windows 11 device using a browser
170  # that supports User-Agent Client Hints.
171  Evidence = EVIDENCE_VALUES[2]
172 
173 def main(argv):
174  # In this example, by default, the 51degrees "Lite" file needs to be
175  # somewhere in the project space, or you may specify another file as
176  # a command line parameter.
177  #
178  # Note that the Lite data file is only used for illustration, and has
179  # limited accuracy and capabilities.
180  # Find out about the Enterprise data file on our pricing page:
181  # https://51degrees.com/pricing
182  data_file = argv[0] if len(argv) > 0 else ExampleUtils.find_file(LITE_DATAFILE_NAME)
183 
184  # Configure a logger to output to the console.
185  logger = Logger(min_level="info")
186 
187  if (data_file != None):
188  MatchMetricsConsole().run(data_file, False, logger, print)
189  else:
190  logger.log("error",
191  "Failed to find a device detection data file. Make sure the " +
192  "device-detection-data submodule has been updated by running " +
193  "`git submodule update --recursive`.")
194 
195 if __name__ == "__main__":
196  main(sys.argv[1:])