Skip to main content

Simple Python wrapper for OpenTREP

Project description

Open Travel Request Parser (TREP)

Build Status Docker Repository on Quay

Table of content

Table of contents generated with markdown-toc

Overview

OpenTREP aims at providing a clean API, and the corresponding C++ implementation, for parsing travel-/transport-focused requests. It powers the https://transport-search.org Web site (as well as its newer version, https://www2.transport-search.org).

OpenTREP uses Xapian (https://www.xapian.org) for the Information Retrieval part, on freely available transport-/travel-related data (e.g., country names and codes, city names and codes, airline names and codes, etc), mainly to be found in the OpenTravelData (OPTD) project: http://github.com/opentraveldata/opentraveldata/tree/master/opentraveldata

OpenTREP exposes a simple, clean and object-oriented, API. For instance, the OPENTREP::interpretTravelRequest() method:

  • takes, as input, a character string containing the travel request;
  • and yields, as output, the list of the recognized terms, as well as their corresponding types.

As an example, the travel request Washington DC Beijing Monday a/r +AA -UA 1 week 2 adults 1 dog would yield the following list:

  • Origin airport: Washington, DC, United States (US)
  • Destination airport: Beijing, China (CN)
  • Date of travel: next Monday
  • Date of return: 1 week after next Monday
  • Preferred airline: American Airlines (AA); non-preferred airline: United Airlines
  • Number of travelers: 2 adults and a dog

The output may then be used by other systems, for instance to book the corresponding travel, or to visualize it on a map and calendar and to share it with others.

Note that the current version of OpenTREP recognizes only geographical POR (points of reference), whatever their number in the request. For instance, the request lviv rivne jitomir kbp kharkiv dnk ods lwo yields the following list of POR: LWO, RWN, ZTR, KBP, HRK, DNK, ODS and LWO again. See that request in action on the transport-search.org site or through the API (enable JSONView or similar for a more confortable reading).

OpenTREP also deals with transport-related requests. For instance, cnshg deham nlrtm uslbg brssz cnshg correspond to a world tour of famous ports:

The underlying data for the POR is the OpenTravelData optd_por_public_all.csv file. A good complementary tool is GeoBase, a Python-based software able to access any travel-related data source.

OpenTREP makes an extensive use of existing open-source libraries for increased functionality, speed and accuracy. In particular the Boost (C++ Standard Extensions), Xapian and SOCI libraries are used.

Docker images

OpenTREP Docker images

Docker images provide ready-to-use environments, and are available on Docker Cloud and Quay.io:

$ docker pull opentrep/search-travel:legacy # for Docker.io
$ docker pull quay.io/trep/opentrep # for Quay.io
$ docker run --rm -it opentrep/search-travel:legacy bash

See https://github.com/trep/opentrep/tree/master/gui/legacy for more details.

General purpose C++/Python Docker images

General purpose Docker images for C++/Python development are also available from Docker Cloud. Those Docker images allow to develop on the major Linux distributions, i.e., CentOS, Debian and Ubuntu.

CentOS 7

$ docker pull cpppythondevelopment/base:centos7
$ docker run -t cpppythondevelopment/base:centos7 bash
[build@2..c ~]$ $ mkdir -p ~/dev/geo && cd ~/dev/geo
[build@2..c geo]$ git clone https://github.com/trep/opentrep.git
[build@2..c geo]$ cd opentrep && mkdir build && cd build
[build@2..c build (master)]$ cmake3 -DCMAKE_INSTALL_PREFIX=${HOME}/dev/deliveries/opentrep-99.99.99 -DCMAKE_BUILD_TYPE:STRING=Debug -DINSTALL_DOC:BOOL=ON -DRUN_GCOV:BOOL=OFF -DLIB_SUFFIX= ..

Ubuntu 18.04 LTS Bionic Beaver

$ docker pull cpppythondevelopment/base:ubuntu1804
$ docker run -t cpppythondevelopment/base:ubuntu1804 bash
[build@2..c ~]$ $ mkdir -p ~/dev/geo && cd ~/dev/geo
[build@2..c geo]$ git clone https://github.com/trep/opentrep.git
[build@2..c geo]$ cd opentrep && mkdir build && cd build
[build@2..c build (master)]$ cmake -DCMAKE_INSTALL_PREFIX=${HOME}/dev/deliveries/opentrep-99.99.99 -DCMAKE_BUILD_TYPE:STRING=Debug -DINSTALL_DOC:BOOL=ON -DRUN_GCOV:BOOL=OFF -DLIB_SUFFIX= ..

Debian 9 Stretch

$ docker pull cpppythondevelopment/base:debian9
$ docker run -t cpppythondevelopment/base:debian9 bash
[build@2..c ~]$ $ mkdir -p ~/dev/geo && cd ~/dev/geo
[build@2..c geo]$ git clone https://github.com/trep/opentrep.git
[build@2..c geo]$ cd opentrep && mkdir build && cd build
[build@2..c build (master)]$ cmake -DCMAKE_INSTALL_PREFIX=${HOME}/dev/deliveries/opentrep-99.99.99 -DCMAKE_BUILD_TYPE:STRING=Debug -DINSTALL_DOC:BOOL=ON -DRUN_GCOV:BOOL=OFF -DLIB_SUFFIX= ..

Common to all the above-mentioned Linux distributions

[build@2..c build (master)]$ make install
[build@2..c build (master)]$ ./opentrep/opentrep-indexer
[build@2..c build (master)]$ ./opentrep/opentrep-searcher -q "nice san francisco"

Native installation (without Docker)

RPM-based distributions (eg, Fedora/CentOS/RedHat)

Since OpenTREP has been approved as an official package of Fedora/CentOS/RedHat (see the review request on Bugzilla for further details), just use DNF (or Yum for the older distributions):

$ dnf -y install opentrep opentrep-doc

Installation from the sources

Clone the Git repository

The GitHub repository may be cloned as following:

$ mkdir -p ~/dev/geo && cd ~/dev/geo
$ git clone https://github.com/trep/opentrep.git
$ cd opentrep
$ git checkout master

Alternatively, download and extract the tar-ball

GitHub generates tar-balls on the fly for every tagged release. For instance:

$ wget https://github.com/trep/opentrep/archive/opentrep-0.07.7.tar.gz

Note that SourceForge also stores some older archived tar-balls.

Installation of the dependencies

On Linux

The following packages may be needed (Fedora/RedHat/CentOS names on the left hand side, Debian/Ubuntu names on the right hand side; names for other Linux distributions may vary):

  • cmake (or cmake3 on CentOS)
  • gcc-c++ / gcc, g++
  • boost-devel (or boost169-devel on CentOS) / libboost-all-dev
  • xapian-core-devel / libxapian-dev
  • python-devel / python, libpython-dev
  • libicu-devel / libicu-dev
  • soci-mysql-devel, soci-sqlite3-devel / SOCI needs to be installed from the sources on Debian, Ubuntu and MacOS (see the dedicated section below)
  • sqlite3-devel / sqlite3, libsqlite3-dev
  • mariadb-devel / libmariadb-dev, libmysql++-dev
  • protobuf-devel, protobuf-compiler / libprotobuf-dev, protobuf-compiler
  • readline-devel / libreadline-dev, libncurses5-dev
  • ghostscript
  • doxygen
  • (optional) tetex-latex / texlive-latex-recommended
  • (optional) rpm-build

For instance, the following subsections show respective installation commands for a few famous Linux distributions.

Fedora
  • General C++-base stack:
$ dnf -y install git-all bash-completion gcc-c++ cmake boost-devel \
 xapian-core-devel soci-mysql-devel soci-sqlite3-devel readline-devel \
 sqlite-devel mariadb-devel libicu-devel protobuf-devel protobuf-compiler
  • Python development (the package names may vary; Python 2 is no longer supported):
$ dnf -y install python-devel python-pip
  • Documentation tools:
$ dnf -y install doxygen ghostscript "tex(latex)"
CentOS
  • If DNF is not already installed (e.g., on Cent OS 7):
$ yum -y install dnf
  • General C++-based stack:
$ dnf -y install git-all bash-completion gcc-c++ cmake boost169-devel \
 xapian-core-devel soci-mysql-devel soci-sqlite3-devel readline-devel \
 sqlite-devel mariadb-devel libicu-devel protobuf-devel protobuf-compiler
  • Python development (the package names may vary):
$ dnf -y install python3-devel python3-pip
  • Documentation tools:
$ dnf -y install doxygen ghostscript "tex(latex)"
Debian/Ubuntu
  • Various C++ and Python packages:
$ apt-get -y install locales && locale-gen "en_US.UTF-8"
$ apt-get -y install zlib1g-dev libbz2-dev lsb-release libgmp-dev \
   libgmp-dev gcc g++ clang cppcheck cmake libboost-all-dev libxapian-dev graphviz \
   libreadline-dev libncurses5-dev libczmq-dev libzmq3-dev libssl-dev libffi-dev \
   sqlite3 libsqlite3-dev libmariadb-dev libmysql++-dev postgresql-server-dev-all \
   libicu-dev libprotobuf-dev protobuf-compiler
  • Python development (the package names may vary)
$ apt-get -y install python3 libpython3-dev python3-pip
  • Documentation tools
$ apt-get -y install doxygen ghostscript texlive-latex-recommended

On MacOS

$ brew install boost boost-python3 cmake libedit \
  xapian sqlite mysql mysql-client icu4c protobuf protobuf-c doxygen
$ brew install homebrew/portable-ruby/portable-readline
  • Note that, as of June 2020, the Hombrew recipe for Python 3.8 (python@3.8) is not the default one for Python 3 (the default one being 3.7.7). It is not even installed in a default location.
    • The following setup was necessary:
$ sudo ln -s /usr/local/Cellar/python\@3.8/3.8.3 /usr/local/Cellar/python/3.8.3
# sudo unlink /usr/local/Frameworks/Python.framework/Versions/3.8
$ sudo ln -s /usr/local/Cellar/python/3.8.3/Frameworks/Python.framework/Versions/3.8 /usr/local/Frameworks/Python.framework/Versions/3.8
$ sudo unlink /usr/local/Frameworks/Python.framework/Versions/Current
$ sudo ln -s /usr/local/Cellar/python/3.8.2/Frameworks/Python.framework/Versions/Current /usr/local/Frameworks/Python.framework/Versions/Current
  • Leading to:
$ ls -lFh /usr/local/Cellar/python/
total 0
drwxr-xr-x  13 user  staff   416B Mar 12 11:34 3.7.7/
lrwxr-xr-x   1 user  staff    34B Apr  7 23:15 3.8.3@ -> /usr/local/Cellar/python@3.8/3.8.3
$ ls -lFh /usr/local/Frameworks/Python.framework/Versions/
total 0
lrwxr-xr-x  1 user  staff    69B Mar 12 11:34 3.7@ -> ../../../Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7
lrwxr-xr-x  1 user  staff    71B Apr  7 23:17 3.8@ -> /usr/local/Cellar/python/3.8.3/Frameworks/Python.framework/Versions/3.8
lrwxr-xr-x  1 user  staff    75B Apr  7 23:19 Current@ -> /usr/local/Cellar/python/3.8.3/Frameworks/Python.framework/Versions/Current
  • The OpenTREP Python extension will work only when Boost.Python and local Python are based on the same version. Up to end of May 2020, Boost 1.72 on MacOS was aligned with Python 3.8.2. However, at the end of May 2020, 3.8.3 became the default version for Python 3.8 on MacOS, while Boost 1.72 was still stuck with Python 3.8.2.

ICU

  • Install ICU with Homebrew
$ brew install icu4c

Boost

Follow the instructions on Boost helper documentation on GitHub to install Python and Boost on some platforms, including MacOS.

CentOS

$ cmake3 [...] \
  -DBOOST_LIBRARYDIR=/usr/lib64/boost169 -DBOOST_INCLUDEDIR=/usr/include/boost169 \
  -DBoost_ADDITIONAL_VERSIONS="1.69 1.69.0" \
  [...]

SOCI

  • On CentOS and Fedora, since the author of this package (OpenTREP) is also the official maintainer of the SOCI package, that latter is usually up-to-date

  • On Debian, Ubuntu and MacOS however, as of mid-2020, SOCI 4.0 has still not been released, and soci-mysql is no longer available. Hence, SOCI must be built from the sources. The following shows how to do that on MacOS (on Debian/Ubuntu, one can have a look at the part installing SOCI on the C++/Python Docker files)

  • Download and prepare SOCI:

$ sudo mkdir -p /opt/soci && sudo chown -R ${USER} /opt/soci
$ git clone https://github.com/SOCI/soci.git /opt/soci/socigit

General Unix/Linux

$ mkdir -p /opt/soci/socigit/build/head
$ pushd /opt/soci/socigit/build/head
$ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DSOCI_CXX11=ON -DSOCI_TESTS=OFF ../..
$ make
$ sudo make install
$ popd

Debian

$ wget https://github.com/trep/opentrep/raw/master/ci-scripts/soci-debian-cmake.patch -O /opt/soci/soci-debian-cmake.patch
$ pushd /opt/soci/socigit
$ patch -p1 < ../soci-debian-cmake.patch
$ popd
$ mkdir -p /opt/soci/socigit/build/head
$ pushd /opt/soci/socigit/build/head
$ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DSOCI_CXX11=ON -DSOCI_TESTS=OFF ../..
$ make
$ sudo make install
$ popd

MacOS

  • Configure SOCI:
$ mkdir -p /opt/soci/socigit/build/head && cd /opt/soci/socigit/build/head
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local \
  -DCMAKE_BUILD_TYPE=Debug -DSOCI_CXX_C11=ON \
  -DSOCI_ASAN=ON -DCMAKE_VERBOSE_MAKEFILE=OFF \
  -DSOCI_TESTS=OFF -DSOCI_STATIC=OFF -DSOCI_DB2=OFF -DSOCI_EMPTY=ON \
  -DSOCI_FIREBIRD=OFF -DSOCI_MYSQL=ON -DSOCI_ODBC=OFF -DSOCI_ORACLE=OFF \
  -DSOCI_POSTGRESQL=ON -DSOCI_SQLITE3=ON ../..
$ make
$ sudo make install

Building the library and test binary

To customize OpenTREP to your environment, you can alter the installation directory:

export INSTALL_BASEDIR="${HOME}/dev/deliveries"
export TREP_VER="0.07.7"
if [ -d /usr/lib64 ]; then LIBSUFFIX="64"; else LIBSUFFIX=""; fi
export LIBSUFFIX_4_CMAKE="-DLIB_SUFFIX=$LIBSUFFIX"

Then, as usual:

  • To configure the project, type something like:
$ mkdir build && cd build
$ cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_BASEDIR}/opentrep-${TREP_VER} \
   -DCMAKE_BUILD_TYPE:STRING=Debug -DENABLE_TEST:BOOL=ON \
   -DINSTALL_DOC:BOOL=ON -DRUN_GCOV:BOOL=OFF ${LIBSUFFIX_4_CMAKE} ..
  • With a specific version of Boost, say Boost 1.69 installed in a parallel way (for instance, as an optional module):
$ cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_BASEDIR}/opentrep-${TREP_VER} \
        -DCMAKE_BUILD_TYPE:STRING=Debug -DENABLE_TEST:BOOL=ON \
        -DBOOST_LIBRARYDIR=/usr/lib64/boost169 -DBOOST_INCLUDEDIR=/usr/include/boost169 \
        -DBoost_ADDITIONAL_VERSIONS="1.69 1.69.0" \
        -DINSTALL_DOC:BOOL=ON -DRUN_GCOV:BOOL=OFF ${LIBSUFFIX_4_CMAKE} ..
  • For an ad hoc installation:
INSTALL_DIR=/var/www/webapps/opentrep/trep

cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \
   -DCMAKE_BUILD_TYPE:STRING=Debug -DINSTALL_DOC:BOOL=OFF \
   -DRUN_GCOV:BOOL=OFF ${LIBSUFFIX_4_CMAKE} ..
  • On MacOS, a few software (e.g., ICU and Readline) are not in the standard place. So, the cmake command becomes:
$ export CMAKE_CXX_FLAGS="-Wno-mismatched-new-delete"; \
  cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_BASEDIR}/opentrep-$TREP_VER \
   -DREADLINE_ROOT=/usr/local/opt/portable-readline \
   -DREADLINE_INCLUDE_DIR=/usr/local/opt/portable-readline/include \
   -DREADLINE_LIBRARY=/usr/local/opt/libedit/lib/libedit.dylib \
   -DICU_ROOT=/usr/local/opt/icu4c \
   -DCMAKE_BUILD_TYPE:STRING=Debug -DINSTALL_DOC:BOOL=ON \
   -DRUN_GCOV:BOOL=OFF ${LIBSUFFIX_4_CMAKE} ..
  • To build the project, type:
