Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
[submodule "pipeline/owid-python"]
path = pipeline/owid-python
url = https://github.com/SWAN-community/owid-python.git
[submodule "ip-intelligence/ip-intelligence-data"]
path = ip-intelligence/ip-intelligence-data
url = ../ip-intelligence-data
[submodule "cxx"]
path = cxx
url = ../51degrees-cxx
18 changes: 14 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,20 @@ so a pipeline change is tested with every product in the same run.

## Submodules

The C/C++ engines and the data files are submodules, listed in the root
`.gitmodules` at their paths under each product folder. The IP Intelligence
on-premise package must compile its own copy of the common C library with
large data file support; the Device Detection one without it.
The C/C++ engines are the `cxx` submodule, listed in the root `.gitmodules`.
The data files come through cxx's own `data` submodule, at
`cxx/data/device-detection` and `cxx/data/ip-intelligence`, where each free
file is a `<file>.link` that `cxx/data/get-data-files.ps1` or
`cxx/data/get-data-files.sh` downloads. Each product's `ci/fetch-assets.ps1`
gets the files its tests need, and the examples' `example_utils.py` runs the
script for a file that is missing. The IP Intelligence on-premise package must
compile its own copy of the common C library with large data file support;
the Device Detection one without it.

A package's `ExtraPaths` in `ci/packages.json` cannot be a folder inside
`cxx`, because git reports any change there as a change to `cxx` itself. A
cxx update therefore releases no package, even when it brings new translations
in `cxx/data/ip-intelligence`.

