├── config ├── __init__.py ├── links.yaml.template └── settings.py.template ├── dashboard ├── __init__.py ├── model │ ├── __init__.py │ ├── links_data.py │ ├── prometheus_data.py │ ├── influxdb_data.py │ └── tables_data.py ├── tests │ ├── __init__.py │ ├── mocks │ │ ├── __init__.py │ │ └── database.py │ ├── dataproviders │ │ ├── __init__.py │ │ └── test_airflow.py │ ├── Dockerfile │ └── docker-compose.yml ├── blueprints │ ├── __init__.py │ └── page │ │ ├── __init__.py │ │ ├── templates │ │ ├── 500.html │ │ ├── login.html │ │ ├── no_config.html │ │ ├── etl_details.html │ │ ├── etl_dashboard.html │ │ └── index.html │ │ └── views.py ├── plugins │ ├── __init__.py │ ├── s3_usage │ │ ├── tests │ │ │ ├── __init__.py │ │ │ ├── s3stats.sql │ │ │ ├── test_refresher.py │ │ │ └── test_statsdb.py │ │ ├── screenshot.png │ │ ├── templates │ │ │ └── s3_usage │ │ │ │ ├── initializing.html │ │ │ │ ├── partial_storage_classes.html │ │ │ │ └── index.html │ │ ├── README.md │ │ ├── __init__.py │ │ ├── statsdb.py │ │ └── refresher.py │ ├── reports │ │ ├── reports_list.png │ │ ├── reports.yaml.template │ │ ├── README.md │ │ ├── __init__.py │ │ ├── reports_data.py │ │ └── templates │ │ │ └── reports │ │ │ └── index.html │ ├── streaming │ │ ├── streaming.png │ │ ├── streaming.yaml.template │ │ ├── README.md │ │ ├── __init__.py │ │ └── templates │ │ │ └── streaming │ │ │ └── index.html │ ├── tables │ │ ├── table_details.png │ │ ├── tables_list.png │ │ ├── tables.yaml.template │ │ ├── __init__.py │ │ ├── README.md │ │ └── templates │ │ │ └── tables │ │ │ ├── details.html │ │ │ └── index.html │ ├── athena_usage │ │ ├── athena_usage.png │ │ ├── README.md │ │ ├── athena_query_model.py │ │ ├── __init__.py │ │ ├── templates │ │ │ └── athena_usage │ │ │ │ └── index.html │ │ ├── athena_summary_provider.py │ │ └── query_dao.py │ ├── table_descriptions │ │ ├── table_descriptions.png │ │ ├── README.md │ │ ├── __init__.py │ │ ├── dataproviders.py │ │ └── templates │ │ │ └── table_descriptions │ │ │ └── index.html │ └── hello_world │ │ ├── README.md │ │ ├── __init__.py │ │ └── templates │ │ └── hello_world │ │ └── index.html ├── static │ ├── favicon.ico │ ├── images │ │ ├── ui_screen.png │ │ └── graph_icon.svg │ ├── js │ │ ├── render_vis.js │ │ ├── highlight.js │ │ ├── tooltips.js │ │ ├── src │ │ │ ├── tooltips.js │ │ │ ├── popovers.js │ │ │ ├── colors.js │ │ │ ├── charts.js │ │ │ └── main.js │ │ ├── popovers.js │ │ ├── tooltips.js.map │ │ ├── popovers.js.map │ │ ├── colors.js │ │ ├── colors.js.map │ │ └── search.js │ ├── css │ │ ├── table_dashboard.css │ │ ├── etl_dashboard.css │ │ ├── reports.css │ │ ├── descriptions.css │ │ └── base.css │ └── vendors │ │ └── @coreui │ │ └── coreui-plugin-chartjs-custom-tooltips │ │ └── js │ │ ├── custom-tooltips.min.js │ │ └── custom-tooltips.min.js.map ├── dataproviders │ ├── __init__.py │ ├── etl.py │ └── airflow.py ├── templates │ ├── macros │ │ └── common.html │ └── layouts │ │ ├── clean.html │ │ └── base.html ├── service │ ├── prometheus_service.py │ ├── influxdb_service.py │ └── mysql.py ├── utils │ ├── __init__.py │ └── vis.py ├── models.py └── app.py ├── .gitignore ├── requirements.txt ├── .travis.yml ├── Dockerfile ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── LICENSE ├── examples └── extra.html ├── CHANGELOG.md └── Readme.md /config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/model/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/blueprints/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/tests/mocks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/tests/dataproviders/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard/plugins/s3_usage/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | */.idea/* 3 | *.pyc 4 | settings.py -------------------------------------------------------------------------------- /dashboard/blueprints/page/__init__.py: -------------------------------------------------------------------------------- 1 | from dashboard.blueprints.page.views import page 2 | -------------------------------------------------------------------------------- /dashboard/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/static/favicon.ico -------------------------------------------------------------------------------- /dashboard/dataproviders/__init__.py: -------------------------------------------------------------------------------- 1 | from .airflow import AirflowDBDataProvider 2 | from .etl import EtlDataProvider -------------------------------------------------------------------------------- /dashboard/static/images/ui_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/static/images/ui_screen.png -------------------------------------------------------------------------------- /dashboard/plugins/reports/reports_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/plugins/reports/reports_list.png -------------------------------------------------------------------------------- /dashboard/plugins/s3_usage/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/plugins/s3_usage/screenshot.png -------------------------------------------------------------------------------- /dashboard/plugins/streaming/streaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/plugins/streaming/streaming.png -------------------------------------------------------------------------------- /dashboard/plugins/tables/table_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/plugins/tables/table_details.png -------------------------------------------------------------------------------- /dashboard/plugins/tables/tables_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/plugins/tables/tables_list.png -------------------------------------------------------------------------------- /dashboard/plugins/athena_usage/athena_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/plugins/athena_usage/athena_usage.png -------------------------------------------------------------------------------- /dashboard/plugins/table_descriptions/table_descriptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikia/discreETLy/master/dashboard/plugins/table_descriptions/table_descriptions.png -------------------------------------------------------------------------------- /dashboard/plugins/hello_world/README.md: -------------------------------------------------------------------------------- 1 | # Hello World plugin 2 | 3 | This *dummy* plugin describes how to contribute to DiscreETLy by creating new plugins. 4 | Enable it in `settings.py` to find more! -------------------------------------------------------------------------------- /dashboard/model/links_data.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class LinksDataProvider: 4 | def __init__(self, links): 5 | self.links = links if links else [] 6 | 7 | def get_links(self): 8 | return self.links 9 | -------------------------------------------------------------------------------- /dashboard/plugins/streaming/streaming.yaml.template: -------------------------------------------------------------------------------- 1 | - name: friendly_name 2 | link: "https://console.aws.amazon.com/kinesis/home?region=us-east-1#/streams/details?streamName=myStream&tab=monitoring" 3 | table: dbname.tablename 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | werkzeug==0.16.* 2 | Flask==1.0.* 3 | Flask-SSLify 4 | gunicorn==19.9.* 5 | influxdb==5.2.* 6 | pyyaml>=4.2b1 7 | loginpass==0.3 8 | Authlib==0.12.1 9 | mysqlclient==1.3.12 10 | backoff 11 | requests==2.20.0 12 | boto3==1.9.67 13 | -------------------------------------------------------------------------------- /dashboard/templates/macros/common.html: -------------------------------------------------------------------------------- 1 | 2 | {% macro status_button_color(status) -%} 3 | {% if status == 'running' %}warning 4 | {% elif status == 'success' %}success 5 | {% elif status == 'failed' %}danger 6 | {% else %}secondary 7 | {% endif %} 8 | {%- endmacro %} 9 | -------------------------------------------------------------------------------- /config/links.yaml.template: -------------------------------------------------------------------------------- 1 | - name: 'The best Athena dashboard ;)' 2 | url: 'https://github.com/Wikia/discreETLy' 3 | - name: 'Sroka - Python library for API access and data analysis in Product, BI, Revenue Operations (GAM, GA, Athena etc.)' 4 | url: 'hhttps://github.com/Wikia/sroka' -------------------------------------------------------------------------------- /dashboard/static/js/render_vis.js: -------------------------------------------------------------------------------- 1 | const render = (element, spec) => { 2 | new vega.View(vega.parse(spec)) 3 | .renderer('svg') // set renderer (canvas or svg) 4 | .initialize(element) // initialize view within parent DOM container 5 | .hover() 6 | .run(); 7 | } -------------------------------------------------------------------------------- /dashboard/plugins/reports/reports.yaml.template: -------------------------------------------------------------------------------- 1 | reports: 2 | - id: sample_id 3 | definition: 4 | name: Sample report 5 | owner: John Rambo 6 | processing_times: '04:00 UTC' 7 | tables: 8 | - id: some_db.some_table 9 | name: Descriptive Name 10 | -------------------------------------------------------------------------------- /dashboard/static/js/highlight.js: -------------------------------------------------------------------------------- 1 | const highlightTable = (tid) => { 2 | document.getElementById(tid).style.backgroundColor = "pink"; 3 | window.scrollBy(0, -100); 4 | }; 5 | 6 | const checkHighlight = () => { 7 | if(window.location.hash) 8 | { highlightTable(window.location.hash.substr(1));}}; -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: "3.6" 3 | 4 | services: 5 | - docker 6 | install: 7 | - docker build -t dashboard:tests -f dashboard/tests/Dockerfile . 8 | script: 9 | - docker-compose --file dashboard/tests/docker-compose.yml up --abort-on-container-exit && docker-compose --file dashboard/tests/docker-compose.yml down 10 | -------------------------------------------------------------------------------- /dashboard/plugins/hello_world/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from flask import render_template 3 | 4 | base_path = '/hello_world' 5 | tab_name = 'Hello World!' 6 | plugin = Blueprint('hello_world', __name__, template_folder='templates') 7 | 8 | @plugin.route('/') 9 | def index(): 10 | return render_template('hello_world/index.html') -------------------------------------------------------------------------------- /dashboard/static/css/table_dashboard.css: -------------------------------------------------------------------------------- 1 | .table-data { 2 | margin: 0 !important; 3 | padding: .5rem; 4 | } 5 | 6 | .table-data:nth-child(odd) { 7 | background-color: #dee7e5; 8 | } 9 | 10 | .table-data div p { 11 | margin: 0 !important; 12 | padding-top: .5rem; 13 | } 14 | 15 | .last-update button { 16 | width: 14rem; 17 | } -------------------------------------------------------------------------------- /dashboard/blueprints/page/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends 'layouts/clean.html' %} 2 | 3 | {% block title %}{{ config['COMPANY_NAME'] }} Data - internal server error{% endblock %} 4 | 5 | {% block body %} 6 | 7 |
| HEX: | \n" + rgbToHex(Color) + " | \n
| RGB: | \n" + Color + " | \n
| HEX: | 17 |${rgbToHex(Color)} | 18 |
| RGB: | 21 |${Color} | 22 |
| HEX: | \n${rgbToHex(Color)} | \n
| RGB: | \n${Color} | \n
❌
32 | {% else %} 33 |✅
34 | {% endif %} 35 |×
22 |Legend:
28 |◉ Success
31 |◉ In Progress
34 |◉ Failure
37 |◉ Root Node
40 |dashboard/plugins and creating
17 | a new one is very easy:
18 |
19 |
20 | # 1. Copy hello_world plugin contents
21 | cp -r dashboard/plugins/hello_world dashboard/plugins/$PLUGIN_NAME
22 |
23 | # 2. Fix the templates namespace
24 | mv dashboard/plugins/$PLUGIN_NAME/templates/hello_world dashboard/plugins/$PLUGIN_NAME/templates/$PLUGIN_NAME
25 |
26 | # 3. Adjust the code and template
27 | vi dashboard/plugins/$PLUGIN_NAME/__init__.py
28 | vi dashboard/plugins/$PLUGIN_NAME/templates/$PLUGIN_NAME/index.html
29 |
30 | # 4. Enable the plugin in settings.py
31 | vi config/settings.py
32 |
33 |
34 | base_path - the URL part that all plugin routes are relative totab_name - user-friendly name of the plugin to put on navigation barplugin - implementation of flask.Blueprint to be createdinit inside that gets application object
46 | (with generic modules like app.config and app.logger)
47 |
48 | from flask import current_app as app and then you can use:
53 |
54 | app.airflow_data_provider - provides Airflow DAGs list, task statuses, ... (retrieved from the DB)app.influx_data_provider - queries InfluxDB for the records count for the tablesapp.prometheus_data_provider - lists currently fired alerts on Prometheusapp.airflow_data_provider - provides tables list with status, alerts, records counts, ...app.etl_data_provider - formats app.airflow_data_provider with custom logic (like calculating DAG duration)app.async_request_executor - ThreadPoolExecutor to query data providers in parallelapp.cache - simple in-memory cache❌
48 | {% else %} 49 |✅
50 | {% endif %} 51 |×
21 |Legend:
27 |◉ Success
30 |◉ In Progress
33 |◉ Failure
36 |{{ etl.name }}
53 |56 | {{ etl.schedule }} 57 |
58 |{{ etl.duration_seconds|describe_seconds }}
{% endif %} 76 |79 | {% for run in history[etl.name] %} 80 | ◉ 82 | {% endfor %} 83 |
84 |{{ etl_summary | selectattr('state', 'eq', 'success') | list | count }}
38 | 39 |{{ etl_summary | selectattr('state', 'eq', 'running') | list | count }}
41 | 42 | 43 |68 | {{ (today_tables/tables|count * 100)|int }}% 69 |
70 | 71 |Total tables: {{ tables|count }}
72 |Tables updated today: {{ today_tables }}
73 | {% set stale_tables = tables|selectattr('last_update', 'ne', none)|selectattr('last_update', 'lt', now.replace(hour=0, minute=0, second=0))|list %} 74 | {% if stale_tables|count > 0 %} 75 |Tables awaiting completion: 76 |
×
20 |35 | {% if 'table_descriptions' in plugins %}{% endif %} 36 | {{ table.id }} 37 | {% if 'table_descriptions' in plugins %}{% endif %} 38 | {% if table.active_alerts %} 39 | {% for alert in table.active_alerts %} 40 | 41 | {% endfor %} 42 | {% else %} 43 | 44 | {% endif %} 45 |
46 |×
22 |ETLs - information about the status of all ETL processes (DAGs) available in airflow database. If tables are defined this view shows number of processed tables. If tables information 101 | is missing, it shows number of tasks completed.
102 |Tables - information available only it tables mapping is defined in a yaml file. It shows current status of processing related to a particular table.
103 |Plugins - any information provided through plugins system available in discreETLy with appropriate tab name.
104 |For more information contact the team responsible for maintaining the dashboard or search the documentation.
105 |