$ make
  • To test the project, type:
$ make check
  • To install the library (libopentrep*.so*) and the binary (opentrep), just type:
  make install
  cd ${INSTALL_BASEDIR}
  rm -f opentrep-stable && ln -s opentrep-${TREP_VER} opentrep-stable
  cd -
  • To package the source files, type:
$ make dist
  • To package the binary and the documentation:
$ make package
  • Install the latest OpenTravelData (OPTD) POR data file. Note that OpenTREP no longer ships with (full) OPTD data files; only test files are shipped. The OPTD POR data file (optd_por_public_all.csv) has therefore to be downloaded aside, usually renamed as optd_por_public.csv, and the OpenTREP binaries have then to be referred to the file-path of that POR data file.
$ wget \
  https://github.com/opentraveldata/opentraveldata/raw/master/opentraveldata/optd_por_public_all.csv \
  -O ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
  • To run the local binary version:
$ ./opentrep/opentrep-{dbmgr,indexer,searcher}
  • To run the installed version (the first following line must be done once per session):
$ export TREP_LIB="${INSTALL_BASEDIR}/opentrep-${TREP_VER}/lib${LIBSUFFIX}"
$ export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${TREP_LIB}"
$ ${INSTALL_BASEDIR}/opentrep-${TREP_VER}/bin/opentrep-{dbmgr,indexer,searcher}

