├── stargazer ├── assets_modern_data_stack │ ├── __init__.py │ ├── utils │ │ └── constants.py │ ├── db_io_manager.py │ └── my_asset.py ├── assets_modern_data_stack_tests │ ├── __init__.py │ ├── test_assets.py │ └── test_repo_loads.py ├── setup.cfg ├── dbt_project_1 │ ├── .gitignore │ ├── .sqlfluffignore │ ├── models │ │ ├── mart │ │ │ ├── schema.yml │ │ │ └── mart_gh_stargazer.sql │ │ └── staging │ │ │ └── sources.yml │ ├── profiles.yml │ ├── config │ │ └── profiles.yml │ ├── packages.yml │ ├── dbt_project.yml │ └── .sqlfluff ├── dbt_project_2 │ ├── .gitignore │ ├── .sqlfluffignore │ ├── models │ │ ├── sources.yml │ │ ├── mart │ │ │ ├── schema.yml │ │ │ ├── mart_gh_join.sql │ │ │ └── mart_gh_cumulative.sql │ │ └── staging │ │ │ └── sources.yml │ ├── profiles.yml │ ├── config │ │ └── profiles.yml │ ├── packages.yml │ ├── dbt_project.yml │ └── .sqlfluff ├── pyproject.toml ├── setup.py └── README.md ├── docs └── images │ ├── fresh.png │ ├── sensor.png │ ├── schedule.png │ ├── dbt-projects.png │ ├── deployment.png │ ├── 9am-freshness.png │ ├── airbyte-assets.png │ ├── 10-minute-freshness.png │ ├── 5-minute-freshness.png │ └── global-asset-lineage.png ├── Pipfile ├── .gitignore ├── LICENSE ├── README.md └── Pipfile.lock /stargazer/assets_modern_data_stack/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stargazer/assets_modern_data_stack_tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /stargazer/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = stargazer 3 | -------------------------------------------------------------------------------- /stargazer/assets_modern_data_stack_tests/test_assets.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target/ 3 | dbt_packages/ 4 | logs/ 5 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target/ 3 | dbt_packages/ 4 | logs/ 5 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/.sqlfluffignore: -------------------------------------------------------------------------------- 1 | target/ 2 | dbt_packages/ 3 | macros/ 4 | logs/ 5 | .venv 6 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/.sqlfluffignore: -------------------------------------------------------------------------------- 1 | target/ 2 | dbt_packages/ 3 | macros/ 4 | logs/ 5 | .venv 6 | -------------------------------------------------------------------------------- /docs/images/fresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/fresh.png -------------------------------------------------------------------------------- /docs/images/sensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/sensor.png -------------------------------------------------------------------------------- /docs/images/schedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/schedule.png -------------------------------------------------------------------------------- /docs/images/dbt-projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/dbt-projects.png -------------------------------------------------------------------------------- /docs/images/deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/deployment.png -------------------------------------------------------------------------------- /docs/images/9am-freshness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/9am-freshness.png -------------------------------------------------------------------------------- /docs/images/airbyte-assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/airbyte-assets.png -------------------------------------------------------------------------------- /docs/images/10-minute-freshness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/10-minute-freshness.png -------------------------------------------------------------------------------- /docs/images/5-minute-freshness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/5-minute-freshness.png -------------------------------------------------------------------------------- /docs/images/global-asset-lineage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathanneo/data-aware-orchestration/HEAD/docs/images/global-asset-lineage.png -------------------------------------------------------------------------------- /stargazer/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.dagster] 6 | module_name = "stargazer" 7 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [dev-packages] 7 | 8 | [requires] 9 | python_version = "3.9" 10 | python_full_version = "3.9.14" 11 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/models/mart/schema.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | models: 4 | - name: mart_gh_stargazer 5 | description: This model cleans up customer data 6 | columns: 7 | - name: repository 8 | - name: starred_at 9 | - name: user_login 10 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/models/sources.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | sources: 3 | - name: mart 4 | database: postgres 5 | schema: public_mart 6 | tables: 7 | - name: mart_gh_stargazer 8 | identifier: mart_gh_stargazer 9 | description: Given stars on GitHub repositories 10 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/profiles.yml: -------------------------------------------------------------------------------- 1 | stargazers: 2 | target: dev 3 | outputs: 4 | dev: 5 | type: postgres 6 | threads: 1 7 | host: localhost 8 | port: 5433 9 | user: postgres 10 | pass: postgres 11 | dbname: postgres 12 | schema: public 13 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/profiles.yml: -------------------------------------------------------------------------------- 1 | stargazers: 2 | target: dev 3 | outputs: 4 | dev: 5 | type: postgres 6 | threads: 1 7 | host: localhost 8 | port: 5433 9 | user: postgres 10 | pass: postgres 11 | dbname: postgres 12 | schema: public 13 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/config/profiles.yml: -------------------------------------------------------------------------------- 1 | stargazers: 2 | target: dev 3 | outputs: 4 | dev: 5 | type: postgres 6 | threads: 1 7 | host: localhost 8 | port: 5433 9 | user: postgres 10 | pass: postgres 11 | dbname: postgres 12 | schema: public 13 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/config/profiles.yml: -------------------------------------------------------------------------------- 1 | stargazers: 2 | target: dev 3 | outputs: 4 | dev: 5 | type: postgres 6 | threads: 1 7 | host: localhost 8 | port: 5433 9 | user: postgres 10 | pass: postgres 11 | dbname: postgres 12 | schema: public 13 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/packages.yml: -------------------------------------------------------------------------------- 1 | # add dependencies. these will get pulled during the `dbt deps` process. 2 | 3 | packages: 4 | - package: calogica/dbt_expectations 5 | version: [">=0.5.0", "<0.6.0"] 6 | - package: dbt-labs/codegen 7 | version: 0.7.0 8 | - package: dbt-labs/dbt_external_tables 9 | version: 0.8.0 -------------------------------------------------------------------------------- /stargazer/dbt_project_2/models/mart/schema.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | models: 4 | - name: mart_gh_cumulative 5 | description: cumulative count of stars 6 | columns: 7 | - name: repository 8 | - name: starred_at_month 9 | - name: sum_starscustomer_id 10 | - name: cumulative_stargazers 11 | 12 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/packages.yml: -------------------------------------------------------------------------------- 1 | # add dependencies. these will get pulled during the `dbt deps` process. 2 | 3 | packages: 4 | - package: calogica/dbt_expectations 5 | version: [">=0.5.0", "<0.6.0"] 6 | - package: dbt-labs/codegen 7 | version: 0.7.0 8 | - package: dbt-labs/dbt_external_tables 9 | version: 0.8.0 -------------------------------------------------------------------------------- /stargazer/dbt_project_2/models/staging/sources.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | sources: 3 | - name: postgres 4 | database: postgres 5 | schema: public 6 | tables: 7 | - name: stargazers 8 | identifier: _airbyte_raw_stargazers 9 | description: Given stars on GitHub repositories 10 | - name: stargazers_user 11 | description: GitHub users that started the GitHub repo -------------------------------------------------------------------------------- /stargazer/dbt_project_1/models/staging/sources.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | sources: 3 | - name: postgres 4 | database: postgres 5 | schema: public 6 | tables: 7 | - name: stargazers 8 | identifier: _airbyte_raw_stargazers 9 | description: Given stars on GitHub repositories 10 | - name: stargazers_user 11 | description: GitHub users that started the GitHub repo 12 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/models/mart/mart_gh_stargazer.sql: -------------------------------------------------------------------------------- 1 | with stargazer as ( 2 | select * 3 | from {{ source('postgres', 'stargazers') }} 4 | ), 5 | users as ( 6 | select * 7 | from {{ source('postgres', 'stargazers_user') }} 8 | ) 9 | 10 | SELECT s.repository , s.starred_at, su.login as user_login 11 | FROM stargazers s left outer join stargazers_user su on s.user_id = su.id 12 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/models/mart/mart_gh_join.sql: -------------------------------------------------------------------------------- 1 | with mart_gh_stargazer as ( 2 | select * 3 | from {{ source('mart', 'mart_gh_stargazer') }} 4 | ), 5 | mart_gh_cumulative as ( 6 | select * 7 | from {{ ref('mart_gh_cumulative') }} 8 | ) 9 | 10 | select 11 | mart_gh_stargazer.repository 12 | from mart_gh_stargazer 13 | left join mart_gh_cumulative on mart_gh_stargazer.repository = mart_gh_cumulative.repository 14 | -------------------------------------------------------------------------------- /stargazer/assets_modern_data_stack_tests/test_repo_loads.py: -------------------------------------------------------------------------------- 1 | from assets_modern_data_stack import assets_modern_data_stack 2 | 3 | 4 | def test_repo_can_load(): 5 | assets_modern_data_stack.load_all_definitions() 6 | 7 | # Repo should have only one "default" asset group, which is represented a "__ASSET_JOB" job 8 | assert [job.name for job in assets_modern_data_stack.get_all_jobs()] == [ 9 | "__ASSET_JOB", 10 | "all_assets", 11 | ] 12 | -------------------------------------------------------------------------------- /stargazer/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name="assets_modern_data_stack", 5 | packages=find_packages(exclude=["assets_modern_data_stack_tests"]), 6 | package_data={"assets_modern_data_stack": ["dbt_project_1/*", "dbt_project_2/*"]}, 7 | install_requires=[ 8 | "dagster", 9 | "dagster-airbyte", 10 | "dagster-managed-elements", 11 | "dagster-dbt", 12 | "dagster-postgres", 13 | "pandas", 14 | "dbt-core", 15 | "dbt-postgres", 16 | ], 17 | extras_require={"dev": ["dagit", "pytest", "black"]}, 18 | ) 19 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/models/mart/mart_gh_cumulative.sql: -------------------------------------------------------------------------------- 1 | {{ 2 | config( 3 | dagster_freshness_policy={"maximum_lag_minutes": 5} 4 | ) 5 | }} 6 | 7 | with stargazer as ( 8 | select * 9 | from {{ source('postgres', 'stargazers') }} 10 | ), 11 | users as ( 12 | select * 13 | from {{ source('postgres', 'stargazers_user') }} 14 | ), 15 | 16 | 17 | per_month as ( 18 | SELECT repository , to_char(s.starred_at , 'YYYY-MM') as starred_at_month, count(*) as sum_stars 19 | FROM stargazers s left outer join users su on s.user_id = su.id 20 | group by repository,to_char(s.starred_at , 'YYYY-MM') 21 | ) 22 | 23 | SELECT repository, starred_at_month, sum_stars 24 | , sum(s.sum_stars) over (partition by repository order by starred_at_month) as cumulative_stargazers 25 | 26 | FROM per_month s 27 | group by repository, starred_at_month, sum_stars 28 | order by repository, starred_at_month -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # user files 2 | tmp* 3 | .user.yml 4 | .DS_Store 5 | 6 | .gradle 7 | .idea 8 | .vscode 9 | *.iml 10 | *.swp 11 | .DS_Store 12 | .dockerversions 13 | .classpath 14 | .project 15 | .settings 16 | .vscode 17 | 18 | build 19 | 20 | # Secrets 21 | secrets 22 | 23 | # Python 24 | *.egg-info 25 | __pycache__ 26 | .pyc 27 | .eggs 28 | .venv 29 | .mypy_cache 30 | .ipynb_checkpoints 31 | *.ipynb 32 | .pytest_ 33 | 34 | # Python unit test / coverage reports 35 | htmlcov/ 36 | .tox/ 37 | .nox/ 38 | .coverage 39 | .coverage.* 40 | .cache 41 | nosetests.xml 42 | coverage.xml 43 | *.cover 44 | *.py,cover 45 | .hypothesis/ 46 | .pytest_cache/ 47 | cover/ 48 | 49 | # airbyte 50 | airbyte/run/ 51 | 52 | #dbt 53 | transformation_dbt/dbt_packages/ 54 | transformation_dbt/logs/ 55 | transformation_dbt/target/ 56 | transformation_dbt/profiles.yml 57 | 58 | 59 | # open stack project 60 | visualization/metabase/plugins 61 | visualization/metabase/metabase.jar -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jonathan Neo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /stargazer/assets_modern_data_stack/utils/constants.py: -------------------------------------------------------------------------------- 1 | from dagster_postgres.utils import get_conn_string 2 | from dagster._utils import file_relative_path 3 | 4 | DBT_PROJECT_DIR1 = file_relative_path(__file__, "../../dbt_project_1") 5 | DBT_PROFILES_DIR1 = file_relative_path(__file__, "../../dbt_project_1/config") 6 | DBT_CONFIG1 = {"project_dir": DBT_PROJECT_DIR1, "profiles_dir": DBT_PROFILES_DIR1} 7 | DBT_PROJECT_DIR2 = file_relative_path(__file__, "../../dbt_project_2") 8 | DBT_PROFILES_DIR2 = file_relative_path(__file__, "../../dbt_project_2/config") 9 | DBT_CONFIG2 = {"project_dir": DBT_PROJECT_DIR2, "profiles_dir": DBT_PROFILES_DIR2} 10 | 11 | PG_DESTINATION_CONFIG = { 12 | "username": "postgres", 13 | "password": "postgres", 14 | "host": "localhost", 15 | "port": 5433, 16 | "database": "postgres", 17 | } 18 | 19 | POSTGRES_CONFIG = { 20 | "con_string": get_conn_string( 21 | username=PG_DESTINATION_CONFIG["username"], 22 | password=PG_DESTINATION_CONFIG["password"], 23 | hostname=PG_DESTINATION_CONFIG["host"], 24 | port=str(PG_DESTINATION_CONFIG["port"]), 25 | db_name=PG_DESTINATION_CONFIG["database"], 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /stargazer/assets_modern_data_stack/db_io_manager.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | from dagster import IOManager, io_manager 4 | 5 | 6 | class DbIOManager(IOManager): 7 | """Sample IOManager to handle loading the contents of tables as pandas DataFrames. 8 | 9 | Does not handle cases where data is written to different schemas for different outputs, and 10 | uses the name of the asset key as the table name. 11 | """ 12 | 13 | def __init__(self, con_string: str): 14 | self._con = con_string 15 | 16 | def handle_output(self, context, obj): 17 | if isinstance(obj, pd.DataFrame): 18 | # write df to table 19 | obj.to_sql(name=context.asset_key.path[-1], con=self._con, if_exists="replace") 20 | elif obj is None: 21 | # dbt has already written the data to this table 22 | pass 23 | else: 24 | raise ValueError(f"Unsupported object type {type(obj)} for DbIOManager.") 25 | 26 | def load_input(self, context) -> pd.DataFrame: 27 | """Load the contents of a table as a pandas DataFrame.""" 28 | model_name = context.asset_key.path[-1] 29 | return pd.read_sql(f"SELECT * FROM {model_name}", con=self._con) 30 | 31 | 32 | @io_manager(config_schema={"con_string": str}) 33 | def db_io_manager(context): 34 | return DbIOManager(context.resource_config["con_string"]) 35 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/dbt_project.yml: -------------------------------------------------------------------------------- 1 | name: 'stargazers' 2 | version: '1.0' 3 | config-version: 2 4 | 5 | # This setting configures which "profile" dbt uses for this project. Profiles contain 6 | # database connection information, and should be configured in the ~/.dbt/profiles.yml file 7 | profile: 'stargazers' 8 | 9 | # These configurations specify where dbt should look for different types of files. 10 | # The `source-paths` config, for example, states that source models can be found 11 | docs-paths: ["docs"] 12 | analysis-paths: ["analysis"] 13 | test-paths: ["tests"] 14 | seed-paths: ["seed"] 15 | macro-paths: ["macros"] 16 | 17 | target-path: "target" # directory which will store compiled SQL files 18 | log-path: "logs" # directory which will store DBT logs 19 | 20 | clean-targets: # directories to be removed by `dbt clean` 21 | - "target" 22 | - "logs" 23 | 24 | quoting: 25 | database: true 26 | schema: true 27 | identifier: true 28 | 29 | dispatch: 30 | - macro_namespace: dbt_utils 31 | search_order: ['airbyte_warehouse', 'dbt_utils'] 32 | 33 | vars: 34 | 'dbt_date:time_zone': 'America/Los_Angeles' 35 | get_max_dt_lookback_window: 4 # Set how many days to look back when computing max dt for incremental 36 | 37 | # Using these configurations, you can enable or disable models, change how they 38 | # are materialized, and more! 39 | models: 40 | stargazers: 41 | +dagster_freshness_policy: 42 | maximum_lag_minutes: 540 43 | cron_schedule: "0 9 * * *" 44 | # When schema changes on models we are building, we should run dbt with --full-refresh flag explicitely 45 | +on_schema_change: "fail" 46 | +materialized: table 47 | staging: 48 | +tags: staging 49 | +materialized: view 50 | +schema: staging 51 | core: 52 | +materialized: view 53 | +schema: core 54 | mart: 55 | +materialized: table 56 | +schema: mart 57 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/dbt_project.yml: -------------------------------------------------------------------------------- 1 | name: 'stargazers' 2 | version: '1.0' 3 | config-version: 2 4 | 5 | # This setting configures which "profile" dbt uses for this project. Profiles contain 6 | # database connection information, and should be configured in the ~/.dbt/profiles.yml file 7 | profile: 'stargazers' 8 | 9 | # These configurations specify where dbt should look for different types of files. 10 | # The `source-paths` config, for example, states that source models can be found 11 | docs-paths: ["docs"] 12 | analysis-paths: ["analysis"] 13 | test-paths: ["tests"] 14 | seed-paths: ["seed"] 15 | macro-paths: ["macros"] 16 | 17 | target-path: "target" # directory which will store compiled SQL files 18 | log-path: "logs" # directory which will store DBT logs 19 | 20 | clean-targets: # directories to be removed by `dbt clean` 21 | - "target" 22 | - "logs" 23 | 24 | quoting: 25 | database: true 26 | schema: true 27 | identifier: true 28 | 29 | dispatch: 30 | - macro_namespace: dbt_utils 31 | search_order: ['airbyte_warehouse', 'dbt_utils'] 32 | 33 | vars: 34 | 'dbt_date:time_zone': 'America/Los_Angeles' 35 | get_max_dt_lookback_window: 4 # Set how many days to look back when computing max dt for incremental 36 | 37 | # Using these configurations, you can enable or disable models, change how they 38 | # are materialized, and more! 39 | models: 40 | stargazers: 41 | +dagster_freshness_policy: 42 | maximum_lag_minutes: 540 43 | cron_schedule: "0 9 * * *" 44 | # When schema changes on models we are building, we should run dbt with --full-refresh flag explicitely 45 | +on_schema_change: "fail" 46 | +materialized: table 47 | staging: 48 | +tags: staging 49 | +materialized: view 50 | +schema: staging 51 | core: 52 | +materialized: view 53 | +schema: core 54 | mart: 55 | +materialized: table 56 | +schema: mart 57 | -------------------------------------------------------------------------------- /stargazer/README.md: -------------------------------------------------------------------------------- 1 | # stargazer 2 | 3 | This is a [Dagster](https://dagster.io/) project scaffolded with [`dagster project scaffold`](https://docs.dagster.io/getting-started/create-new-project). 4 | 5 | ## Getting started 6 | 7 | First, install your Dagster code location as a Python package. By using the --editable flag, pip will install your Python package in ["editable mode"](https://pip.pypa.io/en/latest/topics/local-project-installs/#editable-installs) so that as you develop, local code changes will automatically apply. 8 | 9 | ```bash 10 | pip install -e ".[dev]" 11 | ``` 12 | 13 | Then, start the Dagit web server: 14 | 15 | ```bash 16 | dagit 17 | ``` 18 | 19 | Open http://localhost:3000 with your browser to see the project. 20 | 21 | You can start writing assets in `stargazer/assets.py`. The assets are automatically loaded into the Dagster code location as you define them. 22 | 23 | ## Development 24 | 25 | 26 | ### Adding new Python dependencies 27 | 28 | You can specify new Python dependencies in `setup.py`. 29 | 30 | ### Unit testing 31 | 32 | Tests are in the `stargazer_tests` directory and you can run tests using `pytest`: 33 | 34 | ```bash 35 | pytest stargazer_tests 36 | ``` 37 | 38 | ### Schedules and sensors 39 | 40 | If you want to enable Dagster [Schedules](https://docs.dagster.io/concepts/partitions-schedules-sensors/schedules) or [Sensors](https://docs.dagster.io/concepts/partitions-schedules-sensors/sensors) for your jobs, start the [Dagster Daemon](https://docs.dagster.io/deployment/dagster-daemon) process in the same folder as your `workspace.yaml` file, but in a different shell or terminal. 41 | 42 | The `$DAGSTER_HOME` environment variable must be set to a directory for the daemon to work. Note: using directories within /tmp may cause issues. See [Dagster Instance default local behavior](https://docs.dagster.io/deployment/dagster-instance#default-local-behavior) for more details. 43 | 44 | ```bash 45 | dagster-daemon run 46 | ``` 47 | 48 | Once your Dagster Daemon is running, you can start turning on schedules and sensors for your jobs. 49 | 50 | ## Deploy on Dagster Cloud 51 | 52 | The easiest way to deploy your Dagster project is to use Dagster Cloud. 53 | 54 | Check out the [Dagster Cloud Documentation](https://docs.dagster.cloud) to learn more. 55 | -------------------------------------------------------------------------------- /stargazer/dbt_project_1/.sqlfluff: -------------------------------------------------------------------------------- 1 | [sqlfluff] 2 | templater = jinja 3 | verbose = 3 4 | dialect = postgres 5 | 6 | [sqlfluff:templater:dbt] 7 | profile = airbyte_warehouse 8 | 9 | [sqlfluff:indentation] 10 | indented_joins = False 11 | indented_using_on = True 12 | template_blocks_indent = False 13 | 14 | [sqlfluff:templater] 15 | unwrap_wrapped_queries = True 16 | 17 | [sqlfluff:templater:jinja] 18 | apply_dbt_builtins = True 19 | load_macros_from_path = macros 20 | library_path = dbt_packages 21 | 22 | [sqlfluff:templater:jinja:macros] 23 | # Macros provided as builtins for dbt projects 24 | dbt_ref = {% macro ref(model_ref) %}{{model_ref}}{% endmacro %} 25 | dbt_source = {% macro source(source_name, table) %}{{source_name}}_{{table}}{% endmacro %} 26 | dbt_config = {% macro config() %}{% for k in kwargs %}{% endfor %}{% endmacro %} 27 | dbt_var = {% macro var(variable, default='') %}item{% endmacro %} 28 | dbt_is_incremental = {% macro is_incremental() %}True{% endmacro %} 29 | 30 | # Some rules can be configured directly from the config common to other rules. 31 | [sqlfluff:rules] 32 | tab_space_size = 4 33 | max_line_length = 150 34 | indent_unit = space 35 | comma_style = trailing 36 | allow_scalar = True 37 | single_table_references = consistent 38 | unquoted_identifiers_policy = all 39 | 40 | # Some rules have their own specific config. 41 | [sqlfluff:rules:L007] # Keywords 42 | operator_new_lines = after 43 | 44 | [sqlfluff:rules:L010] # Keywords 45 | capitalisation_policy = consistent 46 | 47 | [sqlfluff:rules:L011] # Aliasing 48 | aliasing = explicit 49 | 50 | [sqlfluff:rules:L012] # Aliasing 51 | aliasing = explicit 52 | 53 | [sqlfluff:rules:L014] # Unquoted identifiers 54 | extended_capitalisation_policy = lower 55 | unquoted_identifiers_policy = all 56 | 57 | [sqlfluff:rules:L016] 58 | ignore_comment_lines = False 59 | 60 | [sqlfluff:rules:L026] 61 | force_enable = False 62 | 63 | [sqlfluff:rules:L028] 64 | force_enable = False 65 | 66 | [sqlfluff:rules:L029] # Keyword identifiers 67 | unquoted_identifiers_policy = aliases 68 | quoted_identifiers_policy = none 69 | 70 | [sqlfluff:rules:L030] # Function names 71 | capitalisation_policy = lower 72 | 73 | [sqlfluff:rules:L038] 74 | select_clause_trailing_comma = forbid 75 | 76 | [sqlfluff:rules:L040] # Null & Boolean Literals 77 | capitalisation_policy = lower 78 | 79 | [sqlfluff:rules:L042] 80 | # By default, allow subqueries in from clauses, but not join clauses. 81 | forbid_subquery_in = join 82 | 83 | [sqlfluff:rules:L047] # Consistent syntax to count all rows 84 | prefer_count_1 = False 85 | prefer_count_0 = False 86 | 87 | [sqlfluff:rules:L052] # Semi-colon formatting approach. 88 | multiline_newline = False 89 | require_final_semicolon = False 90 | 91 | [sqlfluff:rules:L054] # GROUP BY/ORDER BY column references. 92 | group_by_and_order_by_style = consistent 93 | 94 | [sqlfluff:rules:L057] # Special characters in identifiers 95 | unquoted_identifiers_policy = all 96 | quoted_identifiers_policy = all 97 | allow_space_in_identifier = False 98 | -------------------------------------------------------------------------------- /stargazer/dbt_project_2/.sqlfluff: -------------------------------------------------------------------------------- 1 | [sqlfluff] 2 | templater = jinja 3 | verbose = 3 4 | dialect = postgres 5 | 6 | [sqlfluff:templater:dbt] 7 | profile = airbyte_warehouse 8 | 9 | [sqlfluff:indentation] 10 | indented_joins = False 11 | indented_using_on = True 12 | template_blocks_indent = False 13 | 14 | [sqlfluff:templater] 15 | unwrap_wrapped_queries = True 16 | 17 | [sqlfluff:templater:jinja] 18 | apply_dbt_builtins = True 19 | load_macros_from_path = macros 20 | library_path = dbt_packages 21 | 22 | [sqlfluff:templater:jinja:macros] 23 | # Macros provided as builtins for dbt projects 24 | dbt_ref = {% macro ref(model_ref) %}{{model_ref}}{% endmacro %} 25 | dbt_source = {% macro source(source_name, table) %}{{source_name}}_{{table}}{% endmacro %} 26 | dbt_config = {% macro config() %}{% for k in kwargs %}{% endfor %}{% endmacro %} 27 | dbt_var = {% macro var(variable, default='') %}item{% endmacro %} 28 | dbt_is_incremental = {% macro is_incremental() %}True{% endmacro %} 29 | 30 | # Some rules can be configured directly from the config common to other rules. 31 | [sqlfluff:rules] 32 | tab_space_size = 4 33 | max_line_length = 150 34 | indent_unit = space 35 | comma_style = trailing 36 | allow_scalar = True 37 | single_table_references = consistent 38 | unquoted_identifiers_policy = all 39 | 40 | # Some rules have their own specific config. 41 | [sqlfluff:rules:L007] # Keywords 42 | operator_new_lines = after 43 | 44 | [sqlfluff:rules:L010] # Keywords 45 | capitalisation_policy = consistent 46 | 47 | [sqlfluff:rules:L011] # Aliasing 48 | aliasing = explicit 49 | 50 | [sqlfluff:rules:L012] # Aliasing 51 | aliasing = explicit 52 | 53 | [sqlfluff:rules:L014] # Unquoted identifiers 54 | extended_capitalisation_policy = lower 55 | unquoted_identifiers_policy = all 56 | 57 | [sqlfluff:rules:L016] 58 | ignore_comment_lines = False 59 | 60 | [sqlfluff:rules:L026] 61 | force_enable = False 62 | 63 | [sqlfluff:rules:L028] 64 | force_enable = False 65 | 66 | [sqlfluff:rules:L029] # Keyword identifiers 67 | unquoted_identifiers_policy = aliases 68 | quoted_identifiers_policy = none 69 | 70 | [sqlfluff:rules:L030] # Function names 71 | capitalisation_policy = lower 72 | 73 | [sqlfluff:rules:L038] 74 | select_clause_trailing_comma = forbid 75 | 76 | [sqlfluff:rules:L040] # Null & Boolean Literals 77 | capitalisation_policy = lower 78 | 79 | [sqlfluff:rules:L042] 80 | # By default, allow subqueries in from clauses, but not join clauses. 81 | forbid_subquery_in = join 82 | 83 | [sqlfluff:rules:L047] # Consistent syntax to count all rows 84 | prefer_count_1 = False 85 | prefer_count_0 = False 86 | 87 | [sqlfluff:rules:L052] # Semi-colon formatting approach. 88 | multiline_newline = False 89 | require_final_semicolon = False 90 | 91 | [sqlfluff:rules:L054] # GROUP BY/ORDER BY column references. 92 | group_by_and_order_by_style = consistent 93 | 94 | [sqlfluff:rules:L057] # Special characters in identifiers 95 | unquoted_identifiers_policy = all 96 | quoted_identifiers_policy = all 97 | allow_space_in_identifier = False 98 | -------------------------------------------------------------------------------- /stargazer/assets_modern_data_stack/my_asset.py: -------------------------------------------------------------------------------- 1 | from dagster import ( 2 | repository, 3 | with_resources, 4 | ) 5 | 6 | from dagster_airbyte import airbyte_resource 7 | from dagster_dbt import dbt_cli_resource 8 | 9 | from .db_io_manager import db_io_manager 10 | from .utils.constants import DBT_CONFIG1, DBT_CONFIG2, POSTGRES_CONFIG 11 | 12 | from dagster import ( 13 | AssetSelection, 14 | ScheduleDefinition, 15 | build_asset_reconciliation_sensor, 16 | define_asset_job, 17 | ) 18 | from dagster_airbyte import ( 19 | AirbyteManagedElementReconciler, 20 | airbyte_resource, 21 | AirbyteConnection, 22 | AirbyteSyncMode, 23 | load_assets_from_connections, 24 | ) 25 | from dagster_airbyte.managed.generated.sources import GithubSource 26 | from dagster_airbyte.managed.generated.destinations import ( 27 | PostgresDestination, 28 | ) 29 | from dagster_dbt import load_assets_from_dbt_project 30 | 31 | import os 32 | from .utils.constants import DBT_PROJECT_DIR1, DBT_PROJECT_DIR2 33 | 34 | AIRBYTE_PERSONAL_GITHUB_TOKEN = os.environ.get( 35 | "AIRBYTE_PERSONAL_GITHUB_TOKEN", "please-set-your-token" 36 | ) 37 | POSTGRES_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "please-set-your-token") 38 | 39 | airbyte_instance = airbyte_resource.configured( 40 | { 41 | "host": "localhost", 42 | "port": "8000", 43 | "username": "airbyte", 44 | "password": {"env": "AIRBYTE_PASSWORD"}, 45 | } 46 | ) 47 | 48 | gh_awesome_de_list_source = GithubSource( 49 | name="gh_awesome_de_list", 50 | credentials=GithubSource.PATCredentials(AIRBYTE_PERSONAL_GITHUB_TOKEN), 51 | start_date="2020-01-01T00:00:00Z", 52 | repository="dbt-labs/dbt-core", 53 | branch="main", 54 | page_size_for_large_streams=100, 55 | ) 56 | 57 | postgres_destination = PostgresDestination( 58 | name="postgres", 59 | host="localhost", 60 | port=5433, 61 | database="postgres", 62 | schema="public", 63 | username="postgres", 64 | password=POSTGRES_PASSWORD, 65 | ssl_mode=PostgresDestination.Disable(), 66 | ) 67 | 68 | stargazer_connection = AirbyteConnection( 69 | name="fetch_stargazer", 70 | source=gh_awesome_de_list_source, 71 | destination=postgres_destination, 72 | stream_config={"stargazers": AirbyteSyncMode.incremental_append_dedup()}, 73 | normalize_data=True, 74 | ) 75 | 76 | airbyte_reconciler = AirbyteManagedElementReconciler( 77 | airbyte=airbyte_instance, 78 | connections=[stargazer_connection], 79 | ) 80 | 81 | # load airbyte connection from above pythonic definitions 82 | airbyte_assets = load_assets_from_connections( 83 | airbyte=airbyte_instance, 84 | connections=[stargazer_connection], 85 | key_prefix=["postgres"], 86 | ) 87 | 88 | # preparing assets bassed on existing dbt project 89 | dbt_assets_1 = load_assets_from_dbt_project( 90 | project_dir=DBT_PROJECT_DIR1, dbt_resource_key="dbt1", io_manager_key="db_io_manager1", exclude="source:*" 91 | ) 92 | 93 | dbt_assets_2 = load_assets_from_dbt_project( 94 | project_dir=DBT_PROJECT_DIR2, dbt_resource_key="dbt2", io_manager_key="db_io_manager2" 95 | ) 96 | 97 | update_sensor = build_asset_reconciliation_sensor( 98 | name="update_sensor", asset_selection=AssetSelection.all() 99 | ) 100 | 101 | my_job = define_asset_job(name="my_job", selection=AssetSelection.keys("postgres/stargazers", "postgres/stargazers_user")) 102 | 103 | my_job_schedule = ScheduleDefinition( 104 | name="my_job_schedule", job=my_job, cron_schedule="*/30 * * * *" 105 | ) 106 | 107 | @repository 108 | def assets_modern_data_stack(): 109 | return [ 110 | airbyte_assets, 111 | with_resources( 112 | dbt_assets_1, 113 | resource_defs={ 114 | "dbt1": dbt_cli_resource.configured(DBT_CONFIG1), 115 | "db_io_manager1": db_io_manager.configured(POSTGRES_CONFIG), 116 | }, 117 | ), 118 | with_resources( 119 | dbt_assets_2, 120 | resource_defs={ 121 | "dbt2": dbt_cli_resource.configured(DBT_CONFIG2), 122 | "db_io_manager2": db_io_manager.configured(POSTGRES_CONFIG), 123 | }, 124 | ), 125 | update_sensor, 126 | my_job, 127 | my_job_schedule 128 | ] 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data-aware orchestration demo 2 | 3 | This project demonstrates dagster's data-aware orchestration capability. 4 | 5 | ### Concepts demonstrated 6 | 7 | - **dbt cross-project lineage**: dagster's ability to create a global dependency graph between different dbt projects. This is currently [not available in dbt](https://github.com/dbt-labs/dbt-core/discussions/5244). 8 | - **Object-level dependencies between different assets**: dagster's ability to create object-level dependencies between different assets like an airbyte table materialization, and a dbt model materialization. 9 | - **Freshness policy triggers**: Most data orchestration tools use cron schedules to trigger an entire DAG. Dagster reverses this approach and allows developers to define freshness policies on nodes so that upstream nodes can be triggered to deliver data to the target node on time. 10 | 11 | ### Data assets 12 | 13 | This project has the following data assets to orchestrate: 14 | 1. An [airbyte](https://airbyte.com/) connection 15 | 1. Two [dbt](https://www.getdbt.com/) projects 16 | 17 | ![global-asset-lineage](docs/images/global-asset-lineage.png) 18 | 19 | This project forks code from a demo prepared by [airbytehq's open-data-stack repo](https://github.com/airbytehq/open-data-stack/tree/main/dagster), and adds additional code to demonstrate newer concepts. 20 | 21 | # Getting started 22 | 23 | ## Set up virtual environment 24 | 25 | A [Pipfile](./Pipfile) has been provided for use with [pipenv](https://pipenv.pypa.io/en/latest/) to define the python version to use for this virtual environment. 26 | 27 | Assuming you already have pipenv installed, to launch the virtual environment for this project, run the following commands: 28 | 29 | ```bash 30 | cd my-dbt-dagster 31 | pipenv shell 32 | ``` 33 | 34 | If you wish to instead use your local python installation, just make sure that it is at least python 3.8 and above. 35 | 36 | ## Install python dependencies 37 | 38 | To install the python dependencies, run: 39 | 40 | ```bash 41 | cd stargazer 42 | pip install -e ".[dev]" 43 | ``` 44 | 45 | ## Set up local Postgres 46 | 47 | We'll use a local postgres instance as the destination for our data. You can imagine the "destination" as a data warehouse (something like Snowflake). 48 | 49 | To get a postgres instance with the required source and destination databases running on your machine, you can run: 50 | 51 | ```bash 52 | docker pull postgres 53 | docker run --name local-postgres -p 5433:5432 -e POSTGRES_PASSWORD=postgres -d postgres 54 | ``` 55 | 56 | Note: I am mapping local port 5433 to the container's port 5432 as my local port 5432 is already in use. 57 | 58 | ## Set up Airbyte 59 | 60 | Now, you'll want to get Airbyte running locally. The full instructions can be found [here](https://docs.airbyte.com/deploying-airbyte/local-deployment). 61 | 62 | The steps are pretty simple. Run the following in a new terminal: 63 | 64 | ```bash 65 | git clone https://github.com/airbytehq/airbyte.git 66 | cd airbyte 67 | docker-compose up 68 | ``` 69 | 70 | This should take a couple of minutes to pull the images and run them. 71 | 72 | ## Set up airbyte connection 73 | 74 | Now that airbyte is running locally, let's create the source, destination, and connection for a data integration pipeline on airbyte. 75 | 76 | First we set the environment variables we need: 77 | 78 | ```bash 79 | export AIRBYTE_PASSWORD=password 80 | export AIRBYTE_PERSONAL_GITHUB_TOKEN= 81 | ``` 82 | Note: 83 | - The default password for airbyte is `password`. 84 | - We'll need to [create](https://github.com/settings/tokens) a token `AIRBYTE_PERSONAL_GITHUB_TOKEN` for fetching the stargazers from the public repositories. 85 | 86 | After setting the environment variables, we can check if we have everything we need to let dagster create the airbyte source, destination, and connection by running: 87 | 88 | ```bash 89 | cd stargazer 90 | dagster-me check --module assets_modern_data_stack.my_asset:airbyte_reconciler 91 | ``` 92 | 93 | This will print out the assets that dagster will create in airbyte. For example: 94 | 95 | ``` 96 | + fetch_stargazer: 97 | + source: gh_awesome_de_list 98 | + normalize data: True 99 | + destination: postgres 100 | + destination namespace: SAME_AS_SOURCE 101 | + streams: 102 | + stargazers: 103 | + destinationSyncMode: append_dedup 104 | + syncMode: incremental 105 | ``` 106 | 107 | If you are happy with those assets being created in airbyte, then run the following to apply it: 108 | 109 | ```bash 110 | dagster-me apply --module assets_modern_data_stack.my_asset:airbyte_reconciler 111 | ``` 112 | 113 | ## Set up dbt 114 | 115 | We have 2 dbt projects in the [stargazer](./stargazer/) folder: 116 | 117 | - [dbt_project_1](./stargazer/dbt_project_1/) 118 | - [dbt_project_2](./stargazer/dbt_project_2/) 119 | 120 | Install the dbt dependencies required by both projects by running: 121 | 122 | ```bash 123 | cd stargazer/dbt_project_1 124 | dbt deps 125 | ``` 126 | 127 | ```bash 128 | cd stargazer/dbt_project_2 129 | dbt deps 130 | ``` 131 | 132 | ## Start dagster 133 | 134 | We're now ready to get dagster started. Dagster has two services that we need to run: 135 | - [dagit](https://docs.dagster.io/concepts/dagit/dagit): The web-based interface for viewing and interacting with Dagster objects. 136 | - [dagster daemon](https://docs.dagster.io/deployment/dagster-daemon): The service that manages schdeules, sensors, and run queuing. 137 | 138 | For both services to communicate and have shared resources with one another, we need to create a shared directory: 139 | 140 | ```bash 141 | mkdir ~"/dagster_home" 142 | ``` 143 | 144 | We named our shared directory as `dagster_home` for simplicity. 145 | 146 | To run the dagster daemon service, create a new terminal and run: 147 | 148 | ```bash 149 | export DAGSTER_HOME=~"/dagster_home" 150 | export AIRBYTE_PASSWORD=password 151 | export POSTGRES_PASSWORD=postgres 152 | dagster-daemon run -m assets_modern_data_stack.my_asset 153 | ``` 154 | 155 | To run the dagit service, create a new terminal and run: 156 | 157 | ```bash 158 | export DAGSTER_HOME=~"/dagster_home" 159 | export AIRBYTE_PASSWORD=password 160 | export POSTGRES_PASSWORD=postgres 161 | dagit -m assets_modern_data_stack.my_asset 162 | ``` 163 | 164 | Launch the dagit UI by going to [http://localhost:3000/](http://localhost:3000/). 165 | 166 | You'll see the assets of airbyte, dbt that are created automatically in this demo. 167 | 168 | ![deployment](/docs/images/deployment.png) 169 | 170 | Activate the schedule: 171 | 172 | ![schedule](/docs/images/schedule.png) 173 | 174 | Activate the sensor: 175 | 176 | ![sensor](/docs/images/sensor.png) 177 | 178 | # Interact with dagster 179 | 180 | Now you can sit back and watch the [global asset lineage](http://localhost:3000/asset-groups/) trigger based on the schedule and/or sensor trigger. 181 | 182 | You'll notice the following behaviours: 183 | 184 | 1. The airbyte assets will materialize every 30 minutes based on a schedule. 185 | 186 | ![airbyte](/docs/images/airbyte-assets.png) 187 | 188 | 2. The two dbt projects [dbt_project_1](./stargazer/dbt_project_1/) and [dbt_project_2](./stargazer/dbt_project_2/), are now seen as part of the same global asset lineage in dagster without any separation between dbt projects. 189 | 190 | ![dbt-projects](/docs/images/dbt-projects.png) 191 | 192 | 3. `mart_gh_cumulative` will materialize every 5 minutes because it's dbt model [mart_gh_cumulative.sql](stargazer/dbt_project_2/models/mart/mart_gh_cumulative.sql) has a freshness policy of `dagster_freshness_policy={"maximum_lag_minutes": 5}`. This in turn will also trigger the airbyte assets to be materialized first. 193 | 194 | ![5-minute-freshness](/docs/images/5-minute-freshness.png) 195 | 196 | 4. `mart_gh_join` and `mart_gh_stargazer`: by 09:00 AM UTC, these assets should incorporate all data up to 9 hours before that time. This is because a dbt project-level configuration has been set for [project 1](stargazer/dbt_project_1/dbt_project.yml) and [project 2](stargazer/dbt_project_2/dbt_project.yml) with a freshness policy of `maximum_lag_minutes: 540` and `cron_schedule: "0 9 * * *"`. 197 | 198 | ![9am-freshness](/docs/images/9am-freshness.png) 199 | 200 | 201 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "2657899317fc6f4b239f8686745f4c900c69260e450a45c8c5abaa0f5e45196b" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_full_version": "3.9.14", 9 | "python_version": "3.9" 10 | }, 11 | "sources": [ 12 | { 13 | "name": "pypi", 14 | "url": "https://pypi.org/simple", 15 | "verify_ssl": true 16 | } 17 | ] 18 | }, 19 | "default": { 20 | "alembic": { 21 | "hashes": [ 22 | "sha256:a9781ed0979a20341c2cbb56bd22bd8db4fc1913f955e705444bd3a97c59fa32", 23 | "sha256:f9f76e41061f5ebe27d4fe92600df9dd612521a7683f904dab328ba02cffa5a2" 24 | ], 25 | "markers": "python_version >= '3.7'", 26 | "version": "==1.9.1" 27 | }, 28 | "certifi": { 29 | "hashes": [ 30 | "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", 31 | "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" 32 | ], 33 | "markers": "python_version >= '3.6'", 34 | "version": "==2022.12.7" 35 | }, 36 | "charset-normalizer": { 37 | "hashes": [ 38 | "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", 39 | "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" 40 | ], 41 | "markers": "python_full_version >= '3.6.0'", 42 | "version": "==2.1.1" 43 | }, 44 | "click": { 45 | "hashes": [ 46 | "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", 47 | "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" 48 | ], 49 | "markers": "python_version >= '3.7'", 50 | "version": "==8.1.3" 51 | }, 52 | "coloredlogs": { 53 | "hashes": [ 54 | "sha256:346f58aad6afd48444c2468618623638dadab76e4e70d5e10822676f2d32226a", 55 | "sha256:a1fab193d2053aa6c0a97608c4342d031f1f93a3d1218432c59322441d31a505" 56 | ], 57 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 58 | "version": "==14.0" 59 | }, 60 | "croniter": { 61 | "hashes": [ 62 | "sha256:32a5ec04e97ec0837bcdf013767abd2e71cceeefd3c2e14c804098ce51ad6cd9", 63 | "sha256:d6ed8386d5f4bbb29419dc1b65c4909c04a2322bd15ec0dc5b2877bfa1b75c7a" 64 | ], 65 | "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", 66 | "version": "==1.3.8" 67 | }, 68 | "dagster": { 69 | "hashes": [ 70 | "sha256:73a5f1c3f6d6ba154757cfca4245fdd7b1992ad32cccbad4273f8cd9371f02f3", 71 | "sha256:cb9d106ec541b3b09fa532c3d095399a3ad989f9a6179b585d6daf1561e106a5" 72 | ], 73 | "index": "pypi", 74 | "version": "==1.1.9" 75 | }, 76 | "docstring-parser": { 77 | "hashes": [ 78 | "sha256:48ddc093e8b1865899956fcc03b03e66bb7240c310fac5af81814580c55bf682", 79 | "sha256:d1679b86250d269d06a99670924d6bce45adc00b08069dae8c47d98e89b667a9" 80 | ], 81 | "markers": "python_version >= '3.6' and python_version < '4.0'", 82 | "version": "==0.15" 83 | }, 84 | "fsspec": { 85 | "hashes": [ 86 | "sha256:259d5fd5c8e756ff2ea72f42e7613c32667dc2049a4ac3d84364a7ca034acb8b", 87 | "sha256:d6e462003e3dcdcb8c7aa84c73a228f8227e72453cd22570e2363e8844edfe7b" 88 | ], 89 | "markers": "python_version >= '3.7'", 90 | "version": "==2022.11.0" 91 | }, 92 | "grpcio": { 93 | "hashes": [ 94 | "sha256:07b14bd4e4c12942887a2b0a08d008929d744c47f6eacf83711c778b4555a943", 95 | "sha256:0b78e060766967959b53c460ce3fa6d31d6c0dde9f801976875d2e1cac2098f5", 96 | "sha256:1a669c2f52959c82e5994884ac8940fa626dff2f3a9398098d5cdded3ed0b214", 97 | "sha256:2e76b9447e87faaca797f6c74469466a77c6a95aa5e0e36e0887687c204d471c", 98 | "sha256:32b28bfd6de8037ae214bd81af846cf29d39c6518e2005a544c28b10cdef42cb", 99 | "sha256:346859e7c279e81611cf270e2ea67990d6a314e3e2aa75f924615268e0764d0f", 100 | "sha256:36b215d89cadc11c156db8ae4b37ebc50675bae3f75cee230efaf399d19c00c4", 101 | "sha256:3d04a83e9348d464c789f39c15c30608dc51346a2c76502df444197b11463d90", 102 | "sha256:42a87566378866b98e89d2a7c0b5142cf462a5acba041827902f90d561763d14", 103 | "sha256:56e593416e67ca4af7edd626dabe1531d9ef966f8a78aa3156c7d5618b5b270a", 104 | "sha256:60967c0d7725a11978f1d3fe60e21e0e0ad10fac920a35d29721f2b01089604f", 105 | "sha256:60c69dadc0e9f96cb031d1902f8db6e4ef94bbc17667fc673919dc57871744de", 106 | "sha256:696fbb04702dc8d2fcf2a76b366378f2adc606a1c65f277ebbc00f78d5a71cc1", 107 | "sha256:6ce3ab22060f1e04b06567334f07c11d37e01f019841df46910aa2680b4dbc89", 108 | "sha256:70b52128e4395356896bb4eb70c8f8de10f9ce4000eca192a53889a76f80c647", 109 | "sha256:73f46fa86d33a3da2f071c65d1c0bf8b4cef49983e1518aeebaf7700b2a4c23f", 110 | "sha256:89b2696928d0db627a80a55158049f760046b90234bb5a9aeb23f9be2750a03e", 111 | "sha256:8b79f5241acf4a3b3c063a7a6866b70289f5d5fbaa0fcb48e566ce55f85c26fd", 112 | "sha256:92d5f9c7ecb31d6e2579744969f089b285fe45c725d26366a0c1e858d2d52c44", 113 | "sha256:98ecdaea5d66df8ae4a846b50c253aa8bf7ee4162552f56ce37a5d6d561bb711", 114 | "sha256:9ad746fe50780b9b8fb3e15e758d96561758c3cf6f96356cee76721ccd76d254", 115 | "sha256:9ca6bdc2ca97419d9154a2858e7190109280143c87f3eda4ad6dada2729dea7e", 116 | "sha256:a202f137d1e7f018aa28a62bb464fa773ebe37c68ae9761a1b55a00d9beaf2f0", 117 | "sha256:a57f6cef560c7b9f56549d30ef3b3be0c9ec5f0db321062c67fca05756daedaa", 118 | "sha256:a8d4feb221082e91843bcdf16186014583d0fb8671ee0092f66c956752dd81ab", 119 | "sha256:af911454d415eb9d18f011b10c66ac29de87266e47fab5d00481d6478c7cc814", 120 | "sha256:b5ca2fb4b7e48585d5d90e3dbcfbb6efed763c73e6ab2419a2377611396f8a24", 121 | "sha256:bd9129344ccc0845e01134e87e8b7c11f2b6943d0aff016f88273a6b5910bfed", 122 | "sha256:c0ee8af10c65d66f1896b5da09abb4a7bddcf3b6e7a6ffc5661713525ca98704", 123 | "sha256:c17acc79c7280334f5db8af75af9b0861f0f613dca41c982f471d967142a0bad", 124 | "sha256:c1900803f47c37fa7c055f223d88111181ef53c5fc902391ad4f452aa5c97d2a", 125 | "sha256:c7f888b067f2b1833a5670f20eeac2c6063039a23a99b799929de065c1b18692", 126 | "sha256:c867a2ad3fced11b75a27672a1c4d72c1c2d42ed11803bce3263e91eda4b2ce0", 127 | "sha256:c96813980f8e6c3ca302221dfadb380dedca911cd7f74bbab66c98c83b310cee", 128 | "sha256:cd4106bf10fa440c5dd7c5f21484c7b1e608f0b70a274dc6c2c29f3eee4827f6", 129 | "sha256:d3c701104bebc6c66f9fff2ac1f4704f3ab45e179f2be496b2689b6e35179bd4", 130 | "sha256:d6aa4556fb4be906baf2e2501e9edff9fd4817d88a1f544b8d7a7948e48b1954", 131 | "sha256:e578933d5b365506ae85cbe104c5156baa2307ecdcc1a57d80143c078dca4b8f", 132 | "sha256:e5abb83b78a337c7731feb4c734fcaa595b3a1f1bdbbd6233cfc08cbf0a83405", 133 | "sha256:e9d86d03cc8c4dcd1e3d2593dccf12fc61b044358e3085bfd23e50d47686657b", 134 | "sha256:ef09be9118f82bc2ff6aac77d7436eef2434a9580b6ffb3b62ddafc9c1b2015f", 135 | "sha256:ef1f17c47269391af42bc2d64997c9b62bd80ae44cf595b77cf047f4a00b566a", 136 | "sha256:f020d31693c372b89bd479eceec000d9462107a3b17bd0b7aed32f06fe246efe", 137 | "sha256:f13ae44d39c297b6633c86180a11e6c70cf5596074cd16d7dcee55634d9a70e6", 138 | "sha256:f23bf8eafa0265c12b3c54e0b3b77dc4f24dde4b6e9260aecffaed3cfe5d1fbd", 139 | "sha256:f3510688f6c185a9483ecd1580a0ff5e5b0a7301387d831ec6e31e1c1430bd5d" 140 | ], 141 | "markers": "python_version >= '3.6'", 142 | "version": "==1.47.2" 143 | }, 144 | "grpcio-health-checking": { 145 | "hashes": [ 146 | "sha256:23b3c6c3a6da85f7cd944cbebd965cdceb2a5e4e65f071a2b6cf1eb3510c993e", 147 | "sha256:802195ec77dd609ed9721dcb4d5916b6018dc49351b904d152fb697595debd33" 148 | ], 149 | "markers": "python_version >= '3.6'", 150 | "version": "==1.43.0" 151 | }, 152 | "humanfriendly": { 153 | "hashes": [ 154 | "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", 155 | "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc" 156 | ], 157 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 158 | "version": "==10.0" 159 | }, 160 | "idna": { 161 | "hashes": [ 162 | "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", 163 | "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" 164 | ], 165 | "markers": "python_version >= '3.5'", 166 | "version": "==3.4" 167 | }, 168 | "jinja2": { 169 | "hashes": [ 170 | "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", 171 | "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" 172 | ], 173 | "markers": "python_version >= '3.7'", 174 | "version": "==3.1.2" 175 | }, 176 | "mako": { 177 | "hashes": [ 178 | "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818", 179 | "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34" 180 | ], 181 | "markers": "python_version >= '3.7'", 182 | "version": "==1.2.4" 183 | }, 184 | "markupsafe": { 185 | "hashes": [ 186 | "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", 187 | "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", 188 | "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", 189 | "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", 190 | "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", 191 | "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", 192 | "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", 193 | "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", 194 | "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", 195 | "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", 196 | "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", 197 | "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", 198 | "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", 199 | "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", 200 | "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", 201 | "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", 202 | "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", 203 | "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", 204 | "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", 205 | "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", 206 | "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", 207 | "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", 208 | "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", 209 | "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", 210 | "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", 211 | "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", 212 | "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", 213 | "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", 214 | "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", 215 | "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", 216 | "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", 217 | "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", 218 | "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", 219 | "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", 220 | "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", 221 | "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", 222 | "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", 223 | "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", 224 | "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", 225 | "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" 226 | ], 227 | "markers": "python_version >= '3.7'", 228 | "version": "==2.1.1" 229 | }, 230 | "packaging": { 231 | "hashes": [ 232 | "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", 233 | "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" 234 | ], 235 | "markers": "python_version >= '3.6'", 236 | "version": "==21.3" 237 | }, 238 | "pendulum": { 239 | "hashes": [ 240 | "sha256:0731f0c661a3cb779d398803655494893c9f581f6488048b3fb629c2342b5394", 241 | "sha256:1245cd0075a3c6d889f581f6325dd8404aca5884dea7223a5566c38aab94642b", 242 | "sha256:29c40a6f2942376185728c9a0347d7c0f07905638c83007e1d262781f1e6953a", 243 | "sha256:2d1619a721df661e506eff8db8614016f0720ac171fe80dda1333ee44e684087", 244 | "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739", 245 | "sha256:33fb61601083f3eb1d15edeb45274f73c63b3c44a8524703dc143f4212bf3269", 246 | "sha256:3481fad1dc3f6f6738bd575a951d3c15d4b4ce7c82dce37cf8ac1483fde6e8b0", 247 | "sha256:4c9c689747f39d0d02a9f94fcee737b34a5773803a64a5fdb046ee9cac7442c5", 248 | "sha256:7c5ec650cb4bec4c63a89a0242cc8c3cebcec92fcfe937c417ba18277d8560be", 249 | "sha256:94b1fc947bfe38579b28e1cccb36f7e28a15e841f30384b5ad6c5e31055c85d7", 250 | "sha256:9702069c694306297ed362ce7e3c1ef8404ac8ede39f9b28b7c1a7ad8c3959e3", 251 | "sha256:b06a0ca1bfe41c990bbf0c029f0b6501a7f2ec4e38bfec730712015e8860f207", 252 | "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe", 253 | "sha256:c501749fdd3d6f9e726086bf0cd4437281ed47e7bca132ddb522f86a1645d360", 254 | "sha256:c807a578a532eeb226150d5006f156632df2cc8c5693d778324b43ff8c515dd0", 255 | "sha256:db0a40d8bcd27b4fb46676e8eb3c732c67a5a5e6bfab8927028224fbced0b40b", 256 | "sha256:de42ea3e2943171a9e95141f2eecf972480636e8e484ccffaf1e833929e9e052", 257 | "sha256:e95d329384717c7bf627bf27e204bc3b15c8238fa8d9d9781d93712776c14002", 258 | "sha256:f5e236e7730cab1644e1b87aca3d2ff3e375a608542e90fe25685dae46310116", 259 | "sha256:f888f2d2909a414680a29ae74d0592758f2b9fcdee3549887779cd4055e975db", 260 | "sha256:fb53ffa0085002ddd43b6ca61a7b34f2d4d7c3ed66f931fe599e1a531b42af9b" 261 | ], 262 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 263 | "version": "==2.1.2" 264 | }, 265 | "protobuf": { 266 | "hashes": [ 267 | "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7", 268 | "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c", 269 | "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2", 270 | "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b", 271 | "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050", 272 | "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9", 273 | "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7", 274 | "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454", 275 | "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480", 276 | "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469", 277 | "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c", 278 | "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e", 279 | "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db", 280 | "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905", 281 | "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b", 282 | "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86", 283 | "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4", 284 | "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402", 285 | "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7", 286 | "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4", 287 | "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99", 288 | "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee" 289 | ], 290 | "markers": "python_version >= '3.7'", 291 | "version": "==3.20.3" 292 | }, 293 | "pydantic": { 294 | "hashes": [ 295 | "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72", 296 | "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423", 297 | "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f", 298 | "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c", 299 | "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06", 300 | "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53", 301 | "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774", 302 | "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6", 303 | "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c", 304 | "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f", 305 | "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6", 306 | "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3", 307 | "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817", 308 | "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903", 309 | "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a", 310 | "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e", 311 | "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d", 312 | "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85", 313 | "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00", 314 | "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28", 315 | "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3", 316 | "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024", 317 | "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4", 318 | "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e", 319 | "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d", 320 | "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa", 321 | "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854", 322 | "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15", 323 | "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648", 324 | "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8", 325 | "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c", 326 | "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857", 327 | "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f", 328 | "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416", 329 | "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978", 330 | "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d" 331 | ], 332 | "markers": "python_version >= '3.7'", 333 | "version": "==1.10.4" 334 | }, 335 | "pyparsing": { 336 | "hashes": [ 337 | "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", 338 | "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" 339 | ], 340 | "markers": "python_full_version >= '3.6.8'", 341 | "version": "==3.0.9" 342 | }, 343 | "python-dateutil": { 344 | "hashes": [ 345 | "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", 346 | "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" 347 | ], 348 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 349 | "version": "==2.8.2" 350 | }, 351 | "python-dotenv": { 352 | "hashes": [ 353 | "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5", 354 | "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045" 355 | ], 356 | "markers": "python_version >= '3.7'", 357 | "version": "==0.21.0" 358 | }, 359 | "pytz": { 360 | "hashes": [ 361 | "sha256:7ccfae7b4b2c067464a6733c6261673fdb8fd1be905460396b97a073e9fa683a", 362 | "sha256:93007def75ae22f7cd991c84e02d434876818661f8df9ad5df9e950ff4e52cfd" 363 | ], 364 | "version": "==2022.7" 365 | }, 366 | "pytzdata": { 367 | "hashes": [ 368 | "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540", 369 | "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f" 370 | ], 371 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 372 | "version": "==2020.1" 373 | }, 374 | "pyyaml": { 375 | "hashes": [ 376 | "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", 377 | "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", 378 | "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", 379 | "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", 380 | "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", 381 | "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", 382 | "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", 383 | "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", 384 | "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", 385 | "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", 386 | "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", 387 | "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", 388 | "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", 389 | "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", 390 | "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", 391 | "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", 392 | "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", 393 | "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", 394 | "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", 395 | "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", 396 | "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", 397 | "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", 398 | "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", 399 | "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", 400 | "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", 401 | "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", 402 | "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", 403 | "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", 404 | "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", 405 | "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", 406 | "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", 407 | "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", 408 | "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", 409 | "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", 410 | "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", 411 | "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", 412 | "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", 413 | "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", 414 | "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", 415 | "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" 416 | ], 417 | "markers": "python_version >= '3.6'", 418 | "version": "==6.0" 419 | }, 420 | "requests": { 421 | "hashes": [ 422 | "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", 423 | "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" 424 | ], 425 | "markers": "python_version >= '3.7' and python_version < '4'", 426 | "version": "==2.28.1" 427 | }, 428 | "setuptools": { 429 | "hashes": [ 430 | "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54", 431 | "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75" 432 | ], 433 | "markers": "python_version >= '3.7'", 434 | "version": "==65.6.3" 435 | }, 436 | "six": { 437 | "hashes": [ 438 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 439 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 440 | ], 441 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 442 | "version": "==1.16.0" 443 | }, 444 | "sqlalchemy": { 445 | "hashes": [ 446 | "sha256:07e48cbcdda6b8bc7a59d6728bd3f5f574ffe03f2c9fb384239f3789c2d95c2e", 447 | "sha256:18cafdb27834fa03569d29f571df7115812a0e59fd6a3a03ccb0d33678ec8420", 448 | "sha256:1b1e5e96e2789d89f023d080bee432e2fef64d95857969e70d3cadec80bd26f0", 449 | "sha256:315676344e3558f1f80d02535f410e80ea4e8fddba31ec78fe390eff5fb8f466", 450 | "sha256:31de1e2c45e67a5ec1ecca6ec26aefc299dd5151e355eb5199cd9516b57340be", 451 | "sha256:3d94682732d1a0def5672471ba42a29ff5e21bb0aae0afa00bb10796fc1e28dd", 452 | "sha256:3ec187acf85984263299a3f15c34a6c0671f83565d86d10f43ace49881a82718", 453 | "sha256:4847f4b1d822754e35707db913396a29d874ee77b9c3c3ef3f04d5a9a6209618", 454 | "sha256:4d112b0f3c1bc5ff70554a97344625ef621c1bfe02a73c5d97cac91f8cd7a41e", 455 | "sha256:51e1ba2884c6a2b8e19109dc08c71c49530006c1084156ecadfaadf5f9b8b053", 456 | "sha256:535377e9b10aff5a045e3d9ada8a62d02058b422c0504ebdcf07930599890eb0", 457 | "sha256:5dbf17ac9a61e7a3f1c7ca47237aac93cabd7f08ad92ac5b96d6f8dea4287fc1", 458 | "sha256:5f752676fc126edc1c4af0ec2e4d2adca48ddfae5de46bb40adbd3f903eb2120", 459 | "sha256:64cb0ad8a190bc22d2112001cfecdec45baffdf41871de777239da6a28ed74b6", 460 | "sha256:6913b8247d8a292ef8315162a51931e2b40ce91681f1b6f18f697045200c4a30", 461 | "sha256:69fac0a7054d86b997af12dc23f581cf0b25fb1c7d1fed43257dee3af32d3d6d", 462 | "sha256:7001f16a9a8e06488c3c7154827c48455d1c1507d7228d43e781afbc8ceccf6d", 463 | "sha256:7b81b1030c42b003fc10ddd17825571603117f848814a344d305262d370e7c34", 464 | "sha256:7f8267682eb41a0584cf66d8a697fef64b53281d01c93a503e1344197f2e01fe", 465 | "sha256:887865924c3d6e9a473dc82b70977395301533b3030d0f020c38fd9eba5419f2", 466 | "sha256:9167d4227b56591a4cc5524f1b79ccd7ea994f36e4c648ab42ca995d28ebbb96", 467 | "sha256:939f9a018d2ad04036746e15d119c0428b1e557470361aa798e6e7d7f5875be0", 468 | "sha256:955162ad1a931fe416eded6bb144ba891ccbf9b2e49dc7ded39274dd9c5affc5", 469 | "sha256:984ee13543a346324319a1fb72b698e521506f6f22dc37d7752a329e9cd00a32", 470 | "sha256:9883f5fae4fd8e3f875adc2add69f8b945625811689a6c65866a35ee9c0aea23", 471 | "sha256:a1ad90c97029cc3ab4ffd57443a20fac21d2ec3c89532b084b073b3feb5abff3", 472 | "sha256:a3714e5b33226131ac0da60d18995a102a17dddd42368b7bdd206737297823ad", 473 | "sha256:ae067ab639fa499f67ded52f5bc8e084f045d10b5ac7bb928ae4ca2b6c0429a5", 474 | "sha256:b33ffbdbbf5446cf36cd4cc530c9d9905d3c2fe56ed09e25c22c850cdb9fac92", 475 | "sha256:b6e4cb5c63f705c9d546a054c60d326cbde7421421e2d2565ce3e2eee4e1a01f", 476 | "sha256:b7f4b6aa6e87991ec7ce0e769689a977776db6704947e562102431474799a857", 477 | "sha256:c04144a24103135ea0315d459431ac196fe96f55d3213bfd6d39d0247775c854", 478 | "sha256:c522e496f9b9b70296a7675272ec21937ccfc15da664b74b9f58d98a641ce1b6", 479 | "sha256:c5a99282848b6cae0056b85da17392a26b2d39178394fc25700bcf967e06e97a", 480 | "sha256:c7a46639ba058d320c9f53a81db38119a74b8a7a1884df44d09fbe807d028aaf", 481 | "sha256:d4b1cc7835b39835c75cf7c20c926b42e97d074147c902a9ebb7cf2c840dc4e2", 482 | "sha256:d4d164df3d83d204c69f840da30b292ac7dc54285096c6171245b8d7807185aa", 483 | "sha256:d61e9ecc849d8d44d7f80894ecff4abe347136e9d926560b818f6243409f3c86", 484 | "sha256:d68e1762997bfebf9e5cf2a9fd0bcf9ca2fdd8136ce7b24bbd3bbfa4328f3e4a", 485 | "sha256:e3c1808008124850115a3f7e793a975cfa5c8a26ceeeb9ff9cbb4485cac556df", 486 | "sha256:f8cb80fe8d14307e4124f6fad64dfd87ab749c9d275f82b8b4ec84c84ecebdbe" 487 | ], 488 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 489 | "version": "==1.4.46" 490 | }, 491 | "tabulate": { 492 | "hashes": [ 493 | "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", 494 | "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" 495 | ], 496 | "markers": "python_version >= '3.7'", 497 | "version": "==0.9.0" 498 | }, 499 | "tomli": { 500 | "hashes": [ 501 | "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", 502 | "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" 503 | ], 504 | "markers": "python_version >= '3.7'", 505 | "version": "==2.0.1" 506 | }, 507 | "toposort": { 508 | "hashes": [ 509 | "sha256:b1e89996c43daaf0e03805d33df22333c99c9d36715b188dea0e551ce2f1cd81", 510 | "sha256:c87fd1a8d70b2ca8c928eaf90a538307171fed89e1dcfcdbf7cf6599dfc3208a" 511 | ], 512 | "version": "==1.8" 513 | }, 514 | "tqdm": { 515 | "hashes": [ 516 | "sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4", 517 | "sha256:6fee160d6ffcd1b1c68c65f14c829c22832bc401726335ce92c52d395944a6a1" 518 | ], 519 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", 520 | "version": "==4.64.1" 521 | }, 522 | "typing-extensions": { 523 | "hashes": [ 524 | "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", 525 | "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" 526 | ], 527 | "markers": "python_version >= '3.7'", 528 | "version": "==4.4.0" 529 | }, 530 | "universal-pathlib": { 531 | "hashes": [ 532 | "sha256:3ffba2574999ca6aca8c9f4f224499c6571c92cee612cd923a9e909bf23f586b", 533 | "sha256:ed18290f2ded33481a754aac3da94fb6bf78f628027b10c3e95ceb6075415e69" 534 | ], 535 | "markers": "python_version >= '3.7'", 536 | "version": "==0.0.21" 537 | }, 538 | "urllib3": { 539 | "hashes": [ 540 | "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc", 541 | "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8" 542 | ], 543 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", 544 | "version": "==1.26.13" 545 | }, 546 | "watchdog": { 547 | "hashes": [ 548 | "sha256:102a60093090fc3ff76c983367b19849b7cc24ec414a43c0333680106e62aae1", 549 | "sha256:17f1708f7410af92ddf591e94ae71a27a13974559e72f7e9fde3ec174b26ba2e", 550 | "sha256:195ab1d9d611a4c1e5311cbf42273bc541e18ea8c32712f2fb703cfc6ff006f9", 551 | "sha256:4cb5ecc332112017fbdb19ede78d92e29a8165c46b68a0b8ccbd0a154f196d5e", 552 | "sha256:5100eae58133355d3ca6c1083a33b81355c4f452afa474c2633bd2fbbba398b3", 553 | "sha256:61fdb8e9c57baf625e27e1420e7ca17f7d2023929cd0065eb79c83da1dfbeacd", 554 | "sha256:6ccd8d84b9490a82b51b230740468116b8205822ea5fdc700a553d92661253a3", 555 | "sha256:6e01d699cd260d59b84da6bda019dce0a3353e3fcc774408ae767fe88ee096b7", 556 | "sha256:748ca797ff59962e83cc8e4b233f87113f3cf247c23e6be58b8a2885c7337aa3", 557 | "sha256:83a7cead445008e880dbde833cb9e5cc7b9a0958edb697a96b936621975f15b9", 558 | "sha256:8586d98c494690482c963ffb24c49bf9c8c2fe0589cec4dc2f753b78d1ec301d", 559 | "sha256:8b5cde14e5c72b2df5d074774bdff69e9b55da77e102a91f36ef26ca35f9819c", 560 | "sha256:8c28c23972ec9c524967895ccb1954bc6f6d4a557d36e681a36e84368660c4ce", 561 | "sha256:967636031fa4c4955f0f3f22da3c5c418aa65d50908d31b73b3b3ffd66d60640", 562 | "sha256:96cbeb494e6cbe3ae6aacc430e678ce4b4dd3ae5125035f72b6eb4e5e9eb4f4e", 563 | "sha256:978a1aed55de0b807913b7482d09943b23a2d634040b112bdf31811a422f6344", 564 | "sha256:a09483249d25cbdb4c268e020cb861c51baab2d1affd9a6affc68ffe6a231260", 565 | "sha256:a480d122740debf0afac4ddd583c6c0bb519c24f817b42ed6f850e2f6f9d64a8", 566 | "sha256:adaf2ece15f3afa33a6b45f76b333a7da9256e1360003032524d61bdb4c422ae", 567 | "sha256:bc43c1b24d2f86b6e1cc15f68635a959388219426109233e606517ff7d0a5a73", 568 | "sha256:c27d8c1535fd4474e40a4b5e01f4ba6720bac58e6751c667895cbc5c8a7af33c", 569 | "sha256:cdcc23c9528601a8a293eb4369cbd14f6b4f34f07ae8769421252e9c22718b6f", 570 | "sha256:cece1aa596027ff56369f0b50a9de209920e1df9ac6d02c7f9e5d8162eb4f02b", 571 | "sha256:d0f29fd9f3f149a5277929de33b4f121a04cf84bb494634707cfa8ea8ae106a8", 572 | "sha256:d6b87477752bd86ac5392ecb9eeed92b416898c30bd40c7e2dd03c3146105646", 573 | "sha256:e038be858425c4f621900b8ff1a3a1330d9edcfeaa1c0468aeb7e330fb87693e", 574 | "sha256:e618a4863726bc7a3c64f95c218437f3349fb9d909eb9ea3a1ed3b567417c661", 575 | "sha256:f8ac23ff2c2df4471a61af6490f847633024e5aa120567e08d07af5718c9d092" 576 | ], 577 | "markers": "python_version >= '3.6'", 578 | "version": "==2.2.1" 579 | } 580 | }, 581 | "develop": {} 582 | } 583 | --------------------------------------------------------------------------------