## History

Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,29 @@ Clone with the submodules:
git clone --recurse-submodules https://github.com/51Degrees/51degrees-python.git
```

The data files the examples and tests use are not stored in a repository. The
[data](https://github.com/51Degrees/data) repository, which is the `cxx/data`
submodule, holds a `.link` file for each free one, where the file goes, and
two scripts that download them. From the root of this repository, run

```
sh cxx/data/get-data-files.sh
```

or, in PowerShell,

```
./cxx/data/get-data-files.ps1
```

An example whose file is missing runs the script for that file itself.

Git leaves the folder of a removed submodule on disk. A clone made before the
data files moved to `cxx/data` still has an `ip-intelligence/ip-intelligence-data`
folder, and keeps `cxx/device-detection-data` and `cxx/ip-intelligence-data`
once cxx removes them. Delete those folders, so that nothing finds an old copy
of a data file in them.

Each product's README says what else it needs, such as a C/C++ compiler for
the on-premise engines and the data files.

Expand Down
2 changes: 0 additions & 2 deletions ci/packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@
"Name": "fiftyone_ipintelligence_onpremise",
"Product": "ip-intelligence",
"Path": "ip-intelligence/fiftyone_ipintelligence_onpremise",
"ExtraPaths": ["ip-intelligence/ip-intelligence-data"],
"DependsOn": [
"fiftyone_ipintelligence_shared",
"fiftyone_pipeline_engines_fiftyone"
Expand All @@ -153,7 +152,6 @@
"Name": "fiftyone_ipintelligence_translation",
"Product": "ip-intelligence",
"Path": "ip-intelligence/fiftyone_ipintelligence_translation",
"ExtraPaths": ["ip-intelligence/ip-intelligence-data"],
"DependsOn": [
"fiftyone_pipeline_core",
"fiftyone_pipeline_translation"
Expand Down
2 changes: 1 addition & 1 deletion cxx
Submodule cxx updated 72 files
+3 −0 .gitmodules
+205 −6 AGENTS.md
+32 −13 README.md
+17 −2 ci/fetch-assets.ps1
+2 −1 ci/products.ps1
+3 −3 ci/run-performance-tests.ps1
+1 −1 ci/run-unit-tests.ps1
+1 −0 data
+6 −9 docs/dd/README.md
+4 −4 docs/ipi/README.md
+7 −2 docs/path-map.md
+87 −3 examples/dd/C/Hash/ExampleBase.c
+17 −0 examples/dd/C/Hash/ExampleBase.h
+2 −2 examples/dd/C/Hash/FindProfiles.c
+8 −8 examples/dd/C/Hash/GettingStarted.c
+2 −2 examples/dd/C/Hash/MatchForDeviceId.c
+2 −2 examples/dd/C/Hash/MatchMetrics.c
+5 −5 examples/dd/C/Hash/MemHash.c
+8 −8 examples/dd/C/Hash/OfflineProcessing.c
+13 −12 examples/dd/C/Hash/Performance.c
+3 −2 examples/dd/C/Hash/ProcHash.c
+3 −3 examples/dd/C/Hash/ReloadFromFile.c
+3 −3 examples/dd/C/Hash/ReloadFromMemory.c
+2 −2 examples/dd/C/Hash/StronglyTyped.c
+12 −0 examples/dd/CPP/Hash/ExampleBase.cpp
+16 −1 examples/dd/CPP/Hash/ExampleBase.hpp
+1 −1 examples/dd/CPP/Hash/GettingStarted.cpp
+1 −1 examples/dd/CPP/Hash/MatchMetrics.cpp
+1 −1 examples/dd/CPP/Hash/MetaData.cpp
+2 −2 examples/dd/CPP/Hash/ReloadFromFile.cpp
+2 −2 examples/dd/CPP/Hash/ReloadFromMemory.cpp
+1 −1 examples/dd/CPP/Hash/StronglyTyped.cpp
+88 −4 examples/ipi/Base/ExampleBase.c
+17 −0 examples/ipi/Base/ExampleBase.h
+3 −2 examples/ipi/C/IpIntelligence/CountryOverlap.c
+3 −2 examples/ipi/C/IpIntelligence/FindProfiles.c
+3 −2 examples/ipi/C/IpIntelligence/GettingStarted.c
+3 −3 examples/ipi/C/IpIntelligence/MemIpi.c
+4 −4 examples/ipi/C/IpIntelligence/OfflineProcessing.c
+3 −3 examples/ipi/C/IpIntelligence/PerfIpi.c
+11 −12 examples/ipi/C/IpIntelligence/Performance.c
+3 −2 examples/ipi/C/IpIntelligence/ProcIpi.c
+3 −3 examples/ipi/C/IpIntelligence/ReloadFromFile.c
+3 −3 examples/ipi/C/IpIntelligence/ReloadFromMemory.c
+12 −0 examples/ipi/CPP/IpIntelligence/ExampleBase.cpp
+16 −1 examples/ipi/CPP/IpIntelligence/ExampleBase.hpp
+1 −1 examples/ipi/CPP/IpIntelligence/GettingStarted.cpp
+1 −1 examples/ipi/CPP/IpIntelligence/MetaData.cpp
+2 −2 examples/ipi/CPP/IpIntelligence/ReloadFromFile.cpp
+2 −2 examples/ipi/CPP/IpIntelligence/ReloadFromMemory.cpp
+1 −1 examples/ipi/CPP/IpIntelligence/StronglyTyped.cpp
+1 −1 test/both/dd.c
+1 −1 test/both/dd.cpp
+1 −1 test/both/ipi.c
+1 −1 test/both/ipi.cpp
+1 −1 test/dd/CMakeLists.txt
+1 −1 test/dd/Constants.hpp
+1 −1 test/ipi/Constants.hpp
+6 −6 test/ipi/DataFileHeaderTests.cpp
+1 −1 test/ipi/DataFilePathTests.cpp
+2 −2 test/ipi/EngineIpiAddressAnswerTests.cpp
+2 −2 test/ipi/EngineIpiBalancedTests.cpp
+2 −2 test/ipi/EngineIpiInitTests.cpp
+3 −3 test/ipi/IpiFirstReadFileTests.cpp
+1 −1 test/ipi/IpiFirstReadRuleTests.cpp
+3 −3 test/ipi/IpiFirstReadTests.cpp
+1 −1 test/ipi/IpiGraphLeafTests.cpp
+2 −2 test/ipi/IpiLoadFailureTests.cpp
+3 −3 test/ipi/IpiPositionsTests.cpp
+2 −2 test/ipi/IpiPropertyNamesTests.cpp
+2 −2 test/ipi/IpiReloadTests.cpp
+2 −2 test/ipi/IpiShortFileTests.cpp
12 changes: 11 additions & 1 deletion device-detection/ci/fetch-assets.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ if ($env:GITHUB_JOB -eq "PreBuild") {
exit 0
}

$deviceDetectionData = "$PSScriptRoot/../../cxx/device-detection-data"
$data = "$PSScriptRoot/../../cxx/data"
$deviceDetectionData = "$data/device-detection"

$assets = '51Degrees-LiteV4.1.hash', '20000 Evidence Records.yml', '20000 User Agents.csv'
if ($DeviceDetection) {
Expand All @@ -29,3 +30,12 @@ foreach ($asset in $assets) {
default {New-Item -ItemType SymbolicLink -Force -Target "$PWD/assets/$_" -Path "$deviceDetectionData/$_"}
}
}

# Downloads any other file the data repository links to in the folder. The
# script keeps a file that is already there, so the assets linked in above
# are not downloaded again.
$global:LASTEXITCODE = 0
& "$data/get-data-files.ps1" device-detection
if ($LASTEXITCODE -ne 0) {
throw "Not every data file in $deviceDetectionData was downloaded"
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

# First create the device detection pipeline with the desired settings.

data_file = "../../cxx/device-detection-data/51Degrees-LiteV4.1.hash"
data_file = "../../cxx/data/device-detection/51Degrees-LiteV4.1.hash"
resource_key = "!!YOUR_RESOURCE_KEY!!"

# Uncommment the line below 'On-Premise' and comment the line below 'Cloud' to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from datetime import datetime, timedelta
from threading import Event, Thread
import os
import shlex
import subprocess
from pathlib import Path


Expand Down Expand Up @@ -51,6 +53,15 @@ class ExampleUtils:
# file named in its configuration.
DATA_FILE_ENV_VAR = "51DEGREES_DD_PATH"

# The data repository's device detection folder, from the root of a
# checkout of this repository. Each file the data repository links to
# belongs in this folder, beside its .link file.
DATA_FOLDER = os.path.join("cxx", "data", "device-detection")

# Sent with each download, so the downloads these examples make can be
# counted.
DOWNLOAD_SOURCE = "examples-51degrees-python"

@staticmethod
def get_resource_key_from_config(config):
key = ""
Expand Down Expand Up @@ -183,12 +194,29 @@ def __get_env_variable(name):
else:
return ""

# Find the specified filename within the specified directory.
# If no directory is specified, the working directory is used.
# If the file cannot be found, the algorithm will move to the
# parent directory and repeat the process.
# Find the specified file. A path to a file that exists is returned as
# it is. Next, the data repository's folder is checked in the working
# directory and in each directory above it, and a file the data
# repository links to is downloaded there when it is missing. Otherwise
# the working directory is searched for a file with the same name, then
# each parent directory in turn. None is returned when nothing is found.
@staticmethod
def find_file(file_name):
path = Path(file_name).absolute()
if path.is_file():
return str(path.resolve())

# The data repository's copy is preferred to a file with the same
# name elsewhere in the tree, which may be out of date or a Git LFS
# pointer.
current = Path(os.getcwd()).absolute()
for root in [current, *current.parents]:
candidate = root / ExampleUtils.DATA_FOLDER / path.name
if candidate.is_file():
return str(candidate)
if candidate.with_name(candidate.name + ".link").is_file():
return ExampleUtils.__download(str(candidate))

stop_event = Event()
result = [None]
# Start the file system search as a separate task.
Expand Down Expand Up @@ -235,6 +263,39 @@ def __find_file(file_name, stop_event, result, search_dir=None):
if search_dir.parent != search_dir and not stop_event.is_set():
ExampleUtils.__find_file(file_name, stop_event, result, search_dir.parent)

# Download a file the data repository links to, by running the data
# repository's own script for that file, and return its path, or None
# when it could not be downloaded. The data repository is the folder
# above the one that holds the file's .link.
@staticmethod
def __download(path):
name = os.path.basename(path)
data = os.path.dirname(os.path.dirname(path))
if os.name == "nt":
command = [
"powershell", "-NoProfile", "-ExecutionPolicy", "Bypass",
"-File", os.path.join(data, "get-data-files.ps1"), name,
"-Source", ExampleUtils.DOWNLOAD_SOURCE]
text = subprocess.list2cmdline(command)
else:
command = [
"sh", os.path.join(data, "get-data-files.sh"),
"-s", ExampleUtils.DOWNLOAD_SOURCE, name]
text = shlex.join(command)

# Flushed first, because the script writes to the same output.
print(f"{name} is missing, so it is being downloaded by running\n"
f"{text}", flush=True)
try:
subprocess.run(command, check=False)
except OSError:
pass
if os.path.isfile(path):
return path
print(f"{name} could not be downloaded. Run this command to try "
f"again.\n{text}")
return None

@staticmethod
def get_data_file_date(engine):
date = engine.engine.getPublishedTime()
Expand Down Expand Up @@ -272,9 +333,10 @@ def check_data_file(pipeline, logger):
f"that is more than '{ExampleUtils.DATA_FILE_AGE_WARNING}' days " +
"old. A more recent data file may be needed to " +
"correctly detect the latest devices, browsers, " +
"etc. The latest lite data file is available from " +
"the device-detection-data repository on GitHub " +
"https://github.com/51Degrees/device-detection-data. " +
"etc. Get the latest lite data file by running " +
"cxx/data/get-data-files.sh -u 51Degrees-LiteV4.1.hash, " +
"or cxx/data/get-data-files.ps1 51Degrees-LiteV4.1.hash " +
"-Update on Windows. " +
"Find out about the Enterprise data file, which " +
"includes automatic updates, on our pricing page: " +
"https://51degrees.com/pricing?utm_source=code&utm_medium=example&utm_campaign=51degrees-python&utm_content=fiftyone_devicedetection_examples-src-fiftyone_devicedetection_examples-example_utils.py&utm_term=data-file-age-warning")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,8 @@
#
# ## Lite Data File
# Lite data files (free-to-use, limited capabilities, no license key required) are created roughly
# once a month and cannot be updated using auto-update, they may be downloaded from
# [Github](https://github.com/51Degrees/device-detection-data) and are included with
# source distributions of this software.
# once a month and cannot be updated using auto-update, they may be downloaded with the
# scripts in the [data](https://github.com/51Degrees/data) repository on GitHub.
#
# # Update on Start-Up
# You can configure the pipeline builder to download an Enterprise data file on start-up.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ def main(argv):
else:
logger.log("error",
"Failed to find a device detection " +
"data file. Make sure the device-detection-data " +
"submodule has been updated by running " +
"`git submodule update --recursive`.")
"data file. Make sure the data submodule is there, " +
"by running `git submodule update --init --recursive`, " +
"and run this example again to download the file.")

if __name__ == "__main__":
main(sys.argv[1:])
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,9 @@ def build_config():
if foundDataFile == False:
raise Exception("Failed to find a device detection data file matching " +
f"'{dataFile}'. If using the lite file, then make sure the " +
"device-detection-data submodule has been updated by running " +
"`git submodule update --recursive`. Otherwise, ensure that the filename " +
"data submodule is there, by running " +
"`git submodule update --init --recursive`, and run this " +
"example again to download the file. Otherwise, ensure that the filename " +
"is correct in config.json, or set the environment variable " +
f"'{ExampleUtils.DATA_FILE_ENV_VAR}' to the path of the data file.")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
<div class="c-eg-alert">
WARNING: This example is using a data file that is more than {{ utils.DATA_FILE_AGE_WARNING }}
days old. A more recent data file may be needed to correctly detect the latest devices,
browsers, etc. The latest lite data file is available from the
<a href="https://github.com/51Degrees/device-detection-data">device-detection-data</a>
browsers, etc. Get the latest lite data file with the scripts in the
<a href="https://github.com/51Degrees/data">data</a>
repository on GitHub. Find out about the Enterprise data file, which includes automatic
daily updates, on our <a href="https://51degrees.com/pricing?utm_source=code&utm_medium=example&utm_campaign=51degrees-python&utm_content=fiftyone_devicedetection_examples-src-fiftyone_devicedetection_examples-onpremise-gettingstarted_web-templates-index.html&utm_term=client-hints">pricing page</a>.
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,9 @@ def main(argv):
else:
logger.log("error",
"Failed to find a device detection data file. Make sure the " +
"device-detection-data submodule has been updated by running " +
"`git submodule update --recursive`.")
"data submodule is there, by running " +
"`git submodule update --init --recursive`, and run this " +
"example again to download the file.")

if __name__ == "__main__":
main(sys.argv[1:])
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ def main(argv):
else:
logger.log("error",
"Failed to find a device detection data file. Make sure the " +
"device-detection-data submodule has been updated by running " +
"`git submodule update --recursive`.")
"data submodule is there, by running " +
"`git submodule update --init --recursive`, and run this " +
"example again to download the file.")

if __name__ == "__main__":
main(sys.argv[1:])
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@ def main(argv):
else:
logger.log("error",
"Failed to find a device detection data file. Make sure the " +
"device-detection-data submodule has been updated by running " +
"`git submodule update --recursive`.")
"data submodule is there, by running " +
"`git submodule update --init --recursive`, and run this " +
"example again to download the file.")

if __name__ == "__main__":
main(sys.argv[1:])
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def run(data_file, skip = False):
if __name__ == "__main__":
ap = argparse.ArgumentParser(description='Run detection benchmark.')
ap.add_argument('-d', '--data_file', default='', help='Path to data file')
ap.add_argument('-u', '--user_agents_file', default='../../cxx/device-detection-data/20000 User Agents.csv', help='Path to user agents evidence file')
ap.add_argument('-u', '--user_agents_file', default='../../cxx/data/device-detection/20000 User Agents.csv', help='Path to user agents evidence file')
ap.add_argument('-j', '--json_output', default='', help='Output results in JSON format')
args = ap.parse_args()
if args.data_file == "":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ def main(argv):
ShareUsageExample().run(input)
else:
print("ERROR: Failed to find the evidence file. Make sure the "
"device-detection-data submodule has been updated by running "
"`git submodule update --recursive`.")
"data submodule is there, by running "
"`git submodule update --init --recursive`, and run this "
"example again to download the file.")


if __name__ == "__main__":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ def build_config():
if foundDataFile == False:
raise Exception("Failed to find a device detection data file matching " +
f"'{dataFile}'. If using the lite file, then make sure the " +
"device-detection-data submodule has been updated by running " +
"`git submodule update --recursive`. Otherwise, ensure that the filename " +
"data submodule is there, by running " +
"`git submodule update --init --recursive`, and run this " +
"example again to download the file. Otherwise, ensure that the filename " +
"is correct in config.json.")

return config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from fiftyone_pipeline_core.pipelinebuilder import PipelineBuilder
from fiftyone_pipeline_engines.lru_cache import LRUEngineCache

data_file = "../../cxx/device-detection-data/51Degrees-LiteV4.1.hash"
data_file = "../../cxx/data/device-detection/51Degrees-LiteV4.1.hash"

mobile_ua = "Mozilla/5.0 (iPhone; CPU iPhone OS 11_2 like Mac OS X) AppleWebKit/604.4.7 (KHTML, like Gecko) Mobile/15C114"

Expand All @@ -46,7 +46,7 @@

# User agents file

with open('../../cxx/device-detection-data/20000 User Agents.csv', newline='') as file:
with open('../../cxx/data/device-detection/20000 User Agents.csv', newline='') as file:
reader = csv.reader(file)
user_agents = list(reader)
no_user_agents = len(user_agents)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from fiftyone_devicedetection_shared.utils import *
from fiftyone_devicedetection_onpremise import constants

data_file = "../../cxx/device-detection-data/51Degrees-LiteV4.1.hash"
data_file = "../../cxx/data/device-detection/51Degrees-LiteV4.1.hash"
mobile_ua = ("Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) "
"AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile"
"/11D167 Safari/9537.53")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from fiftyone_devicedetection_onpremise.swig_data import SwigData
from fiftyone_devicedetection_shared.utils import *

data_file = "../../cxx/device-detection-data/51Degrees-LiteV4.1.hash"
data_file = "../../cxx/data/device-detection/51Degrees-LiteV4.1.hash"

# Create a simple pipeline to access the engine with and process it with flow data
deviceDetectionOnPremiseEngine = DeviceDetectionOnPremise(
Expand Down
Loading
Loading