Underlying (relational) database, SQLite or MySQL/MariaDB, if any

OpenTREP may use, if so configured, a relational database. For now, two database products are supported, SQLite3 and MySQL/MariaDB. The database accelerates the look up of POR by (IATA, ICAO, FAA) codes and of Geonames ID. When OpenTREP is configured to run without database, those codes and Geonames ID are full-text searched directly with Xapian. Note that the database can be managed directly, i.e., without the OpenTREP search interface on top of it, thanks to the opentrep-dbmgr utility, which is detailed below.

Indexing the POR data

Filling the (relational) database, SQLite or MySQL/MariaDB, if any

Here, for clarity reason, we use the local version. It is easy (see above) to derive the same commands with the installed version.

  • The following command prompts a Shell:
$ ./opentrep/opentrep-dbmgr -t sqlite -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
  • Then, within the opentrep> shell, a typical sequence for SQLite would be:
 info
 create_user
 fill_from_por_file
 list_nb
 list_by_iata nce
 list_by_icao lfmn
 list_by_faa afm
 list_by_geonameid 6299418
  • The following command prompts a shell:
./opentrep/opentrep-dbmgr -t mysql -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
  • Then, within the opentrep> shell, a typical sequence for MySQL/MariaDB would be:
 reset_connection_string db=mysql user=root password=<passwd>
 create_user
 reset_connection_string db=trep_trep user=trep password=trep
 fill_from_por_file
 list_nb
 list_by_iata nce
 list_by_icao lfmn
 list_by_faa afm
 list_by_geonameid 6299418

Xapian indexing with standard installation

By default, the Xapian indexer runs without filling any relational database, as that step can be performed independantly by opentrep-dbmgr, as seen above.

  • Xapian indexing without any relational database:
$ ./opentrep/opentrep-indexer -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
  • Xapian indexing and filling and indexing the SQLite database:
$ ./opentrep/opentrep-indexer -t sqlite -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
  • Xapian indexing and filling and indexing the MySQL/MariaDB database:
$ ./opentrep/opentrep-indexer -t mysql -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
$ ./opentrep/opentrep-indexer -x 0 -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv

Xapian indexing for an ad hoc deployed Web application

  • Xapian indexing without any relational database:
$ ./opentrep/opentrep-indexer -d /var/www/webapps/opentrep/trep/traveldb -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
  • (Optional) Filling and indexing the SQLite database:
$ ./opentrep/opentrep-dbmgr -t sqlite -s /var/www/webapps/opentrep/trep/sqlite_travel.db -p ${INSTALL_BASEDIR}/share/opentrep/data/por/optd_por_public.csv
    create_user
    fill_from_por_file
    quit

Searching

  • Searching without any relational database support:
$ ./opentrep/opentrep-searcher -q "nce sfo"
  • Searching with SQLite (note that it should be quicker than without a database):
$ ./opentrep/opentrep-searcher -t sqlite -q "nce sfo"
  • Searching with MySQL/MariaDB:
$ ./opentrep/opentrep-searcher -t mysql -q "nce sfo"
  • Searching with SQLite (with Xapian and SQLite DB in a webapps directory):
$ ./opentrep/opentrep-searcher -d /var/www/webapps/opentrep/trep/traveldb -t sqlite -s /var/www/webapps/opentrep/trep/sqlite_travel.db -q "nce sfo"

Deployment stages

The idea is to have at least two pieces of infrastructure (SQL database, Xapian index) in parallel:

  • one is used by the production;
  • the other one used as a staging platform in order to test and validate a new version.

Once the new version has been validated, the two pieces of infrastructure can then be interverted, ie, the production becomes the new version, and the older version ends up in staging.

It means that all programs have to choose which version they want to work on. That version may even be toggled in live.

That method to deploy in production through a staging process is even more needed by the fact that indexing a new POR data file takes up to 30 minutes in the worst case. So, we cannot afford 30-45 minutes of downtime everytime a new POR data file is released (potentially every day).

With that staging process, it is even possible to fully automate the re-indexing after a new POR data file release: once the new release has been cleared by QA (Quality Assurance) on staging, it becomes production.

The corresponding command-line option for the various programs (opentrep-dbmgr, opentrep-indexer, opentrep-searcher) is -m.

  • For instance, to build the Xapian index/database for deployment number 0:
$ ./opentrep/opentrep-indexer -m 0 -d /var/www/webapps/opentrep/trep/traveldb
$ chown -R apache.apache /var/www/webapps/opentrep/trep
$ ls -laFh /var/www/webapps/opentrep/trep/
-rw-r--r-- 1 apache apache  16M Oct 14 2018 sqlite_travel.db0
drwxr-xr-x 2 apache apache 4.0K Oct 14 2018 traveldb0/
  • And to build the Xapian index/database for deployment number 1:
