├── dags ├── core sentiment │ ├── __init__.py │ ├── includes │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-312.pyc │ │ │ ├── analysis.cpython-312.pyc │ │ │ ├── fetch_page.cpython-312.pyc │ │ │ └── download_file.cpython-312.pyc │ │ ├── analysis.py │ │ ├── download_file.py │ │ └── fetch_page.py │ ├── __pycache__ │ │ ├── __init__.cpython-312.pyc │ │ └── coresentiment.cpython-312.pyc │ └── coresentiment.py └── sql_scripts │ ├── create_table_schema.sql │ └── coresentiment_schema.sql ├── Dockerfile ├── .gitignore ├── pipeline_image.png ├── analysis.sql ├── README.md └── docker-compose.yaml /dags/core sentiment/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM apache/airflow:2.10.1 -------------------------------------------------------------------------------- /dags/core sentiment/includes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | /dags/CoreSentiment/files 3 | .devcontainer/ 4 | .github/ 5 | /logs -------------------------------------------------------------------------------- /pipeline_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chisomnwa/CDE_Core_Sentiment_Analysis/master/pipeline_image.png -------------------------------------------------------------------------------- /analysis.sql: -------------------------------------------------------------------------------- 1 | sql =''' SELECT * FROM sentiment_table 2 | ORDER BY num_appeared DESC 3 | LIMIT 1; 4 | ''' -------------------------------------------------------------------------------- /dags/core sentiment/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chisomnwa/CDE_Core_Sentiment_Analysis/master/dags/core sentiment/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /dags/sql_scripts/create_table_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS sentiment_tbl ( 2 | id SERIAL PRIMARY KEY, 3 | name VARCHAR NOT NULL, 4 | num_appeared VARCHAR NOT NULL); 5 | -------------------------------------------------------------------------------- /dags/core sentiment/__pycache__/coresentiment.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chisomnwa/CDE_Core_Sentiment_Analysis/master/dags/core sentiment/__pycache__/coresentiment.cpython-312.pyc -------------------------------------------------------------------------------- /dags/core sentiment/includes/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chisomnwa/CDE_Core_Sentiment_Analysis/master/dags/core sentiment/includes/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /dags/core sentiment/includes/__pycache__/analysis.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chisomnwa/CDE_Core_Sentiment_Analysis/master/dags/core sentiment/includes/__pycache__/analysis.cpython-312.pyc -------------------------------------------------------------------------------- /dags/core sentiment/includes/__pycache__/fetch_page.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chisomnwa/CDE_Core_Sentiment_Analysis/master/dags/core sentiment/includes/__pycache__/fetch_page.cpython-312.pyc -------------------------------------------------------------------------------- /dags/core sentiment/includes/__pycache__/download_file.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chisomnwa/CDE_Core_Sentiment_Analysis/master/dags/core sentiment/includes/__pycache__/download_file.cpython-312.pyc -------------------------------------------------------------------------------- /dags/core sentiment/includes/analysis.py: -------------------------------------------------------------------------------- 1 | def save_highest_pageviews_to_file(company, pageviews): 2 | with open(f'/opt/airflow/dags/CoreSentiment/files/result_file.txt', 'w') as f: 3 | f.write(f"Company with the highest pageviews: {company} with {pageviews} pageviews.\n") -------------------------------------------------------------------------------- /dags/sql_scripts/coresentiment_schema.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO sentiment_tbl VALUES ( 11, 'Amazon', '2'); 2 | INSERT INTO sentiment_tbl VALUES ( 12, 'Apple', '53'); 3 | INSERT INTO sentiment_tbl VALUES ( 13, 'Facebook', '673'); 4 | INSERT INTO sentiment_tbl VALUES ( 14, 'Google', '415'); 5 | INSERT INTO sentiment_tbl VALUES ( 15, 'Microsoft', '109'); 6 | -------------------------------------------------------------------------------- /dags/core sentiment/includes/download_file.py: -------------------------------------------------------------------------------- 1 | import requests 2 | base_path = "/opt/airflow/dags/CoreSentiment/files" 3 | 4 | def download_file(): 5 | url = "https://dumps.wikimedia.org/other/pageviews/2024/2024-10/pageviews-20241012-130000.gz" 6 | file_name = url.split('/')[-1] 7 | local_filename = f'{base_path}/{file_name}' 8 | req = requests.get(url, stream=True) 9 | with open(local_filename, 'wb') as f: 10 | for chunk in req.raw.stream(1024, decode_content=False): 11 | if chunk: 12 | f.write(chunk) -------------------------------------------------------------------------------- /dags/core sentiment/includes/fetch_page.py: -------------------------------------------------------------------------------- 1 | base_path = "/opt/airflow/dags/CoreSentiment/files" 2 | 3 | def fetch_page(): 4 | with open(f'{base_path}/pageviews-20241012-130000') as f: 5 | search_values = ['Facebook', 'Google', 'Apple', 'Amazon', 'Microsoft'] 6 | count = 10 7 | for line in f: 8 | for search_value in search_values: 9 | if line.startswith(f'en.m {search_value} '): 10 | print(f'this is line {line}') 11 | my_val = line.split(' ')[-2] 12 | count = count + 1 13 | with open(f'/opt/airflow/dags/sql/coresentiment_schema.sql', 'a') as query_file: 14 | query = f"INSERT INTO sentiment_tbl VALUES ( {count}, '{search_value}', '{my_val}');\n" 15 | query_file.write(query) 16 | -------------------------------------------------------------------------------- /dags/core sentiment/coresentiment.py: -------------------------------------------------------------------------------- 1 | from airflow import DAG 2 | from airflow.operators.dummy import DummyOperator 3 | from datetime import timedelta 4 | from airflow.utils.dates import days_ago 5 | from airflow.operators.bash import BashOperator 6 | from airflow.operators.python import PythonOperator 7 | from airflow.providers.common.sql.operators.sql import SQLExecuteQueryOperator 8 | from airflow.providers.postgres.operators.postgres import PostgresOperator 9 | from CoreSentiment.includes.fetch_page import fetch_page 10 | from CoreSentiment.includes.download_file import download_file 11 | from CoreSentiment.includes.analysis import save_highest_pageviews_to_file 12 | base_path = "/opt/airflow/dags/CoreSentiment/files" 13 | local_filename = '' 14 | 15 | with DAG( 16 | 'coresentiment', 17 | description='A simple tutorial DAG', 18 | schedule_interval=timedelta(days=1), 19 | start_date= days_ago(1), 20 | tags=['example'], 21 | template_searchpath="/opt/airflow/dags/sql/" 22 | ) as dag: 23 | 24 | download_zip_file = PythonOperator( 25 | task_id="download_zip_file", 26 | python_callable=download_file, 27 | ) 28 | print('downloading zip files', local_filename) 29 | 30 | unzip_file = BashOperator( 31 | task_id="unzip_file", 32 | bash_command=f'gzip -dv /opt/airflow/dags/CoreSentiment/files/pageviews-20241012-130000.gz', 33 | ) 34 | print('extracting files') 35 | 36 | fetch_page_views = PythonOperator( 37 | task_id = 'fetch_page_views', 38 | python_callable = fetch_page 39 | ) 40 | print('fetching page views ans creating SQL file') 41 | 42 | create_table = PostgresOperator( 43 | task_id = 'create_table_new', 44 | postgres_conn_id = 'postgres_default', 45 | sql= "create_table_schema.sql", 46 | ) 47 | 48 | load_data_into_db = PostgresOperator( 49 | task_id="load_data", 50 | postgres_conn_id="postgres_default", 51 | sql="coresentiment_schema.sql", 52 | ) 53 | print('loading data into the database table') 54 | 55 | 56 | download_zip_file >> unzip_file >> fetch_page_views >> create_table >> load_data_into_db 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoreSentiment: Data Pipeline with Apache Airflow 2 | This project showcases a **data pipeline** that orchestrates the data ingestion, processing, storage, and analysis of Wikipedia pageview counts using **Apache Airflow**. The goal is to support **CoreSentiment**, a stock market prediction tool that analyzes Wikipedia pageviews to infer market sentiment. 3 | 4 | ## Project Overview 5 | The hypothesis behind CoreSentiment is that an increase in a company's Wikipedia page views correlates with a positive market sentiment and an expected stock price increase, while a decrease in views suggests negative sentiment. 6 | 7 | In this first iteration, **the data pipeline is designed to**: 8 | 9 | * Download Wikipedia pageview data for one hour on a specific date in October 2024. 10 | 11 | * Focus on five companies: Amazon, Apple, Facebook, Google, and Microsoft. 12 | Extract the relevant pageview counts. 13 | 14 | * Load the processed data into a database. 15 | 16 | * Perform a simple analysis to identify the company with the highest pageviews. 17 | 18 | ## Steps to Reproduce 19 | * **Docker Setup**: Ensure Docker Desktop is running on your machine. 20 | 21 | * **Clone the Repository**: 22 | 23 | `git clone ` 24 | 25 | `cd ` 26 | 27 | * **Start Airflow**: Run the following command to launch the Airflow services: 28 | 29 | `docker-compose up -d` 30 | 31 | * **Access Airflow UI**: Once the services are up, visit the Airflow web interface on your browser (typically accessible at http://localhost:8080). 32 | 33 | * **Run the DAG**: Trigger the DAG from the Airflow UI to execute the data pipeline. 34 | 35 | ## Data Pipeline Details 36 | * **Ingestion**: Wikipedia pageview data for one hour (4pm on October 10th, 2024) is downloaded in a gzip format. 37 | 38 | * **Processing**: The pageview data is extracted and filtered to focus on the five selected companies. 39 | 40 | * **Storage**: The processed data is loaded into a relational database (e.g., Postgres). 41 | 42 | * **Analysis**: A simple SQL query is executed to determine which company had the highest pageviews during the selected hour. 43 | 44 | ## Conclusion 45 | This project is part of a capstone aimed at solidifying knowledge in data pipeline orchestration using **Apache Airflow**. The solution demonstrates how Airflow can be leveraged to automate data workflows, providing a scalable and maintainable system for future iterations of the CoreSentiment tool. 46 | 47 | ## Future Enhancements 48 | Potential improvements could include: 49 | 50 | * Expanding the pipeline to process data for multiple time periods. 51 | 52 | * Enhancing the sentiment analysis by integrating other data sources. 53 | 54 | * Automating stock price predictions based on the pageview trends. 55 | 56 | ## License 57 | This project is licensed under the MIT License. -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | # 18 | 19 | # Basic Airflow cluster configuration for CeleryExecutor with Redis and PostgreSQL. 20 | # 21 | # WARNING: This configuration is for local development. Do not use it in a production deployment. 22 | # 23 | # This configuration supports basic configuration using environment variables or an .env file 24 | # The following variables are supported: 25 | # 26 | # AIRFLOW_IMAGE_NAME - Docker image name used to run Airflow. 27 | # Default: apache/airflow:2.10.1 28 | # AIRFLOW_UID - User ID in Airflow containers 29 | # Default: 50000 30 | # AIRFLOW_PROJ_DIR - Base path to which all the files will be volumed. 31 | # Default: . 32 | # Those configurations are useful mostly in case of standalone testing/running Airflow in test/try-out mode 33 | # 34 | # _AIRFLOW_WWW_USER_USERNAME - Username for the administrator account (if requested). 35 | # Default: airflow 36 | # _AIRFLOW_WWW_USER_PASSWORD - Password for the administrator account (if requested). 37 | # Default: airflow 38 | # _PIP_ADDITIONAL_REQUIREMENTS - Additional PIP requirements to add when starting all containers. 39 | # Use this option ONLY for quick checks. Installing requirements at container 40 | # startup is done EVERY TIME the service is started. 41 | # A better way is to build a custom image or extend the official image 42 | # as described in https://airflow.apache.org/docs/docker-stack/build.html. 43 | # Default: '' 44 | # 45 | # Feel free to modify this file to suit your needs. 46 | --- 47 | x-airflow-common: 48 | &airflow-common 49 | # In order to add custom dependencies or upgrade provider packages you can use your extended image. 50 | # Comment the image line, place your Dockerfile in the directory where you placed the docker-compose.yaml 51 | # and uncomment the "build" line below, Then run `docker-compose build` to build the images. 52 | image: ${AIRFLOW_IMAGE_NAME:-apache/airflow:2.10.1} 53 | # build: . 54 | environment: 55 | &airflow-common-env 56 | AIRFLOW__CORE__EXECUTOR: CeleryExecutor 57 | AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@postgres/airflow 58 | AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@postgres/airflow 59 | AIRFLOW__CELERY__BROKER_URL: redis://:@redis:6379/0 60 | AIRFLOW__CORE__FERNET_KEY: '' 61 | AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: 'true' 62 | AIRFLOW__CORE__LOAD_EXAMPLES: 'true' 63 | AIRFLOW__API__AUTH_BACKENDS: 'airflow.api.auth.backend.basic_auth,airflow.api.auth.backend.session' 64 | # yamllint disable rule:line-length 65 | # Use simple http server on scheduler for health checks 66 | # See https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/logging-monitoring/check-health.html#scheduler-health-check-server 67 | # yamllint enable rule:line-length 68 | AIRFLOW__SCHEDULER__ENABLE_HEALTH_CHECK: 'true' 69 | # WARNING: Use _PIP_ADDITIONAL_REQUIREMENTS option ONLY for a quick checks 70 | # for other purpose (development, test and especially production usage) build/extend Airflow image. 71 | _PIP_ADDITIONAL_REQUIREMENTS: ${_PIP_ADDITIONAL_REQUIREMENTS:-} 72 | # The following line can be used to set a custom config file, stored in the local config folder 73 | # If you want to use it, outcomment it and replace airflow.cfg with the name of your config file 74 | # AIRFLOW_CONFIG: '/opt/airflow/config/airflow.cfg' 75 | AIRFLOW__CORE__TEST_CONNECTION: Enabled 76 | volumes: 77 | - ${AIRFLOW_PROJ_DIR:-.}/dags:/opt/airflow/dags 78 | - ${AIRFLOW_PROJ_DIR:-.}/logs:/opt/airflow/logs 79 | - ${AIRFLOW_PROJ_DIR:-.}/config:/opt/airflow/config 80 | - ${AIRFLOW_PROJ_DIR:-.}/plugins:/opt/airflow/plugins 81 | user: "${AIRFLOW_UID:-50000}:0" 82 | depends_on: 83 | &airflow-common-depends-on 84 | redis: 85 | condition: service_healthy 86 | postgres: 87 | condition: service_healthy 88 | 89 | services: 90 | postgres: 91 | image: postgres:13 92 | environment: 93 | POSTGRES_USER: airflow 94 | POSTGRES_PASSWORD: airflow 95 | POSTGRES_DB: airflow 96 | volumes: 97 | - postgres-db-volume:/var/lib/postgresql/data 98 | healthcheck: 99 | test: ["CMD", "pg_isready", "-U", "airflow"] 100 | interval: 10s 101 | retries: 5 102 | start_period: 5s 103 | restart: always 104 | 105 | redis: 106 | # Redis is limited to 7.2-bookworm due to licencing change 107 | # https://redis.io/blog/redis-adopts-dual-source-available-licensing/ 108 | image: redis:7.2-bookworm 109 | expose: 110 | - 6379 111 | healthcheck: 112 | test: ["CMD", "redis-cli", "ping"] 113 | interval: 10s 114 | timeout: 30s 115 | retries: 50 116 | start_period: 30s 117 | restart: always 118 | 119 | airflow-webserver: 120 | <<: *airflow-common 121 | command: webserver 122 | ports: 123 | - "8080:8080" 124 | healthcheck: 125 | test: ["CMD", "curl", "--fail", "http://localhost:8080/health"] 126 | interval: 30s 127 | timeout: 10s 128 | retries: 5 129 | start_period: 30s 130 | restart: always 131 | depends_on: 132 | <<: *airflow-common-depends-on 133 | airflow-init: 134 | condition: service_completed_successfully 135 | 136 | airflow-scheduler: 137 | <<: *airflow-common 138 | command: scheduler 139 | healthcheck: 140 | test: ["CMD", "curl", "--fail", "http://localhost:8974/health"] 141 | interval: 30s 142 | timeout: 10s 143 | retries: 5 144 | start_period: 30s 145 | restart: always 146 | depends_on: 147 | <<: *airflow-common-depends-on 148 | airflow-init: 149 | condition: service_completed_successfully 150 | 151 | airflow-worker: 152 | <<: *airflow-common 153 | command: celery worker 154 | healthcheck: 155 | # yamllint disable rule:line-length 156 | test: 157 | - "CMD-SHELL" 158 | - 'celery --app airflow.providers.celery.executors.celery_executor.app inspect ping -d "celery@$${HOSTNAME}" || celery --app airflow.executors.celery_executor.app inspect ping -d "celery@$${HOSTNAME}"' 159 | interval: 30s 160 | timeout: 10s 161 | retries: 5 162 | start_period: 30s 163 | environment: 164 | <<: *airflow-common-env 165 | # Required to handle warm shutdown of the celery workers properly 166 | # See https://airflow.apache.org/docs/docker-stack/entrypoint.html#signal-propagation 167 | DUMB_INIT_SETSID: "0" 168 | restart: always 169 | depends_on: 170 | <<: *airflow-common-depends-on 171 | airflow-init: 172 | condition: service_completed_successfully 173 | 174 | airflow-triggerer: 175 | <<: *airflow-common 176 | command: triggerer 177 | healthcheck: 178 | test: ["CMD-SHELL", 'airflow jobs check --job-type TriggererJob --hostname "$${HOSTNAME}"'] 179 | interval: 30s 180 | timeout: 10s 181 | retries: 5 182 | start_period: 30s 183 | restart: always 184 | depends_on: 185 | <<: *airflow-common-depends-on 186 | airflow-init: 187 | condition: service_completed_successfully 188 | 189 | airflow-init: 190 | <<: *airflow-common 191 | entrypoint: /bin/bash 192 | # yamllint disable rule:line-length 193 | command: 194 | - -c 195 | - | 196 | if [[ -z "${AIRFLOW_UID}" ]]; then 197 | echo 198 | echo -e "\033[1;33mWARNING!!!: AIRFLOW_UID not set!\e[0m" 199 | echo "If you are on Linux, you SHOULD follow the instructions below to set " 200 | echo "AIRFLOW_UID environment variable, otherwise files will be owned by root." 201 | echo "For other operating systems you can get rid of the warning with manually created .env file:" 202 | echo " See: https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html#setting-the-right-airflow-user" 203 | echo 204 | fi 205 | one_meg=1048576 206 | mem_available=$$(($$(getconf _PHYS_PAGES) * $$(getconf PAGE_SIZE) / one_meg)) 207 | cpus_available=$$(grep -cE 'cpu[0-9]+' /proc/stat) 208 | disk_available=$$(df / | tail -1 | awk '{print $$4}') 209 | warning_resources="false" 210 | if (( mem_available < 4000 )) ; then 211 | echo 212 | echo -e "\033[1;33mWARNING!!!: Not enough memory available for Docker.\e[0m" 213 | echo "At least 4GB of memory required. You have $$(numfmt --to iec $$((mem_available * one_meg)))" 214 | echo 215 | warning_resources="true" 216 | fi 217 | if (( cpus_available < 2 )); then 218 | echo 219 | echo -e "\033[1;33mWARNING!!!: Not enough CPUS available for Docker.\e[0m" 220 | echo "At least 2 CPUs recommended. You have $${cpus_available}" 221 | echo 222 | warning_resources="true" 223 | fi 224 | if (( disk_available < one_meg * 10 )); then 225 | echo 226 | echo -e "\033[1;33mWARNING!!!: Not enough Disk space available for Docker.\e[0m" 227 | echo "At least 10 GBs recommended. You have $$(numfmt --to iec $$((disk_available * 1024 )))" 228 | echo 229 | warning_resources="true" 230 | fi 231 | if [[ $${warning_resources} == "true" ]]; then 232 | echo 233 | echo -e "\033[1;33mWARNING!!!: You have not enough resources to run Airflow (see above)!\e[0m" 234 | echo "Please follow the instructions to increase amount of resources available:" 235 | echo " https://airflow.apache.org/docs/apache-airflow/stable/howto/docker-compose/index.html#before-you-begin" 236 | echo 237 | fi 238 | mkdir -p /sources/logs /sources/dags /sources/plugins 239 | chown -R "${AIRFLOW_UID}:0" /sources/{logs,dags,plugins} 240 | exec /entrypoint airflow version 241 | # yamllint enable rule:line-length 242 | environment: 243 | <<: *airflow-common-env 244 | _AIRFLOW_DB_MIGRATE: 'true' 245 | _AIRFLOW_WWW_USER_CREATE: 'true' 246 | _AIRFLOW_WWW_USER_USERNAME: ${_AIRFLOW_WWW_USER_USERNAME:-airflow} 247 | _AIRFLOW_WWW_USER_PASSWORD: ${_AIRFLOW_WWW_USER_PASSWORD:-airflow} 248 | _PIP_ADDITIONAL_REQUIREMENTS: '' 249 | user: "0:0" 250 | volumes: 251 | - ${AIRFLOW_PROJ_DIR:-.}:/sources 252 | 253 | airflow-cli: 254 | <<: *airflow-common 255 | profiles: 256 | - debug 257 | environment: 258 | <<: *airflow-common-env 259 | CONNECTION_CHECK_MAX_COUNT: "0" 260 | # Workaround for entrypoint issue. See: https://github.com/apache/airflow/issues/16252 261 | command: 262 | - bash 263 | - -c 264 | - airflow 265 | 266 | # You can enable flower by adding "--profile flower" option e.g. docker-compose --profile flower up 267 | # or by explicitly targeted on the command line e.g. docker-compose up flower. 268 | # See: https://docs.docker.com/compose/profiles/ 269 | flower: 270 | <<: *airflow-common 271 | command: celery flower 272 | profiles: 273 | - flower 274 | ports: 275 | - "5555:5555" 276 | healthcheck: 277 | test: ["CMD", "curl", "--fail", "http://localhost:5555/"] 278 | interval: 30s 279 | timeout: 10s 280 | retries: 5 281 | start_period: 30s 282 | restart: always 283 | depends_on: 284 | <<: *airflow-common-depends-on 285 | airflow-init: 286 | condition: service_completed_successfully 287 | 288 | volumes: 289 | postgres-db-volume: 290 | --------------------------------------------------------------------------------