\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:

  • fiftyone_devicedetection
  • flask

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 2019 51 Degrees Mobile Experts Limited, 5 Charlotte Close,
4 # Caversham, Reading, Berkshire, United Kingdom RG4 7BY.
5 #
6 # This Original Work is licensed under the European Union Public Licence (EUPL)
7 # 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
80 from fiftyone_devicedetection_examples.example_utils import ExampleUtils
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 *
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  @app.route('/json', methods=['POST'])
105  def jsonroute():
106 
107  # Create the flowdata object for the JSON route
108  flowdata = GettingStartedWeb.pipeline.create_flowdata()
109 
110  # Add any information from the request (headers, cookies and additional
111  # client side provided information)
112 
113  flowdata.evidence.add_from_dict(webevidence(request))
114 
115  # Process the flowdata
116 
117  flowdata.process()
118 
119  # Return the JSON from the JSONBundler engine
120 
121  return json.dumps(flowdata.jsonbundler.json)
122 
123  # In the main route we dynamically update the screen's device property display
124  # using the above JSON route
125 
126  @app.route('/')
127  def server():
128 
129  # Create the flowdata object for the JSON route
130  flowdata = GettingStartedWeb.pipeline.create_flowdata()
131 
132  # Add any information from the request (headers, cookies and additional
133  # client side provided information)
134 
135  flowdata.evidence.add_from_dict(webevidence(request))
136 
137  # Process the flowdata
138 
139  flowdata.process()
140 
141  response = make_response()
142 
143  # Some browsers require that extra HTTP headers are explicitly
144  # requested. So set whatever headers are required by the browser in
145  # order to return the evidence needed by the pipeline.
146  # More info on this can be found at
147  # https://51degrees.com/blog/user-agent-client-hints
148 
149  set_response_header(flowdata, response)
150 
151  # Generate the HTML
152  response.set_data(render_template(
153  'index.html',
154  data=flowdata,
155  utils=ExampleUtils,
156  response=response))
157 
158  return response
159 
160 
161  # Typically, something like this will not be necessary.
162  # The device detection API will accept an absolute or relative path for the data file.
163  # However, if a relative path is specified, it will only look in the current working
164  # directory.
165  # In our examples, we have many different projects and we don't want to have a copy of
166  # the data file for every single one.
167  # In order to handle this, we dynamically search the project directories for the data
168  # file location and then override the configured setting with the absolute path if
169  # necessary.
170  # In a real-world scenario, you can just put the data file in your working directory
171  # or use an absolute path in the configuration file.
172  @staticmethod
173  def build_config():
174 
175  # Load the configuration file
176  configFile = Path(__file__).resolve().parent.joinpath("config.json").read_text()
177  config = json.loads(configFile)
178 
179  dataFile = ExampleUtils.get_data_file_from_config(config)
180  foundDataFile = False
181  if not dataFile:
182  raise Exception("A data file must be specified in the config.json file.")
183  # The data file location provided in the configuration may be using an absolute or
184  # relative path. If it is relative then search for a matching file using the
185  # ExampleUtils.find_file function.
186  elif os.path.isabs(dataFile) == False:
187  newPath = ExampleUtils.find_file(dataFile)
188  if newPath:
189  # Add an override for the absolute path to the data file.
190  ExampleUtils.set_data_file_in_config(config, newPath)
191  foundDataFile = True
192  else:
193  foundDataFile = os.path.exists(dataFile)
194 
195  if foundDataFile == False:
196  raise Exception("Failed to find a device detection data file matching " +
197  f"'{dataFile}'. If using the lite file, then make sure the " +
198  "device-detection-data submodule has been updated by running " +
199  "`git submodule update --recursive`. Otherwise, ensure that the filename " +
200  "is correct in config.json.")
201 
202  return config
203 
204 def main(argv):
205  # Configure a logger to output to the console.
206  logger = Logger(min_level="info")
207 
208  config = GettingStartedWeb.build_config()
209 
210  GettingStartedWeb().build(config, logger).run()
211 
212 if __name__ == "__main__":
213  main(sys.argv[1:])