$ ./opentrep/opentrep-indexer -m 1 -d /var/www/webapps/opentrep/trep/traveldb 
$ chown -R apache.apache /var/www/webapps/opentrep/trep
$ ls -laFh /var/www/webapps/opentrep/trep/
-rw-r--r-- 1 apache apache  16M Oct 14 2018 sqlite_travel.db1
drwxr-xr-x 2 apache apache 4.0K Oct 14 2018 traveldb1/

Index, or not, non-IATA POR

There is also a command-line option, namely -n, to state whether or not the non-IATA-referenced POR should be included/parsed and indexed.

By default, and historically, only the POR, which are referenced by IATA (ie, which have a specific IATA code), are indexed (and may be searched for) in OpenTREP.

POR are also referenced by other international organizations, such as ICAO or UN/LOCODE, and may not be referenced by IATA (in which case their IATA code is left empty).

As of October 2018, there are around 110,000 POR in OpenTravelData (OPTD), the reference data source for OpenTREP:

  • Around 20,000 POR are referenced by IATA

  • Around 90,000 POR are not referenced by IATA, but referenced by other international organizations (eg, ICAO, UN/LOCODE)

  • Indexing 20,000 POR takes already a few minutes on standard hardware.

  • Indexing 110,000 POR would take 15 to 20 minutes.

Once indexed, all those POR become searchable. That flag is therefore only used at indexing time (ie, by the opentrep-dbmgr and opentrep-indexer programs).

Installing a Python virtual environment

All the details are explained on a dedicated procedure, which works for the major Linux distributions and on MacOS.

The procedure first installs a specific version of Python (as of June 2020, 3.8.3) thanks to Pyenv, then install pipenv thanks to the pip utility provided with that specific Python version.

Checking that the Python module works

  • With the standard installation:
PYTHON_VERSION=$(python3 --version 2>&1 | cut -d' ' -f2,2 | cut -d'.' -f1,2)
PYTHONPATH=${INSTALL_BASEDIR}/opentrep-${TREP_VER}/lib${LIBSUFFIX}:${INSTALL_BASEDIR}/opentrep-${TREP_VER}/lib${LIBSUFFIX}/python${PYTHON_VERSION}/site-packages/pyopentrep \
 python3 -c "import pyopentrep; \
 openTrepLibrary = pyopentrep.OpenTrepSearcher(); \
 initOK = openTrepLibrary.init (\"${INSTALL_BASEDIR}/opentrep-${TREP_VER}/share/opentrep/data/por/test_optd_por_public.csv\", '/tmp/opentrep/xapian_traveldb', 'sqlite', '/tmp/opentrep/sqlite_travel.db', 0, False, True, True, 'pyopentrep.log'); \
 print (openTrepLibrary.search ('S', 'los las'))"
  • With an ad hoc installation:
PYTHON_VERSION=$(python3 --version 2>&1 | cut -d' ' -f2,2 | cut -d'.' -f1,2)
PYTHONPATH=${INSTALL_DIR}/lib${LIBSUFFIX}:${INSTALL_BASEDIR}/lib${LIBSUFFIX}/python${PYTHON_VERSION}/site-packages/pyopentrep \
 python3 -c "import pyopentrep; \
 openTrepLibrary = pyopentrep.OpenTrepSearcher(); \
 initOK = openTrepLibrary.init (\"${INSTALL_BASEDIR}/opentrep-${TREP_VER}/share/opentrep/data/por/test_optd_por_public.csv\", '/var/www/webapps/opentrep/trep/traveldb', 'mysql', 'db=trep_trep user=trep password=trep', 0, False, True, True, 'pyopentrep.log'); \
 print (openTrepLibrary.search ('S', 'los las'))"

Trouble-shooting Python issues on MacOS

Interceptors not installed / late

  • On some versions of MacOS (e.g., with 10.15 aka Catalina), there may be some strange issue related to interceptors:
$ ./opentrep/python/pyopentrep -d /tmp/opentrep/xapian_traveldb "nce sfo"
==217==ERROR: Interceptors are not working. This may be because AddressSanitizer is loaded too late (e.g. via dlopen). Please launch the executable with:
DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
"interceptors not installed" && 0Abort trap: 6
  • A work around is to explicitly use the MacOS native Python interpreter. The whole command-line then becomes:
$ PYTHON_VERSION=$(python3 --version 2>&1 | cut -d' ' -f2,2 | cut -d'.' -f1,2)
$ PYTHONPATH=${INSTALL_BASEDIR}/opentrep-${TREP_VER}/lib${LIBSUFFIX}:${INSTALL_BASEDIR}/opentrep-${TREP_VER}/lib${LIBSUFFIX}/python${PYTHON_VERSION}/site-packages/pyopentrep \
  DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib \
  ASAN_OPTIONS=detect_container_overflow=0 \
  /usr/local/Cellar/python/3.8.3/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python \
  ./opentrep/python/pyopentrep.py -d /tmp/opentrep/xapian_traveldb "nce sfo"
OPTD-maintained list of POR (points of reference): '~/dev/deliveries/opentrep-${TREP_VER}/share/opentrep/data/por/test_optd_por_public.csv'
Xapian-based travel database/index: '/tmp/opentrep/xapian_traveldb0'
SQLite database: '/tmp/opentrep/sqlite_travel.db'
searchString: nce sfo
Compact format => recognised place (city/airport) codes:
NCE SFO
------------------

OpenTREP as a Python extension

References

