\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 2023 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/main/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 # pylint: disable=E0402
48 from ..example_utils import ExampleUtils
49 from fiftyone_devicedetection_shared.example_constants import EVIDENCE_VALUES
50 from fiftyone_devicedetection_shared.example_constants import LITE_DATAFILE_NAME
51 
52 class MatchMetricsConsole():
53 
54  def run(self, data_file, show_descs, logger, output):
55  # Build a new Pipeline to use an on-premise Hash engine with the
56  # low memory performance profile.
57  pipeline = DeviceDetectionPipelineBuilder(
58  data_file_path = data_file,
59  # Inhibit auto-update of the data file for this example
60  auto_update = False,
61  licence_keys = "",
62  # Prefer low memory profile where all data streamed from disk
63  # on-demand. Experiment with other profiles.
64  performance_profile = "LowMemory",
65  #performance_profile = "HighPerformance",
66  #performance_profile = "Balanced",
67  # Disable share usage for this example.
68  usage_sharing = False,
69  # You can improve matching performance by specifying only those
70  # properties you wish to use. If you don't specify any properties
71  # you will get all those available in the data file tier that
72  # you have used. The free "Lite" tier contains fewer than 20.
73  # Since we are specifying properties here, we will only see
74  # those properties, along with the match metric properties
75  # in the output.
76  restricted_properties=["ismobile", "hardwarename"],
77  # Uncomment BrowserName to include Browser component profile ID
78  # in the device ID value.
79  #restricted_properties=["ismobile", "hardwarename", "browsername"],
80  # If using the full on-premise data file this property will be
81  # present in the data file. See https://51degrees.com/pricing
82  # Only use the predictive graph to better handle variances
83  # between the training data and the target User-Agent string.
84  # For a more detailed description of the differences between
85  # performance and predictive, see
86  # https://51degrees.com/documentation/_device_detection__hash.html#DeviceDetection_Hash_DataSetProduction_Performance
87  use_predictive_graph = True,
88  use_performance_graph = False,
89  # We want to show the matching evidence characters as part of this example, so we have to set
90  # this flag to true.
91  update_matched_useragent = True).add_logger(logger).build()
92 
93  ExampleUtils.check_data_file(pipeline, logger)
94 
95  data = pipeline.create_flowdata()
96 
97  # Process a single evidence to retrieve the values
98  # associated with the user-agent and other evidence such as sec-ch-* for the
99  # selected properties.
100  data.evidence.add_from_dict(self.Evidence)
101  data.process()
102 
103  device = data.device
104 
105  output("--- Compare evidence with what was matched ---\n")
106  output("Evidence")
107  # output the evidence in reverse value length order
108  for entry in sorted(self.Evidence.items(), key=lambda item: len(item[1]), reverse=True):
109  output(f" {entry[0]}: {entry[1]}")
110  # Obtain the matched User-Agents: the matched substrings in the
111  # User-Agents are separated with underscores - output in forward length order.
112  output("Matches")
113  for entry in sorted(device.useragents.value(), key=lambda item: len(item)):
114  output(f" Matched User-Agent: {entry}")
115 
116  output("")
117 
118 
119  output("--- Listing all available properties, by component, by property name ---")
120  output("For a discussion of what the match properties mean, see: https://51degrees.com/documentation/_device_detection__hash.html#DeviceDetection_Hash_DataSetProduction_Performance\n")
121 
122  # get the properties available from the DeviceDetection engine
123  # which has the key "device". For the sake of illustration we will
124  # retrieve it indirectly.
125  hashEngineElementKey = pipeline.get_element("device").datakey
126 
127  # retrieve the available properties from the hash engine. The properties
128  # available depends on
129  # a) the use of restricted_properties in the builder (see above)
130  # which controls which properties will be extracted, and also affects
131  # the performance of extraction
132  # b) the tier of data file being used. The Lite data file contains fewer
133  # than 20 of the >200 available properties
134  availableProperties = dict(pipeline.get_properties()[hashEngineElementKey])
135 
136  # create a Map keyed on the component name of the properties available
137  # components being hardware, browser, OS and Crawler.
138  def get_component(property):
139  return property[1]["component"]
140 
141  availableProperties = dict(sorted(availableProperties.items(),key=lambda item: get_component(item)))
142  categoryMap = groupby(availableProperties.items(), get_component)
143  # iterate the map created above
144  for component, properties in categoryMap:
145  output(component)
146  for propertyKey, property in properties:
147  propertyName = property["name"]
148  propertyDescription = property["description"]
149 
150  # while we get the available properties and their metadata from the
151  # pipeline we get the values for the last detection from flowData
152  value = device[propertyKey]
153 
154  # output property names, values and descriptions
155  # some property values are lists.
156  if value.has_value() and isinstance(value.value(), list):
157  output(f" {propertyName}: {len(value.value())} Values")
158  for item in value.value():
159  output(f" : {item}")
160 
161  else:
162  output(f" {propertyName}: {value.value()}")
163 
164  if (show_descs == True):
165  output(f" {propertyDescription}")
166 
167  output()
168  logger.log("info", "Finished Match Metrics Example")
169 
170  # Evidence values from a windows 11 device using a browser
171  # that supports User-Agent Client Hints.
172  Evidence = EVIDENCE_VALUES[2]
173 
174 def main(argv):
175  # In this example, by default, the 51degrees "Lite" file needs to be
176  # somewhere in the project space, or you may specify another file as
177  # a command line parameter.
178  #
179  # Note that the Lite data file is only used for illustration, and has
180  # limited accuracy and capabilities.
181  # Find out about the Enterprise data file on our pricing page:
182  # https://51degrees.com/pricing
183  data_file = argv[0] if len(argv) > 0 else ExampleUtils.find_file(LITE_DATAFILE_NAME)
184 
185  # Configure a logger to output to the console.
186  logger = Logger(min_level="info")
187 
188  if (data_file != None):
189  MatchMetricsConsole().run(data_file, False, logger, print)
190  else:
191  logger.log("error",
192  "Failed to find a device detection data file. Make sure the " +
193  "device-detection-data submodule has been updated by running " +
194  "`git submodule update --recursive`.")
195 
196 if __name__ == "__main__":
197  main(sys.argv[1:])