\r\n

51Degrees Device Detection Python  4.4

Device Detection services for 51Degrees Pipeline

onpremise/gettingstarted_web/app.py

This example is available in full on GitHub.

This example requires a local data file. The free 'Lite' data file can be acquired by pulling the git submodules under this repository (run `git submodule update --recursive`) or from the device-detection-data GitHub repository.

The Lite data file is only used for illustration, and has limited accuracy and capabilities. Find out about the more capable data files that are available on our pricing page

Required PyPi Dependencies:

Overview

The DeviceDetectionPipelineBuilder class is used to create a Pipeline instance from the configuration that is supplied. The fiftyone_pipeline_core.web module contains helpers which deal with automatically populating evidence from a web request.

1 flowdata.evidence.add_from_dict(webevidence(request))
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 
45 
46 # The module can also handling setting response headers (e.g. Accept-CH for User-Agent
47 # Client Hints) and serving requests for client-side JavaScript and JSON resources.
48 # ```{py}
49 # set_response_header(flowdata, response)
50 # ```
51 #
52 # The results of detection can be accessed by through the flowdata object once
53 # processed. This can then be used to interrogate the data.
54 # ```{py}
55 # flowdata.process()
56 # device = flowdata.device
57 # hardware_vendor = device.hardwarevendor
58 # ```
59 #
60 # Results can also be accessed in client-side code by using the `fod` object. See the
61 # [JavaScriptBuilderElementBuilder](https://51degrees.com/pipeline-python/4.3/classpipeline-python_1_1fiftyone__pipeline__core_1_1fiftyone__pipeline__core_1_1javascriptbuilde778a9036818b19ab55d981a40be4a4d7.html)
62 # for details on available settings such as changing the `fod` name.
63 # ```{js}
64 # window.onload = function () {
65 # fod.complete(function(data) {
66 # var hardwareName = data.device.hardwarename;
67 # alert(hardwareName.join(", "));
68 # }
69 # }
70 # ```
71 #
72 # ## View
73 # @include templates/index.html
74 #
75 # ## App
76 
77 import os
78 from pathlib import Path
79 import sys
81 from flask.helpers import make_response
82 from flask import Flask, request, render_template
83 from fiftyone_pipeline_core.logger import Logger
84 from fiftyone_pipeline_core.pipelinebuilder import PipelineBuilder
85 from fiftyone_pipeline_core.web import webevidence, set_response_header
86 import json
87 
88 class GettingStartedWeb():
89  app = Flask(__name__)
90 
91  def build(self, config, logger):
92  # Here we add some callback settings for the page to make a request with extra evidence from the client side, in this case the Flask /json route we will make below
93 
94  GettingStartedWeb.pipeline = PipelineBuilder().add_logger(logger).build_from_configuration(config)
95  return self
96 
97  def run(self):
98 
99  GettingStartedWeb.app.run()
100 
101  # First we make a JSON route that will be called from the client side and will return
102  # a JSON encoded property database using any additional evidence provided by the client
103 
104  @staticmethod
105  @app.route('/json', methods=['POST'])
106  def jsonroute():
107 
108  # Create the flowdata object for the JSON route
109  flowdata = GettingStartedWeb.pipeline.create_flowdata()
110 
111  # Add any information from the request (headers, cookies and additional
112  # client side provided information)
113 
114  flowdata.evidence.add_from_dict(webevidence(request))
115 
116  # Process the flowdata
117 
118  flowdata.process()
119 
120  # Return the JSON from the JSONBundler engine
121 
122  return json.dumps(flowdata.jsonbundler.json)
123 
124  # In the main route we dynamically update the screen's device property display
125  # using the above JSON route
126 
127  @staticmethod
128  @app.route('/')
129  def server():
130 
131  # Create the flowdata object for the JSON route
132  flowdata = GettingStartedWeb.pipeline.create_flowdata()
133 
134  # Add any information from the request (headers, cookies and additional
135  # client side provided information)
136 
137  flowdata.evidence.add_from_dict(webevidence(request))
138 
139  # Process the flowdata
140 
141  flowdata.process()
142 
143  response = make_response()
144 
145  # Some browsers require that extra HTTP headers are explicitly
146  # requested. So set whatever headers are required by the browser in
147  # order to return the evidence needed by the pipeline.
148  # More info on this can be found at
149  # https://51degrees.com/blog/user-agent-client-hints
150 
151  set_response_header(flowdata, response)
152 
153  # Generate the HTML
154  response.set_data(render_template(
155  'index.html',
156  data=flowdata,
157  utils=ExampleUtils,
158  response=response))
159 
160  return response
161 
162 
163  # Typically, something like this will not be necessary.
164  # The device detection API will accept an absolute or relative path for the data file.
165  # However, if a relative path is specified, it will only look in the current working
166  # directory.
167  # In our examples, we have many different projects and we don't want to have a copy of
168  # the data file for every single one.
169  # In order to handle this, we dynamically search the project directories for the data
170  # file location and then override the configured setting with the absolute path if
171  # necessary.
172  # In a real-world scenario, you can just put the data file in your working directory
173  # or use an absolute path in the configuration file.
174  @staticmethod
175  def build_config():
176 
177  # Load the configuration file
178  configFile = Path(__file__).resolve().parent.joinpath("config.json").read_text()
179  config = json.loads(configFile)
180 
181  dataFile = ExampleUtils.get_data_file_from_config(config)
182  foundDataFile = False
183  if not dataFile:
184  raise Exception("A data file must be specified in the config.json file.")
185  # The data file location provided in the configuration may be using an absolute or
186  # relative path. If it is relative then search for a matching file using the
187  # ExampleUtils.find_file function.
188  elif os.path.isabs(dataFile) == False:
189  newPath = ExampleUtils.find_file(dataFile)
190  if newPath:
191  # Add an override for the absolute path to the data file.
192  ExampleUtils.set_data_file_in_config(config, newPath)
193  foundDataFile = True
194  else:
195  foundDataFile = os.path.exists(dataFile)
196 
197  if foundDataFile == False:
198  raise Exception("Failed to find a device detection data file matching " +
199  f"'{dataFile}'. If using the lite file, then make sure the " +
200  "device-detection-data submodule has been updated by running " +
201  "`git submodule update --recursive`. Otherwise, ensure that the filename " +
202  "is correct in config.json.")
203 
204  return config
205 
206 def main(argv):
207  # Configure a logger to output to the console.
208  logger = Logger(min_level="info")
209 
210  config = GettingStartedWeb.build_config()
211 
212  GettingStartedWeb().build(config, logger).run()
213 
214 if __name__ == "__main__":
215  main(sys.argv[1:])