Build and package OpenTREP as a Python extension

  • OpenTREP depends on Boost libraries, including Boost Python C extensions, and on Protobuf Python extensions too. Those Boost and Protobuf C extensions generally come with the system (e.g., installed by Homebrew on MacOS, DNF on Fedora/CentOS/RedHat, APT on Debian/Ubuntu).

  • Hence, installing a local virtual environment will not work, especially when the Python version of the virtual environment does not match exactly the Python version of the system-installed libraries.

  • The simplest approach so far is to use the Python installed by the system. Still, this can be done with pip, with that latter potentially installed on the user account only with the --user option when upgrading pip itself (with python3 -m pip install --user -U pip).

  • Note that Linux binary wheels cannot be pushed as is onto PyPi. Manylinux should be used for that. Scikit-build maintains some additions on top of Manylinux and the corresponding Manylinux Docker images. The way to run those have still to be documented below. In the meantime, the OpenTREP wheel has to be built from the sources, either (see below for the details):

Install Python modules/dependencies

  • Install Python 3 modules if not already done so:
$ pyenv global system
$ python3 -m pip install --user -U pip
$ python3 -m pip install -U setuptools cmake wheel ninja scikit-build
$ python3 -m pip install -U pytest tox twine keyrings.alt
$ python3 -m pip install -U simplejson protobuf

Install OpenTrep Python extension with system-based pip

  • OpenTREP extension/module is installed here as a system-based module with system-based Python and pip

  • Build from source and install with pip:

