{"id":13394973,"url":"https://github.com/ankane/blazer","last_synced_at":"2025-11-17T14:08:34.353Z","repository":{"id":21349221,"uuid":"24666294","full_name":"ankane/blazer","owner":"ankane","description":"Business intelligence made simple","archived":false,"fork":false,"pushed_at":"2025-10-23T17:16:04.000Z","size":4279,"stargazers_count":4744,"open_issues_count":41,"forks_count":486,"subscribers_count":67,"default_branch":"master","last_synced_at":"2025-11-10T13:22:53.000Z","etag":null,"topics":["business-intelligence","charts","sql"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ankane.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2014-10-01T04:54:55.000Z","updated_at":"2025-11-04T16:47:30.000Z","dependencies_parsed_at":"2023-11-27T22:24:22.252Z","dependency_job_id":"3dbd5b78-a80c-435a-8476-4b1b6cb98e85","html_url":"https://github.com/ankane/blazer","commit_stats":{"total_commits":1248,"total_committers":55,"mean_commits":22.69090909090909,"dds":"0.43669871794871795","last_synced_commit":"1e50ed8d9efb155fed0ff6c80d2f71b5f2b0ff20"},"previous_names":[],"tags_count":83,"template":false,"template_full_name":null,"purl":"pkg:github/ankane/blazer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fblazer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fblazer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fblazer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fblazer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ankane","download_url":"https://codeload.github.com/ankane/blazer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ankane%2Fblazer/sbom","scorecard":{"id":197140,"data":{"date":"2025-08-11","repo":{"name":"github.com/ankane/blazer","commit":"312dce4094a6e502ec3412a561d44a51c621b8a8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.6,"checks":[{"name":"Code-Review","score":0,"reason":"Found 2/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":2,"reason":"3 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/ankane/blazer/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/ankane/blazer/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/ankane/blazer/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/ankane/blazer/build.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T22:01:00.516Z","repository_id":21349221,"created_at":"2025-08-16T22:01:00.516Z","updated_at":"2025-08-16T22:01:00.516Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":283950016,"owners_count":26921648,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-11T02:00:06.610Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["business-intelligence","charts","sql"],"created_at":"2024-07-30T17:01:37.895Z","updated_at":"2025-11-17T14:08:34.334Z","avatar_url":"https://github.com/ankane.png","language":"Ruby","readme":"# Blazer\n\nExplore your data with SQL. Easily create charts and dashboards, and share them with your team.\n\n[Try it out](https://blazer.dokkuapp.com)\n\n[![Screenshot](https://blazer.dokkuapp.com/assets/blazer-176c595c.png)](https://blazer.dokkuapp.com)\n\nBlazer is also available as a [Docker image](https://github.com/ankane/blazer-docker).\n\n:tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)\n\n[![Build Status](https://github.com/ankane/blazer/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/blazer/actions)\n\n## Features\n\n- **Multiple data sources** - PostgreSQL, MySQL, Redshift, and [many more](#full-list)\n- **Variables** - run the same queries with different values\n- **Checks \u0026 alerts** - get emailed when bad data appears\n- **Audits** - all queries are tracked\n- **Security** - works with your authentication system\n\n## Docs\n\n- [Installation](#installation)\n- [Queries](#queries)\n- [Charts](#charts)\n- [Dashboards](#dashboards)\n- [Checks](#checks)\n- [Cohorts](#cohorts)\n- [Anomaly Detection](#anomaly-detection)\n- [Forecasting](#forecasting)\n- [Uploads](#uploads)\n- [Data Sources](#data-sources)\n- [Query Permissions](#query-permissions)\n\n## Installation\n\nAdd this line to your application’s Gemfile:\n\n```ruby\ngem \"blazer\"\n```\n\nRun:\n\n```sh\nrails generate blazer:install\nrails db:migrate\n```\n\nAnd mount the dashboard in your `config/routes.rb`:\n\n```ruby\nmount Blazer::Engine, at: \"blazer\"\n```\n\nFor production, specify your database:\n\n```ruby\nENV[\"BLAZER_DATABASE_URL\"] = \"postgres://user:password@hostname:5432/database\"\n```\n\nWhen possible, Blazer tries to protect against queries which modify data by running each query in a transaction and rolling it back, but a safer approach is to use a read-only user. [See how to create one](#permissions).\n\n#### Checks (optional)\n\nBe sure to set a host in `config/environments/production.rb` for emails to work.\n\n```ruby\nconfig.action_mailer.default_url_options = {host: \"blazer.dokkuapp.com\"}\n```\n\nSchedule checks to run (with cron, Solid Queue, [Heroku Scheduler](https://elements.heroku.com/addons/scheduler), etc). The default options are every 5 minutes, 1 hour, or 1 day, which you can customize. For each of these options, set up a task to run.\n\n```sh\nrake blazer:run_checks SCHEDULE=\"5 minutes\"\nrake blazer:run_checks SCHEDULE=\"1 hour\"\nrake blazer:run_checks SCHEDULE=\"1 day\"\n```\n\nYou can also set up failing checks to be sent once a day (or whatever you prefer).\n\n```sh\nrake blazer:send_failing_checks\n```\n\nHere’s what it looks like with cron.\n\n```\n*/5 * * * * rake blazer:run_checks SCHEDULE=\"5 minutes\"\n0   * * * * rake blazer:run_checks SCHEDULE=\"1 hour\"\n30  7 * * * rake blazer:run_checks SCHEDULE=\"1 day\"\n0   8 * * * rake blazer:send_failing_checks\n```\n\nFor Solid Queue, update `config/recurring.yml`.\n\n```yml\nproduction:\n  blazer_run_checks_5_minutes:\n    command: \"Blazer.run_checks(schedule: '5 minutes')\"\n    schedule: every 5 minutes\n  blazer_run_checks_1_hour:\n    command: \"Blazer.run_checks(schedule: '1 hour')\"\n    schedule: every hour\n  blazer_run_checks_1_day:\n    command: \"Blazer.run_checks(schedule: '1 day')\"\n    schedule: every day at 7:30am\n  blazer_send_failing_checks:\n    command: \"Blazer.send_failing_checks\"\n    schedule: every day at 8am\n```\n\nFor Slack notifications, create an [incoming webhook](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks) and set:\n\n```sh\nBLAZER_SLACK_WEBHOOK_URL=https://hooks.slack.com/...\n```\n\nName the webhook “Blazer” and add a cool icon.\n\n## Authentication\n\nDon’t forget to protect the dashboard in production.\n\n### Basic Authentication\n\nSet the following variables in your environment or an initializer.\n\n```ruby\nENV[\"BLAZER_USERNAME\"] = \"andrew\"\nENV[\"BLAZER_PASSWORD\"] = \"secret\"\n```\n\n### Devise\n\n```ruby\nauthenticate :user, -\u003e(user) { user.admin? } do\n  mount Blazer::Engine, at: \"blazer\"\nend\n```\n\n### Other\n\nSpecify a `before_action` method to run in `config/blazer.yml`.\n\n```yml\nbefore_action_method: require_admin\n```\n\nYou can define this method in your `ApplicationController`.\n\n```ruby\ndef require_admin\n  # depending on your auth, something like...\n  redirect_to main_app.root_path unless current_user \u0026\u0026 current_user.admin?\nend\n```\n\nBe sure to render or redirect for unauthorized users.\n\n## Permissions\n\n### PostgreSQL\n\nCreate a user with read-only permissions:\n\n```sql\nBEGIN;\nCREATE ROLE blazer LOGIN PASSWORD 'secret';\nGRANT CONNECT ON DATABASE dbname TO blazer;\nGRANT USAGE ON SCHEMA public TO blazer;\nGRANT SELECT ON ALL TABLES IN SCHEMA public TO blazer;\nALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO blazer;\nCOMMIT;\n```\n\n### MySQL and MariaDB\n\nCreate a user with read-only permissions:\n\n```sql\nCREATE USER 'blazer'@'127.0.0.1' IDENTIFIED BY 'secret';\nGRANT SELECT, SHOW VIEW ON dbname.* TO 'blazer'@'127.0.0.1';\nFLUSH PRIVILEGES;\n```\n\n## Sensitive Data\n\nIf your database contains sensitive or personal data, check out [Hypershield](https://github.com/ankane/hypershield) to shield it.\n\n## Encrypted Data\n\nIf you need to search encrypted data, use [blind indexing](https://github.com/ankane/blind_index).\n\nYou can have Blazer transform specific variables with:\n\n```ruby\nBlazer.transform_variable = lambda do |name, value|\n  value = User.generate_email_bidx(value) if name == \"email_bidx\"\n  value\nend\n```\n\n## Queries\n\n### Variables\n\nCreate queries with variables.\n\n```sql\nSELECT * FROM users WHERE gender = {gender}\n```\n\nUse `{start_time}` and `{end_time}` for time ranges. [Example](https://blazer.dokkuapp.com/queries/9-time-range-selector?start_time=1997-10-03T05%3A00%3A00%2B00%3A00\u0026end_time=1997-10-04T04%3A59%3A59%2B00%3A00)\n\n```sql\nSELECT * FROM ratings WHERE rated_at \u003e= {start_time} AND rated_at \u003c= {end_time}\n```\n\n### Smart Variables\n\n[Example](https://blazer.dokkuapp.com/queries/1-smart-variable)\n\nSuppose you have the query:\n\n```sql\nSELECT * FROM users WHERE occupation_id = {occupation_id}\n```\n\nInstead of remembering each occupation’s id, users can select occupations by name.\n\nAdd a smart variable in `config/blazer.yml` with:\n\n```yml\nsmart_variables:\n  occupation_id: \"SELECT id, name FROM occupations ORDER BY name ASC\"\n```\n\nThe first column is the value of the variable, and the second column is the label.\n\nYou can also use an array or hash for static data and enums.\n\n```yml\nsmart_variables:\n  period: [\"day\", \"week\", \"month\"]\n  status: {0: \"Active\", 1: \"Archived\"}\n```\n\n### Linked Columns\n\n[Example](https://blazer.dokkuapp.com/queries/3-linked-column) - title column\n\nLink results to other pages in your apps or around the web. Specify a column name and where it should link to. You can use the value of the result with `{value}`.\n\n```yml\nlinked_columns:\n  user_id: \"/admin/users/{value}\"\n  ip_address: \"https://www.infosniper.net/index.php?ip_address={value}\"\n```\n\n### Smart Columns\n\n[Example](https://blazer.dokkuapp.com/queries/2-smart-column) - occupation_id column\n\nSuppose you have the query:\n\n```sql\nSELECT name, city_id FROM users\n```\n\nSee which city the user belongs to without a join.\n\n```yml\nsmart_columns:\n  city_id: \"SELECT id, name FROM cities WHERE id IN {value}\"\n```\n\nYou can also use a hash for static data and enums.\n\n```yml\nsmart_columns:\n  status: {0: \"Active\", 1: \"Archived\"}\n```\n\n### Caching\n\nBlazer can automatically cache results to improve speed. It can cache slow queries:\n\n```yml\ncache:\n  mode: slow\n  expires_in: 60 # min\n  slow_threshold: 15 # sec\n```\n\nOr it can cache all queries:\n\n```yml\ncache:\n  mode: all\n  expires_in: 60 # min\n```\n\nOf course, you can force a refresh at any time.\n\n## Charts\n\nBlazer will automatically generate charts based on the types of the columns returned in your query.\n\n**Note:** The order of columns matters.\n\n### Line Chart\n\nThere are two ways to generate line charts.\n\n2+ columns - timestamp, numeric(s) - [Example](https://blazer.dokkuapp.com/queries/4-line-chart-format-1)\n\n```sql\nSELECT date_trunc('week', created_at), COUNT(*) FROM users GROUP BY 1\n```\n\n3 columns - timestamp, string, numeric - [Example](https://blazer.dokkuapp.com/queries/5-line-chart-format-2)\n\n\n```sql\nSELECT date_trunc('week', created_at), gender, COUNT(*) FROM users GROUP BY 1, 2\n```\n\n### Column Chart\n\nThere are also two ways to generate column charts.\n\n2+ columns - string, numeric(s) - [Example](https://blazer.dokkuapp.com/queries/6-column-chart-format-1)\n\n```sql\nSELECT gender, COUNT(*) FROM users GROUP BY 1\n```\n\n3 columns - string, string, numeric - [Example](https://blazer.dokkuapp.com/queries/7-column-chart-format-2)\n\n```sql\nSELECT gender, zip_code, COUNT(*) FROM users GROUP BY 1, 2\n```\n\n### Scatter Chart\n\n2 columns - both numeric - [Example](https://blazer.dokkuapp.com/queries/16-scatter-chart)\n\n```sql\nSELECT x, y FROM table\n```\n\n### Pie Chart\n\n2 columns - string, numeric - and last column named `pie` - [Example](https://blazer.dokkuapp.com/queries/17-pie-chart)\n\n```sql\nSELECT gender, COUNT(*) AS pie FROM users GROUP BY 1\n```\n\n### Maps\n\nColumns named `latitude` and `longitude` or `lat` and `lon` or `lat` and `lng` - [Example](https://blazer.dokkuapp.com/queries/15-map)\n\n```sql\nSELECT name, latitude, longitude FROM cities\n```\n\nor a column named `geojson`\n\n```sql\nSELECT name, geojson FROM counties\n```\n\nTo enable, get an access token from [Mapbox](https://www.mapbox.com/) and set `ENV[\"MAPBOX_ACCESS_TOKEN\"]`.\n\n### Targets\n\nUse the column name `target` to draw a line for goals. [Example](https://blazer.dokkuapp.com/queries/8-target-line)\n\n```sql\nSELECT date_trunc('week', created_at), COUNT(*) AS new_users, 100000 AS target FROM users GROUP BY 1\n```\n\n## Dashboards\n\nCreate a dashboard with multiple queries. [Example](https://blazer.dokkuapp.com/dashboards/1-dashboard-demo)\n\nIf the query has a chart, the chart is shown. Otherwise, you’ll see a table.\n\nIf any queries have variables, they will show up on the dashboard.\n\n## Checks\n\nChecks give you a centralized place to see the health of your data. [Example](https://blazer.dokkuapp.com/checks)\n\nCreate a query to identify bad rows.\n\n```sql\nSELECT * FROM ratings WHERE user_id IS NULL /* all ratings should have a user */\n```\n\nThen create check with optional emails if you want to be notified. Emails are sent when a check starts failing, and when it starts passing again.\n\n## Cohorts\n\nCreate a cohort analysis from a simple SQL query. [Example](https://blazer.dokkuapp.com/queries/19-cohort-analysis-from-first-order)\n\nCreate a query with the comment `/* cohort analysis */`. The result should have columns named `user_id` and `conversion_time` and optionally `cohort_time`.\n\nYou can generate cohorts from the first conversion time:\n\n```sql\n/* cohort analysis */\nSELECT user_id, created_at AS conversion_time FROM orders\n```\n\n(the first conversion isn’t counted in the first time period with this format)\n\nOr from another time, like sign up:\n\n```sql\n/* cohort analysis */\nSELECT users.id AS user_id, orders.created_at AS conversion_time, users.created_at AS cohort_time\nFROM users LEFT JOIN orders ON orders.user_id = users.id\n```\n\nThis feature requires PostgreSQL or MySQL 8.\n\n## Anomaly Detection\n\nBlazer supports three different approaches to anomaly detection.\n\n### Prophet\n\nAdd [prophet-rb](https://github.com/ankane/prophet) to your Gemfile:\n\n```ruby\ngem \"prophet-rb\"\n```\n\nAnd add to `config/blazer.yml`:\n\n```yml\nanomaly_checks: prophet\n```\n\n### Trend\n\nAdd [trend](https://github.com/ankane/trend) to your Gemfile:\n\n```ruby\ngem \"trend\"\n```\n\nSet the URL to the [API](https://github.com/ankane/trend-api) in an initializer:\n\n```ruby\nTrend.url = \"http://localhost:8000\"\n```\n\nAnd add to `config/blazer.yml`:\n\n```yml\nanomaly_checks: trend\n```\n\n### AnomalyDetection.rb\n\nAdd [anomaly_detection](https://github.com/ankane/AnomalyDetection.rb) to your Gemfile:\n\n```ruby\ngem \"anomaly_detection\"\n```\n\nAnd add to `config/blazer.yml`:\n\n```yml\nanomaly_checks: anomaly_detection\n```\n\n## Forecasting\n\nBlazer supports for two different forecasting methods. [Example](https://blazer.dokkuapp.com/queries/18-forecast?forecast=t)\n\nA forecast link will appear for queries that return 2 columns with types timestamp and numeric.\n\n### Prophet\n\nAdd [prophet-rb](https://github.com/ankane/prophet) to your Gemfile:\n\n```ruby\ngem \"prophet-rb\", \"\u003e= 0.2.1\"\n```\n\nAnd add to `config/blazer.yml`:\n\n```yml\nforecasting: prophet\n```\n\n### Trend\n\nAdd [trend](https://github.com/ankane/trend) to your Gemfile:\n\n```ruby\ngem \"trend\"\n```\n\nSet the URL to the [API](https://github.com/ankane/trend-api) in an initializer:\n\n```ruby\nTrend.url = \"http://localhost:8000\"\n```\n\nAnd add to `config/blazer.yml`:\n\n```yml\nforecasting: trend\n```\n\n## Uploads\n\nCreate database tables from CSV files. [Example](https://blazer.dokkuapp.com/uploads)\n\nRun:\n\n```sh\nrails generate blazer:uploads\nrails db:migrate\n```\n\nAnd add to `config/blazer.yml`:\n\n```yml\nuploads:\n  url: postgres://...\n  schema: uploads\n  data_source: main\n```\n\nThis feature requires PostgreSQL. Create a new schema just for uploads.\n\n```sql\nCREATE SCHEMA uploads;\n```\n\n## Data Sources\n\nBlazer supports multiple data sources :tada:\n\nAdd additional data sources in `config/blazer.yml`:\n\n```yml\ndata_sources:\n  main:\n    url: \u003c%= ENV[\"BLAZER_DATABASE_URL\"] %\u003e\n    # timeout, smart_variables, linked_columns, smart_columns\n  catalog:\n    url: \u003c%= ENV[\"CATALOG_DATABASE_URL\"] %\u003e\n    # ...\n  redshift:\n    url: \u003c%= ENV[\"REDSHIFT_DATABASE_URL\"] %\u003e\n    # ...\n```\n\n### Full List\n\n- [Amazon Athena](#amazon-athena)\n- [Amazon Redshift](#amazon-redshift)\n- [Apache Drill](#apache-drill)\n- [Apache Hive](#apache-hive)\n- [Apache Ignite](#apache-ignite)\n- [Apache Spark](#apache-spark)\n- [Cassandra](#cassandra)\n- [Druid](#druid)\n- [Elasticsearch](#elasticsearch)\n- [Google BigQuery](#google-bigquery)\n- [IBM DB2 and Informix](#ibm-db2-and-informix)\n- [InfluxDB](#influxdb)\n- [MySQL and MariaDB](#mysql-and-mariadb-1)\n- [Neo4j](#neo4j)\n- [OpenSearch](#opensearch)\n- [Oracle](#oracle)\n- [PostgreSQL](#postgresql-1)\n- [Presto and Trino](#presto-and-trino)\n- [Salesforce](#salesforce)\n- [Snowflake](#snowflake)\n- [Socrata Open Data API (SODA)](#socrata-open-data-api-soda)\n- [SQLite](#sqlite)\n- [SQL Server](#sql-server)\n\nYou can also [create an adapter](#creating-an-adapter) for any other data store.\n\n**Note:** In the examples below, we recommend using environment variables for urls.\n\n```yml\ndata_sources:\n  my_source:\n    url: \u003c%= ENV[\"BLAZER_MY_SOURCE_URL\"] %\u003e\n```\n\n### Amazon Athena\n\nAdd [aws-sdk-athena](https://github.com/aws/aws-sdk-ruby) and [aws-sdk-glue](https://github.com/aws/aws-sdk-ruby) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: athena\n    database: database\n\n    # optional settings\n    output_location: s3://some-bucket/\n    workgroup: primary\n    access_key_id: ...\n    secret_access_key: ...\n    region: ...\n```\n\nHere’s an example IAM policy:\n\n```json\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"athena:GetQueryExecution\",\n                \"athena:GetQueryResults\",\n                \"athena:StartQueryExecution\"\n            ],\n            \"Resource\": [\n                \"arn:aws:athena:region:account-id:workgroup/primary\"\n            ]\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"glue:GetTable\",\n                \"glue:GetTables\"\n            ],\n            \"Resource\": [\n                \"arn:aws:glue:region:account-id:catalog\",\n                \"arn:aws:glue:region:account-id:database/default\",\n                \"arn:aws:glue:region:account-id:table/default/*\"\n            ]\n        }\n    ]\n}\n```\n\nYou also need to configure [S3 permissions](https://repost.aws/knowledge-center/access-denied-athena).\n\n### Amazon Redshift\n\nAdd [activerecord7-redshift-adapter-pennylane](https://github.com/pennylane-hq/activerecord-adapter-redshift) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: redshift://user:password@hostname:5439/database\n```\n\nUse a [read-only user](https://docs.aws.amazon.com/redshift/latest/dg/r_GRANT.html).\n\n### Apache Drill\n\nAdd [drill-sergeant](https://github.com/ankane/drill-sergeant) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: drill\n    url: http://hostname:8047\n```\n\nUse a [read-only user](https://drill.apache.org/docs/roles-and-privileges/).\n\n### Apache Hive\n\nAdd [hexspace](https://github.com/ankane/hexspace) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: hive\n    url: sasl://user:password@hostname:10000/database\n```\n\nUse a [read-only user](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Authorization). Requires [HiveServer2](https://cwiki.apache.org/confluence/display/Hive/Setting+Up+HiveServer2).\n\n### Apache Ignite\n\nAdd [ignite-client](https://github.com/ankane/ignite-ruby) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: ignite://user:password@hostname:10800\n```\n\nUse a [read-only user](https://www.gridgain.com/docs/latest/administrators-guide/security/authorization-permissions) (requires a third-party plugin).\n\n### Apache Spark\n\nAdd [hexspace](https://github.com/ankane/hexspace) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: spark\n    url: sasl://user:password@hostname:10000/database\n```\n\nUse a read-only user. Requires the [Thrift server](https://spark.apache.org/docs/latest/sql-distributed-sql-engine.html).\n\n### Cassandra\n\nAdd [cassandra-driver](https://github.com/datastax/ruby-driver) and [sorted_set](https://github.com/knu/sorted_set) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: cassandra://user:password@hostname:9042/keyspace\n```\n\nUse a [read-only role](https://docs.datastax.com/en/cql-oss/3.3/cql/cql_using/useSecurePermission.html).\n\n### Druid\n\nEnable [SQL support](https://druid.apache.org/docs/latest/querying/sql) on the broker and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: druid\n    url: http://hostname:8082\n```\n\nUse a [read-only role](https://druid.apache.org/docs/latest/development/extensions-core/druid-basic-security.html).\n\n### Elasticsearch\n\nAdd [elasticsearch](https://github.com/elastic/elasticsearch-ruby) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: elasticsearch\n    url: http://user:password@hostname:9200\n```\n\nUse a [read-only role](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-privileges.html).\n\n### Google BigQuery\n\nAdd [google-cloud-bigquery](https://github.com/GoogleCloudPlatform/google-cloud-ruby/tree/main/google-cloud-bigquery) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: bigquery\n    project: your-project\n    keyfile: path/to/keyfile.json\n```\n\n### IBM DB2 and Informix\n\nAdd [ibm_db](https://github.com/ibmdb/ruby-ibmdb) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: ibm-db://user:password@hostname:50000/database\n```\n\nUse a [read-only user](https://www.ibm.com/support/pages/creating-read-only-database-permissions-user).\n\n### InfluxDB\n\nAdd [influxdb](https://github.com/influxdata/influxdb-ruby) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: influxdb\n    url: http://user:password@hostname:8086/database\n```\n\nUse a [read-only user](https://docs.influxdata.com/influxdb/v1.8/administration/authentication_and_authorization/). Supports [InfluxQL](https://docs.influxdata.com/influxdb/v1.8/query_language/explore-data/).\n\n### MySQL and MariaDB\n\nAdd [mysql2](https://github.com/brianmario/mysql2) to your Gemfile (if it’s not there) and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: mysql2://user:password@hostname:3306/database\n```\n\nUse a [read-only user](#mysql-and-mariadb).\n\n### Neo4j\n\nAdd [neo4j-ruby-driver](https://github.com/neo4jrb/neo4j-ruby-driver) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: neo4j\n    url: bolt://user:password@hostname:7687/database\n```\n\nUse a [read-only user](https://neo4j.com/docs/cypher-manual/current/access-control/manage-privileges/).\n\n### OpenSearch\n\nAdd [opensearch-ruby](https://github.com/opensearch-project/opensearch-ruby) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: opensearch\n    url: http://user:password@hostname:9200\n```\n\nUse a [read-only user](https://opensearch.org/docs/latest/security-plugin/access-control/permissions/).\n\n### Oracle\n\nAdd [activerecord-oracle_enhanced-adapter](https://github.com/rsim/oracle-enhanced) and [ruby-oci8](https://github.com/kubo/ruby-oci8) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: oracle-enhanced://user:password@hostname:1521/database\n```\n\nUse a [read-only user](https://docs.oracle.com/cd/B19306_01/network.102/b14266/authoriz.htm).\n\n### PostgreSQL\n\nAdd [pg](https://github.com/ged/ruby-pg) to your Gemfile (if it’s not there) and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: postgres://user:password@hostname:5432/database\n```\n\nUse a [read-only user](#postgresql).\n\n### Presto and Trino\n\nAdd [presto-client](https://github.com/treasure-data/trino-client-ruby/tree/v0.6.5) or [trino-client](https://github.com/treasure-data/trino-client-ruby) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: presto://user@hostname:8080/catalog\n    # or\n    url: trino://user@hostname:8080/catalog\n```\n\nUse a read-only user for [Presto](https://prestodb.io/docs/current/security/built-in-system-access-control.html) or [Trino](https://trino.io/docs/current/security/built-in-system-access-control.html).\n\n### Salesforce\n\nAdd [restforce](https://github.com/restforce/restforce) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: salesforce\n```\n\nAnd set the appropriate environment variables:\n\n```sh\nSALESFORCE_USERNAME=\"username\"\nSALESFORCE_PASSWORD=\"password\"\nSALESFORCE_SECURITY_TOKEN=\"security token\"\nSALESFORCE_CLIENT_ID=\"client id\"\nSALESFORCE_CLIENT_SECRET=\"client secret\"\nSALESFORCE_API_VERSION=\"41.0\"\n```\n\nUse a read-only user. Supports [SOQL](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm).\n\n### Snowflake\n\nFirst, install ODBC. For Homebrew, use:\n\n```sh\nbrew install unixodbc\n```\n\nFor Ubuntu, use:\n\n```sh\nsudo apt-get install unixodbc-dev\n```\n\nFor Heroku, use the [Apt buildpack](https://github.com/heroku/heroku-buildpack-apt) and create an `Aptfile` with:\n\n```text\nunixodbc-dev\nhttps://sfc-repo.snowflakecomputing.com/odbc/linux/2.21.5/snowflake-odbc-2.21.5.x86_64.deb\n```\n\n\u003e This installs the driver at `/app/.apt/usr/lib/snowflake/odbc/lib/libSnowflake.so`\n\nThen, download the [Snowflake ODBC driver](https://docs.snowflake.com/developer-guide/odbc/odbc-download). Add [odbc_adapter](https://github.com/localytics/odbc_adapter) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: snowflake\n    conn_str: Driver=/path/to/libSnowflake.so;uid=user;pwd=password;server=host.snowflakecomputing.com\n```\n\nUse a [read-only role](https://docs.snowflake.com/en/user-guide/security-access-control-configure.html).\n\n### Socrata Open Data API (SODA)\n\nSet:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: soda\n    url: https://soda.demo.socrata.com/resource/4tka-6guv.json\n    app_token: ...\n```\n\nSupports [SoQL](https://dev.socrata.com/docs/functions/).\n\n### SQLite\n\nAdd [sqlite3](https://github.com/sparklemotion/sqlite3-ruby) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: sqlite3:path/to/database.sqlite3\n```\n\n### SQL Server\n\nAdd [tiny_tds](https://github.com/rails-sqlserver/tiny_tds) and [activerecord-sqlserver-adapter](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter) to your Gemfile and set:\n\n```yml\ndata_sources:\n  my_source:\n    url: sqlserver://user:password@hostname:1433/database\n```\n\nUse a [read-only user](https://docs.microsoft.com/en-us/sql/relational-databases/security/authentication-access/getting-started-with-database-engine-permissions?view=sql-server-ver15).\n\n## Creating an Adapter\n\nCreate an adapter for any data store with:\n\n```ruby\nclass FooAdapter \u003c Blazer::Adapters::BaseAdapter\n  # code goes here\nend\n\nBlazer.register_adapter \"foo\", FooAdapter\n```\n\nSee the [Presto adapter](https://github.com/ankane/blazer/blob/master/lib/blazer/adapters/presto_adapter.rb) for a good example. Then use:\n\n```yml\ndata_sources:\n  my_source:\n    adapter: foo\n    url: http://user:password@hostname:9200/\n```\n\n## Query Permissions\n\nBlazer supports a basic permissions model.\n\n1. Queries without a name are unlisted\n2. Queries whose name starts with `#` are only listed to the creator\n3. Queries whose name starts with `*` can only be edited by the creator\n\n## Learn SQL\n\nHave team members who want to learn SQL? Here are a few great, free resources.\n\n- [The Data School](https://dataschool.com/learn-sql/)\n- [SQLBolt](https://sqlbolt.com/)\n\n## Useful Tools\n\nFor an easy way to group by day, week, month, and more with correct time zones, check out [Groupdate.sql](https://github.com/ankane/groupdate.sql).\n\n## Performance\n\nBy default, queries take up a request while they are running. To run queries asynchronously, add to your config:\n\n```yml\nasync: true\n```\n\n**Note:** Requires caching to be enabled. If you have multiple web processes, your app must use a centralized cache store like Memcached or Redis.\n\n```ruby\nconfig.cache_store = :mem_cache_store\n```\n\n## Archiving\n\nArchive queries that haven’t been viewed in over 90 days.\n\n```sh\nrake blazer:archive_queries\n```\n\n## Content Security Policy\n\nIf views are stuck with a `Loading...` message, there might be a problem with strict CSP settings in your app. This can be checked with Firefox or Chrome dev tools. You can allow Blazer to override these settings for its controllers with:\n\n```yml\noverride_csp: true\n```\n\n## History\n\nView the [changelog](https://github.com/ankane/blazer/blob/master/CHANGELOG.md)\n\n## Thanks\n\nBlazer uses a number of awesome open source projects, including [Rails](https://github.com/rails/rails/), [Vue.js](https://github.com/vuejs/vue), [jQuery](https://github.com/jquery/jquery), [Bootstrap](https://github.com/twbs/bootstrap), [Selectize](https://github.com/brianreavis/selectize.js), [StickyTableHeaders](https://github.com/jmosbech/StickyTableHeaders), [Stupid jQuery Table Sort](https://github.com/joequery/Stupid-Table-Plugin), and [Date Range Picker](https://github.com/dangrossman/bootstrap-daterangepicker).\n\nDemo data from [MovieLens](https://grouplens.org/datasets/movielens/).\n\n## Want to Make Blazer Better?\n\nThat’s awesome! Here are a few ways you can help:\n\n- [Report bugs](https://github.com/ankane/blazer/issues)\n- Fix bugs and [submit pull requests](https://github.com/ankane/blazer/pulls)\n- Write, clarify, or fix documentation\n- Suggest or add new features\n\nCheck out the [dev app](https://github.com/ankane/blazer-dev) to get started.\n","funding_links":[],"categories":["Ruby","sql","Business Intelligence","Uncategorized","Dashboards","ActiveRecord"],"sub_categories":["Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankane%2Fblazer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fankane%2Fblazer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fankane%2Fblazer/lists"}