Get it running

Install and configure

Database::BI is a normal Perl distribution with a Mojolicious application inside it. Install it with cpanm, point it at a directory of data, and start the server.

Requirements

The two version floors that matter; the rest are ordinary CPAN modules.
RequirementVersionWhy
Perl 5.28 Subroutine attributes have to precede a signature, which older perls cannot parse.
Mojolicious 9.49 The web framework the application is built on.
Database::Abstraction 0.54 Reads the data files; 0.54 reads a table named data, so a file from Export > SQLite opens again.
Database::Join 0.009.0 Left joins, with spill-to-SQLite for large results.
HTML::D3 0.27 The chart snippets. 0.27 stops a label ending the script it is written into.
HTML::TagCloud 0.38 Word clouds.

Two modules are recommended rather than required, so the application installs, starts and tests without them: HTML::OSM (0.12 or later) draws maps, and Geo::Coder::Free (0.48 or later) turns a place column into coordinates from a local database. Without them the map pages are hidden and the rest of the application is unaffected. Redis (or Redis::Fast) is optional too, and switches on the redis: data source when installed.

DBD::MariaDB (or DBD::mysql) is optional too, and is what a mariadb: source connects with. t/mariadb.t runs without a server; t/mariadb_live.t runs when MARIADB_SERVER is set, and creates its own tables and drops them.

MARIADB_SERVER=mariadb://user:pass@host/test prove -lv t/mariadb_live.t

Install

# from CPAN
cpanm Database::BI

# or from a checkout
cpanm --installdeps .
perl Makefile.PL
make
make test

Run it

# development: restarts when a source file is edited
script/database-bi

# the same thing, spelled out
morbo script/database-bi

# the same, showing one database at the home address
script/database-bi data/store.db

# production: a preforking server; start, or restart with no downtime
hypnotoad script/database-bi

# stop it
hypnotoad -s script/database-bi

With no arguments the launcher starts the development server itself; any other Mojolicious command word (routes, get, …) is passed through to the application. The preforking server is the one to put behind a reverse proxy for long-running use. It listens on port 8080 and ignores MOJO_LISTEN; set the port in database_bi.conf, as hypnotoad => { listen => ['http://*:3000'] }. It keeps its process id in script/hypnotoad.pid unless pid_file says otherwise.

Given the name of a database, the launcher starts the same development server, and the home address shows that database. The Home link in its breadcrumb leads to the usual list. It takes a data file, a remote path, a Redis spec or a MariaDB spec, as /open does.

Only one can be named, because a join cannot be given on the command line: open one and use Merge or Combine in the browser. Naming more than one, or a file that is missing, cannot be read or is of an unsupported type, prints the reason and exits with status 2 before any server starts.

MOJO_LISTEN chooses the port. script/database-bi daemon ignores a file named after it, and so does hypnotoad; to show a database from either, set BI_OPEN:

BI_OPEN=/full/path/store.db script/database-bi daemon
BI_OPEN=/full/path/store.db hypnotoad script/database-bi

The launcher's #! line must name perl, not morbo. morbo starts the application as perl script/database-bi daemon, and perl hands a script whose shebang names another interpreter to that interpreter — so a morbo shebang starts a morbo inside a morbo, one process per second, until exec runs out of argument space.

The launcher's last statement must be its call to start_app. hypnotoad loads the file instead of running it, and takes the value of that statement as the application.

Configuration

This is for whoever runs the application. A reader's own choices, such as light or dark, are not set here: see Preferences.

Create database_bi.conf in the application root next to script/. It is optional, but if it exists it must be valid Perl and must return a hashref:

{
    data_dir  => 'data',          # scanned by the home page
    model_dir => '.bi',           # analyses.json, models.json
    language  => 'en',            # template directory under templates/web/
    platform  => 'web',
}
Keys you are most likely to set. Unset keys fall back to the defaults shown.
KeyDefaultEffect
data_dir data Directory of files the home page offers. A relative path is resolved against the application home, not the process working directory.
model_dir .bi Where saved canvases, calculated fields, groups, date patterns and geocoded places live. It may be absolute, but it may not be equal to or inside data_dir.
uploads_dir .uploads Staging directory for files dropped on the page. Point it somewhere writable if the application directory is read-only.
language en Template language. Adding one means adding templates/web/<lang>/, not just changing the key.
page_cache_dir unset Keeps /catalog and /tutorial on disc. Data pages are never cached, because the files behind them change.
ai_api_key unset Enables the natural-language question and summary endpoints. Off unless you set it; prompts carry column names and aggregate text only.

Serving your own D3 and Leaflet

Chart pages load D3 from a CDN and map pages load Leaflet from unpkg by default. A deployment with no outbound network access, or one that prefers its own assets, points the keys at local copies under public/:

{
    d3_url                 => '/static/d3/d3.min.js',
    leaflet_js_url         => '/static/leaflet/leaflet.js',
    leaflet_css_url        => '/static/leaflet/leaflet.css',
    leaflet_cluster_js_url => '/static/leaflet/leaflet.markercluster.js',
    tile_url               => 'https://tiles.internal/{z}/{x}/{y}.png',
}

That is public/static/d3/d3.min.js and so on. The versions to copy are the ones the defaults name: D3 7.9.0, Leaflet 1.9.4 and leaflet.markercluster 1.5.3. With tile_url set, every request needed to draw a map can stay on the local network.

Command-line tools

script/bi-geocode

Warms the shared geocode cache for one column, so later map requests make no lookups:

./script/bi-geocode --spec table:customers --col city \
                    [--data-dir DIR] [--model-dir DIR] \
                    [--openaddr PATH] [--region CC] [--dry-run] [--verbose]

It is idempotent: a second run against a warm cache resolves nothing and says so. Exit status is 0 on success, 1 when the column is missing or no rows load, and 2 on bad arguments or an unopenable source.

script/bi-extract

Accelerates a large file: builds a SQLite copy of it, an extract, which the pages read for as long as the file is unchanged. Once the file changes, the file itself is read and the table page says the extract is stale.

./script/bi-extract --spec table:sales [--spec ...]   # accelerate, and build
./script/bi-extract --all                             # rebuild every one
./script/bi-extract --remove --spec table:sales       # stop, and remove its extract
./script/bi-extract --list
                    [--data-dir DIR] [--model-dir DIR]

Extracts live in <model_dir>/extracts/, never under data_dir, and deleting that directory loses nothing. Exit status is 0 on success, 1 when an extract could not be built, and 2 on bad arguments. Run --all from cron after the files are refreshed.

script/bi-alert

Reports saved analyses and turns a crossed threshold into a non-zero exit status, so cron can act on it:

./script/bi-alert --analysis ID [--analysis ID ...] \
                  [--threshold MEASURE:OP:VALUE] \
                  [--suppress 1h] [--recovery] \
                  [--notify-cmd 'mail -s "$BI_ALERT_SUBJECT" oncall@example.com']

Exit status is 0 when nothing is announced, 1 when a threshold is crossed and announced, 2 on error, and 3 when a crossing that was announced has ended and --recovery asked for that to be announced. --suppress takes a duration and stops a condition that is still true from being reported as news again; the throttle is one small JSON file in the model directory. The digest goes to standard output and nothing leaves the machine unless --notify-cmd says where.

Staying up to date

The distribution ships a test suite that covers the routes, the pipeline, the renderer invariants and the templates; run it before upgrading:

prove -l t/

The release notes in Changes list every behaviour change, and the test dashboard is published at nigelhorne.github.io/Database-BI/coverage.