$ python3 -m pip install -U opentrep
Defaulting to user installation because normal site-packages is not writeable
Collecting opentrep
  Using cached opentrep-0.7.6.post1.tar.gz (1.7 MB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Building wheels for collected packages: opentrep
  Building wheel for opentrep (PEP 517) ... done
  Created wheel for opentrep: filename=opentrep-0.7.6.post1-cp38-cp38-linux_x86_64.whl size=3060796 sha256=6362e3a86af016b251fe33b9f76db17322ec15a60575082f31f6b719ba2cf97f
  Stored in directory: ~/.cache/pip/wheels/82/b3/7c/f026b883cc204eefab1588f5e68661f78fec25395277bd221d
Successfully built opentrep
Installing collected packages: opentrep
Successfully installed opentrep-0.7.6.post1
  • Set the LD_LIBRARY_PATH and PYTHONPATH environment variables
    • On MacOS:
$ export INST_DIR=/usr/local
$ export PYTHONPATH=${INST_DIR}/lib:${INST_DIR}/lib/python3.8/site-packages/pyopentrep
$ export DYLD_LIBRARY_PATH=${INST_DIR}/lib
  • On Linux:
$ INST_DIR=${HOME}/.local
$ TREPBINDIR=${INST_DIR}/bin
$ OPTDPOR=${INST_DIR}/share/opentrep/data/por/test_optd_por_public.csv
$ export LD_LIBRARY_PATH=${INST_DIR}/lib; export PYTHONPATH=${INST_DIR}/lib:${INST_DIR}/lib/python3.8/site-packages/pyopentrep
  • See how to use the newly installed extension in the dedicated sub-section below

Build the OpenTrep Python extension locally with system-based Scikit-build

  • Set Pyenv on the system-based Python, which should be Python 3:
$ pyenv global system
$ python --version
Python 3.8.3
  • Clean potential former builds and launch a new build with Scikit-build:
$ rm -rf _skbuild dist MANIFEST
$ python3 setup.py --build-type=Debug build sdist bdist_wheel # the build takes a few minutes
$ ls -lFh _skbuild/linux-x86_64-3.8/ dist/
dist/:
total 42M
-rw-rw-r-- 1 user staff  40M May 15 23:45 opentrep-0.7.6.post1-cp38-cp38-linux_x86_64.whl
-rw-rw-r-- 1 user staff 1.6M May 15 23:45 opentrep-0.7.6.post1.tar.gz

_skbuild/linux-x86_64-3.8/:
total 12K
drwxrwxr-x 8 user staff 4.0K May 15 23:45 cmake-build/
drwxrwxr-x 6 user staff 4.0K May 15 23:45 cmake-install/
drwxrwxr-x 3 user staff 4.0K May 15 23:45 setuptools/
  • Set the LD_LIBRARY_PATH and PYTHONPATH environment variables:
$ INST_DIR=${PWD}/_skbuild/macosx-10.15-x86_64-3.8/cmake-install
$ TREPBINDIR=${INST_DIR}/bin
$ OPTDPOR=${INST_DIR}/share/opentrep/data/por/test_optd_por_public.csv
$ export LD_LIBRARY_PATH=${INST_DIR}/lib; export PYTHONPATH=${INST_DIR}/lib:${INST_DIR}/lib/python3.8/site-packages/pyopentrep
  • Manylinux (for now, limited to Python2, so, not working):
$ docker pull scikitbuild/manylinux2010_x86_64:09d11d5f8
$ docker run --rm -e PLAT=manylinux2010_x86_64 -v `pwd`:/io scikitbuild/manylinux2010_x86_64:09d11d5f8 linux64 /io/travis/build-wheels.sh
  • Upload to PyPi (remember, no Linux binary wheel):
user@laptop$ PYPIURL="https://test.pypi.org"
user@laptop$ twine upload -u __token__ --repository-url ${PYPIURL}/legacy/ dist/*
Uploading distributions to https://test.pypi.org/legacy/
Uploading opentrep-0.7.6-cp38-cp38-macosx_10_15_x86_64.whl
100%|██████████████████████████████████████████████████████████████████████| 13.4M/13.4M [00:16<00:00, 853kB/s]
Uploading opentrep-0.7.6.macosx-10.15-x86_64.tar.gz
100%|██████████████████████████████████████████████████████████████████████| 13.2M/13.2M [00:13<00:00, 993kB/s]

View at:
https://test.pypi.org/project/opentrep/0.7.6/
user@laptop$ PYPIURL="https://upload.pypi.org"
user@laptop$ keyring set ${PYPIURL}/ __token__
Password for '__token__' in '${PYPIURL}/':
user@laptop$ twine upload -u __token__ --repository-url ${PYPIURL}/legacy/ dist/*
Uploading distributions to https://upload.pypi.org/legacy/
Uploading opentrep-0.7.6-cp38-cp38-macosx_10_15_x86_64.whl
100%|█████████████████████████████████████████████████████████████████████| 13.4M/13.4M [00:48<00:00, 293kB/s]
Uploading opentrep-0.7.6.macosx-10.15-x86_64.tar.gz
100%|█████████████████████████████████████████████████████████████████████| 13.2M/13.2M [00:49<00:00, 278kB/s]

View at:
https://pypi.org/project/opentrep/0.7.6/

Test the OpenTREP Python extension

  • Remove older version of and install the opentrep Python extension:
$ python3 -m pip uninstall opentrep
$ python3 -m pip install -U opentrep
  • Launch a simple end-to-end test with pytest
    • On Linux:
$ python3 -m pytest test_trep_e2e_simple.py
  • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.3/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python -m pytest test_trep_e2e_simple.py

Use the OpenTREP Python extension

Download the latest OpenTravelData (OPTD) POR data file

  • If not already done, install a few more Python modules:
$ python3 -m pip install -U opentrepwrapper opentraveldata
  • Download and use the latest POR data file
    • On Linux:
$ python3
Python 3.8.3 (default, June 2020) 
  • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.3/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python
  • Python interactive shell:
>>> import opentraveldata
>>> myOPTD = opentraveldata.OpenTravelData()
>>> myOPTD.downloadFilesIfNeeded()
>>> myOPTD
OpenTravelData:
	Local IATA/ICAO POR file: /tmp/opentraveldata/optd_por_public_all.csv
	Local UN/LOCODE POR file: /tmp/opentraveldata/optd_por_unlc.csv
>>> myOPTD.fileSizes()
(44044195, 4888086)

Xapian index initialization

  • Initialize the Xapian index with the -i option of pyopentrep.py, so as to index the full OpenTravelData (OPTD) POR (points of reference) data file
    • On Linux:
$ python3 ~/.local/lib/python3.8/site-packages/pyopentrep/pyopentrep.py -p /tmp/opentraveldata/optd_por_public_all.csv -i
  • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.3/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python /usr/local/lib/python3.8/site-packages/pyopentrep/pyopentrep.py -p /tmp/opentraveldata/optd_por_public_all.csv -i
OPTD-maintained list of POR (points of reference): '/tmp/opentraveldata/optd_por_public_all.csv'
Xapian-based travel database/index: '/tmp/opentrep/xapian_traveldb0'
SQLite database: '/tmp/opentrep/sqlite_travel.db'
Perform the indexation of the (Xapian-based) travel database.
That operation may take several minutes on some slow machines.
It takes less than 20 seconds on fast ones...
Number of actually parsed records: 1,000, out of 103,394 records in the POR data file so far
...
Number of actually parsed records: 20,000, out of 122,394 records in the POR data file so far
Done. Indexed 20335 POR (points of reference)
  • The Xapian index may also be initialized with the C++ (non-Python) opentrep-indexer utility (that is the former way of initializing the Xapian index, when it was not available from the Python utility):
$ ${TREPBINDIR}/opentrep-indexer -t sqlite -a 1 -p ${OPTDPOR}
POR file-path is: ~/.local/share/opentrep/data/por/test_optd_por_public.csv
Deployment number: 0
Xapian index/database filepath is: /tmp/opentrep/xapian_traveldb0
SQL database type is: sqlite
SQL database connection string is: /tmp/opentrep/sqlite_travel.db0
Are non-IATA-referenced POR included? 0
Index the POR in Xapian? 1
Add and re-index the POR in the SQL-based database? 1
Log filename is: opentrep-indexer.log
Parsing and indexing the OpenTravelData POR data file (into Xapian and/or SQL databases) may take a few tens of minutes on some architectures (and a few minutes on fastest ones)...
9 entries have been processed

Search with the OpenTrep Python extension

  • Use a Python wrapper script around the OpenTrep Python extension to search for terms
    • On Linux:
$ python3 ~/.local/lib/python3.8/site-packages/pyopentrep/pyopentrep.py
  • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.3/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python /usr/local/lib/python3.8/site-packages/pyopentrep/pyopentrep.py
OPTD-maintained list of POR (points of reference): '/tmp/opentrep/test_optd_por_public.csv'
Xapian-based travel database/index: '/tmp/opentrep/xapian_traveldb0'
SQLite database: '/tmp/opentrep/sqlite_travel.db'
searchString: sna francisco rio de janero los angeles reykyavki
Compact format => recognised place (city/airport) codes:
SFO RIO LAX REK
------------------
  • When the full POR data file has been indexed
    • On Linux:
$ python3 ~/.local/lib/python3.8/site-packages/pyopentrep/pyopentrep.py -f F "cnsha deham deess"
  • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.3/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python /usr/local/lib/python3.8/site-packages/pyopentrep/pyopentrep.py -f F "cnsha deham deess"
OPTD-maintained list of POR (points of reference): '/tmp/opentrep/test_optd_por_public.csv'
Xapian-based travel database/index: '/tmp/opentrep/xapian_traveldb0'
SQLite database: '/tmp/opentrep/sqlite_travel.db'
searchString: cnsha deham deess
Raw result from the OpenTrep library:
1. SHA-A-6301388, 34.7007%, Shanghai Hongqiao International Airport, Shanghai Hongqiao International Airport, ZSSS, , CNSHA, , 0, 1970-Jan-01, 2999-Dec-31, , SHA|1796236|Shanghai|Shanghai|CN|SH, SH, CN, , China, 713, China, CNY, NA, Asia, 31.1979, 121.336, S, AIRP, 23, Shanghai, Shanghai, , , , Z, , 0, 3, 3, Asia/Shanghai, 8, 8, 8, 2014-Aug-01, , https://en.wikipedia.org/wiki/Shanghai_Hongqiao_International_Airport, 31.1979, 121.336, cnsha, cnsha, 34.7007%, 0, 0
2. HAM-C-2911298, 12.8103%, Hamburg, Hamburg, , , DEHAM, , 0, 1970-Jan-01, 2999-Dec-31, , HAM|2911298|Hamburg|Hamburg|DE|HH, HH, DE, , Germany, 429, Germany, EUR, NA, Europe, 53.5753, 10.0153, P, PPLA, 04, Hamburg, Hamburg, 00, , , 02000, 02000000, 1739117, 0, 2, Europe/Berlin, 1, 2, 1, 2019-Nov-28, HAM,LBC,OBZ,XFW,ZMB, https://en.wikipedia.org/wiki/Hamburg, 53.5507, 9.99302, deham, deham, 12.8103%, 0, 0
3. ESS-C-2928810, 1.34094%, Essen, Essen, , , DEESS, , 0, 1970-Jan-01, 2999-Dec-31, , ESS|2928810|Essen|Essen|DE|NW, NW, DE, , Germany, 429, Germany, EUR, NA, Europe, 51.4566, 7.01228, P, PPLA3, 07, North Rhine-Westphalia, North Rhine-Westphalia, 051, Düsseldorf District, Duesseldorf District, 05113, 05113000, 593085, 0, 83, Europe/Berlin, 1, 2, 1, 2019-Oct-22, ESS,ESZ, https://en.wikipedia.org/wiki/Essen, 0, 0, deess, deess, 1.34094%, 0, 0
------------------
  • Use the OpenTrep Python extension from a Python interactive shell
    • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.3/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python
  • On Linux:
$ python3
Python 3.8.3 (default, June 2020)
>>> import pyopentrep
>>> openTrepLibrary = pyopentrep.OpenTrepSearcher()
>>> initOK = openTrepLibrary.init ('/tmp/opentraveldata/optd_por_public.csv', '/tmp/opentrep/xapian_traveldb', 'sqlite', '/tmp/opentrep/sqlite_travel.db', 0, False, True, True, 'pyopentrep.log')
>>> openTrepLibrary.search('S', 'nce sfo')
'NCE/0,SFO/0'
>>> openTrepLibrary.search('F', 'nce sfo')
"1. NCE-C-2990440, 8.16788%, Nice, Nice, , , FRNCE, , 0, 1970-Jan-01, 2999-Dec-31, , NCE|2990440|Nice|Nice|FR|PAC, PAC, FR, , France, 427, France, EUR, NA, Europe, 43.7031, 7.26608, P, PPLA2, 93, Provence-Alpes-Côte d'Azur, Provence-Alpes-Cote d'Azur, 06, Alpes-Maritimes, Alpes-Maritimes, 062, 06088, 338620, 25, 18, Europe/Paris, 1, 2, 1, 2019-Sep-05, NCE, https://en.wikipedia.org/wiki/Nice, 0, 0, NA, nce, 0%, 0, 0\n2. SFO-C-5391959, 32.496%, San Francisco, San Francisco, , , USSFO, , 0, 1970-Jan-01, 2999-Dec-31, , SFO|5391959|San Francisco|San Francisco|US|CA, CA, US, , United States, 91, California, USD, NA, North America, 37.7749, -122.419, P, PPLA2, CA, California, California, 075, City and County of San Francisco, City and County of San Francisco, Z, , 864816, 16, 28, America/Los_Angeles, -8, -7, -8, 2019-Sep-05, SFO, https://en.wikipedia.org/wiki/San_Francisco, 0, 0, NA, sfo, 0%, 0, 0\n"
>>> quit()

Search with the OpenTrepWrapper package

  • Use the OpenTREP wrapper on the full index
    • On Linux:
$ python3
Python 3.8.3 (default, June 2020, 15:53:34) 
  • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.2/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python
  • Python interactive shell:
>>> from OpenTrepWrapper import main_trep, index_trep
>>> main_trep (searchString = 'nce sfo', outputFormat = 'S',  xapianDBPath = '/tmp/opentrep/xapian_traveldb',  logFilePath = '/tmp/opentrep/opeentrep-searcher.log',  verbose = False)
([(173.579, 'NCE'), (390.644, 'SFO')], '')
>>> main_trep (searchString = 'cnsha deham', outputFormat = 'S',  xapianDBPath = '/tmp/opentrep/xapian_traveldb',  logFilePath = '/tmp/opentrep/opeentrep-searcher.log',  verbose = False)
([(0.34700699999999995, 'SHA'), (0.128103, 'HAM')], '')
>>> quit()

(Optional) Running the Django-based application server (needs update)

  • Install Django as a Python module:
$ python3 -m pip -U django
  • Go in the Django directory:
$ export TREP_TRAVELDB=/tmp/opentrep/traveldb
$ export TREP_LOG=django_trep.log
$ pushd gui/django/webapps/opentrep
  • Start the Django Web application server
    • On Linux:
$ python3 manage.py runserver localhost:8000
Python 3.8.2 (default, Apr 27 2020, 15:53:34) 
  • On MacOS:
$ DYLD_INSERT_LIBRARIES=/Library/Developer/CommandLineTools/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib ASAN_OPTIONS=detect_container_overflow=0 /usr/local/Cellar/python@3.8/3.8.2/Frameworks/Python.framework/Versions/3.8/Resources/Python.app/Contents/MacOS/Python manage.py runserver localhost:8000
  • Query OpenTREP with a web browser:
$ open "http://localhost:8000/search?q=rio de janero reykyavik sna francicso"

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

opentrep-0.7.7.post3.tar.gz (1.7 MB view hashes)

Uploaded Source

Built Distribution

opentrep-0.7.7.post3-cp38-cp38-macosx_10_15_x86_64.whl (10.2 MB view hashes)

Uploaded CPython 3.8 macOS 10.15+ x86-64

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page