{#
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership.  The ASF licenses this file
 to you under the Apache License, Version 2.0 (the
 "License"); you may not use this file except in compliance
 with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on an
 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
#}

{% extends base_template %}
{% from 'appbuilder/loading_dots.html' import loading_dots %}
{% from 'airflow/dataset_next_run_modal.html' import dataset_next_run_modal %}
{% from 'airflow/_messages.html' import show_message %}

{%- macro sortable_column(display_name, attribute_name) -%}
  {% set curr_ordering_direction = (request.args.get('sorting_direction', 'desc')) %}
  {% set new_ordering_direction = ('asc' if (request.args.get('sorting_key') != attribute_name or curr_ordering_direction == 'desc') else 'desc') %}
  <a href="{{ url_for('Airflow.index',
                       status=request.args.get('status', 'all'),
                       search=request.args.get('search', None),
                       tags=request.args.get('tags', None),
                       sorting_key=attribute_name,
                       sorting_direction=new_ordering_direction
                       ) }}"
     class="js-tooltip"
     role="tooltip"
     title="Sort by {{ new_ordering_direction }} {{ attribute_name }}."
  >
    {{ display_name }}

    <span class="material-icons" aria-hidden="true" aria-describedby="sorting-tip-{{ display_name }}">
      {% if curr_ordering_direction == 'desc' and request.args.get('sorting_key') == attribute_name %}
        expand_more
      {% elif curr_ordering_direction == 'asc' and request.args.get('sorting_key') == attribute_name %}
        expand_less
      {% else %}
        unfold_more
      {% endif %}
    </span>
  </a>
{%- endmacro -%}

{% block page_title %}
  {% if search_query %}"{{ search_query }}" - {% endif %}DAGs - {{ appbuilder.app_name }}
{% endblock %}

{% block head_meta %}
  {{ super() }}
  <meta name="dags_index" content="{{ url_for('Airflow.index') }}">
  <meta name="paused_url" content="{{ url_for('Airflow.paused') }}">
  <meta name="status_filter" content="{{ status_filter }}">
  <meta name="autocomplete_url" content="{{ url_for('AutocompleteView.autocomplete') }}">
  <meta name="graph_url" content="{{ url_for('Airflow.legacy_graph') }}">
  <meta name="dag_run_url" content="{{ url_for('DagRunModelView.list') }}">
  <meta name="task_instance_url" content="{{ url_for('TaskInstanceModelView.list') }}">
  <meta name="blocked_url" content="{{ url_for('Airflow.blocked') }}">
  <meta name="csrf_token" content="{{ csrf_token() }}">
  <meta name="last_dag_runs_url" content="{{ url_for('Airflow.last_dagruns') }}">
  <meta name="dag_stats_url" content="{{ url_for('Airflow.dag_stats') }}">
  <meta name="task_stats_url" content="{{ url_for('Airflow.task_stats') }}">
  <meta name="grid_url" content="{{ url_for('Airflow.grid', dag_id='__DAG_ID__') }}">
  <meta name="datasets_url" content="{{ url_for('Airflow.datasets') }}">
  <meta name="next_run_datasets_summary_url" content="{{ url_for('Airflow.next_run_datasets_summary') }}">
  <meta name="next_run_datasets_url" content="{{ url_for('Airflow.next_run_datasets', dag_id='__DAG_ID__') }}">
{% endblock %}

{% block head_css %}
  {{ super() }}
  <link rel="stylesheet" type="text/css" href="{{ url_for_asset('switch.css') }}">
  <link rel="stylesheet" type="text/css" href="{{ url_for_asset('dags.css') }}">
{% endblock %}

{% block messages %}
  {% for m in dashboard_alerts %}
    {{ show_message(m.message, m.category) }}
  {% endfor %}
  {% if migration_moved_data_alerts %}
    {% call show_message(category='warning', dismissible=false) %}
      While upgrading the metadatabase, Airflow had to move some bad data in order to apply new constraints.
      The moved data can be found in the following tables:<br>
        <table>
          <tr>
            <th style="padding-right:10px">Source table</th>
            <th>Table with moved rows</th>
          </tr>
          {% for original_table_name, moved_table_name in migration_moved_data_alerts %}
            <tr>
              <td style="padding-right:10px"><code>{{ original_table_name }}</code></td>
              <td><code>{{ moved_table_name }}</code></td>
            </tr>
          {% endfor %}
        </table>
      Please inspect the moved data to decide whether you need to keep them, and manually drop
      the moved tables to dismiss this warning. Read more about it
      in <a href={{ get_docs_url("installation/upgrading.html") }}><b>Upgrading</b></a>.
    {% endcall %}
  {% endif %}
  {{ super() }}
  {% if sqlite_warning | default(true) %}
    {% call show_message(category='warning', dismissible=false) %}
      Do not use <b>SQLite</b> as metadata DB in production &#8211; it should only be used for dev/testing.
      We recommend using Postgres or MySQL.
      <a href={{ get_docs_url("howto/set-up-database.html") }}><b>Click here</b></a> for more information.
    {% endcall %}
  {% endif %}
  {% if production_executor_warning | default(false) %}
    {% call show_message(category='warning', dismissible=false) %}
      Do not use the <b>{{ production_executor_warning }}</b> in production.
      <a href={{ get_docs_url("executor/index.html") }}><b>Click here</b></a> for more information.
    {% endcall %}
  {% endif %}
  {% if otel_on | default(false) %}
    {% call show_message(category='warning', dismissible=false) %}
      OpenTelemetry support is experimental. <a href="https://github.com/apache/airflow/blob/main/BREEZE.rst#running-breeze-with-an-opentelemetry-metrics-stack">
        <b>Click here</b></a> for more information.
    {% endcall %}
  {% endif %}
{% endblock %}

{% block content %}
  <h2>{{ page_title }}</h2>
  <div id="main_content">
    <div class="row dags-table-header">
      <div class="col-sm-4 col-md-4 no-x-padding">
        <div class="form-group btn-group">
          <a
            href="{{ url_for('Airflow.index', status='all', search=request.args.get('search', None), tags=request.args.getlist('tags', None)) }}"
            class="btn {{ 'btn-primary' if status_filter == 'all' else 'btn-default' }}"
            title="Show active and paused DAGs">All <span class="badge">{{ "{:,}".format(status_count_all) }}</span></a>
          <a
            href="{{ url_for('Airflow.index', status='active', search=request.args.get('search', None), tags=request.args.getlist('tags', None)) }}"
            class="btn {{ 'btn-primary' if status_filter == 'active' else 'btn-default' }}"
            title="Show only active DAGs">Active <span class="badge">{{ "{:,}".format(status_count_active) }}</span></a>
          <a
            href="{{ url_for('Airflow.index', status='paused', search=request.args.get('search', None), tags=request.args.getlist('tags', None)) }}"
            class="btn {{ 'btn-primary' if status_filter == 'paused' else 'btn-default' }}"
            title="Show only paused DAGs">Paused <span class="badge">{{ "{:,}".format(status_count_paused) }}</span></a>
        </div>
      </div>
      <div class="col-sm-2 col-md-2">
        <form id="tags_form" style="width: 100%; text-align: left;">
          <div class="form-group search-input" style="width:100%;">
            <select multiple name="tags" id="tags_filter" class="select2-drop-mask" style="width: 100%;">
              {% for tag in tags %}
                <option value="{{ tag.name }}" {% if tag.selected %}selected{% endif %}>{{ tag.name }}</option>
              {% endfor %}
            </select>
            {% if tags_filter|length > 0 %}
              <button type="reset" aria-label="Clear all tags"
                      class="btn btn-default btn-sm material-icons search-input__clear-btn">cancel
              </button>
            {% endif %}
          </div>
        </form>
      </div>
      <div class="col-sm-2 col-md-2 col-md-offset-1 no-x-padding">
        <form id="search_form">
          <div class="form-group search-input" style="width: 100%;">
            <label for="dag_query" class="sr-only">Search DAGs</label>
            <input type="search" id="dag_query" class="typeahead form-control search-input__input"
                   data-provide="typeahead" style="width:100%;" value="{{ search_query }}" autocomplete="off"
                   placeholder="Search DAGs">
            {% if search_query %}
              <button type="reset" aria-label="Clear DAG Search Term"
                      class="btn btn-default btn-sm material-icons search-input__clear-btn">cancel
              </button>
            {% endif %}
          </div>
        </form>
      </div>
      <div class="refresh-actions col-sm-2 col-md-2 no-x-padding">
        {{ loading_dots(id='loading-dots', classes='refresh-loading') }}
        <label class="switch-label">
          <input class="switch-input" id="auto_refresh" type="checkbox">
          <span class="switch" aria-hidden="true"></span>
          Auto-refresh
        </label>
        <button class="btn btn-default btn-sm" id="refresh_button">
          <span class="material-icons" aria-hidden="true">refresh</span>
        </button>
      </div>
    </div>
  </div>
  <div class="dags-table-wrap">
    <div class="dags-table-body">
      <table class="table table-striped table-bordered table-hover">
        <thead>
        <tr>
          <th width="12">
            <span class="material-icons text-muted js-tooltip"
                  title="Use this toggle to pause/unpause a DAG. The scheduler won't schedule new tasks instances for a paused DAG. Tasks already running at pause time won't be affected.">info</span>
          </th>
          <th>{{ sortable_column("DAG", "dag_id") }}</th>
          <th>{{ sortable_column("Owner", "owners") }}</th>
          <th>Runs
            <span class="material-icons text-muted js-tooltip" aria-hidden="true"
                  title="Status of all previous DAG runs.">info</span>
          </th>
          <th>Schedule</th>
          <th style="width:180px;">Last Run
            <span class="material-icons text-muted js-tooltip" aria-hidden="true"
                  title="Date/Time of the latest Dag Run.">info</span>
          </th>
          <th style="width:180px;">{{ sortable_column("Next Run", "next_dagrun") }}
            <span class="material-icons text-muted js-tooltip" aria-hidden="true"
                  title="Expected date/time of the next DAG Run, or for dataset triggered DAGs, how many datasets have been updated since the last DAG Run.">info</span>
          </th>
          <th>Recent Tasks
            <span class="material-icons text-muted js-tooltip" aria-hidden="true"
                  title="Status of tasks from all active DAG runs or, if not currently active, from most recent run.">info</span>
          </th>
          <th class="text-center" style="width:110px;">Actions</th>
          <th style="width:52px;">Links</th>
        </tr>
        </thead>
        <tbody>
        {% if dags|length == 0 %}
          <tr>
            <td colspan="10">No results</td>
          </tr>
        {% endif %}
        {% for dag in dags %}
          {% set dag_is_paused = dag.get_is_paused() %}
          <tr>
            <td style="padding-right:0;">
              {% if dag.can_edit %}
                {% set switch_tooltip = 'Pause/Unpause DAG' %}
              {% else %}
                {% set switch_tooltip = 'DAG is Paused' if dag_is_paused else 'DAG is Active' %}
              {% endif %}
              <label class="switch-label{{ ' disabled' if not dag.can_edit else '' }} js-tooltip"
                     title="{{ switch_tooltip }}">
                <input class="switch-input" id="toggle-{{ dag.dag_id }}" data-dag-id="{{ dag.dag_id }}" type="checkbox"
                  {{ " checked" if not dag_is_paused else "" }}
                  {{ " disabled" if not dag.can_edit else "" }}>
                <span class="switch" aria-hidden="true"></span>
              </label>
            </td>
            <td>
              <a href="{{ url_for('Airflow.'+ dag.get_default_view(), dag_id=dag.dag_id) }}"
                 title="{{ dag.description[0:80] + '…' if dag.description and dag.description|length > 80 else dag.description|default('', true) }}">
                <strong>{{ dag.dag_id }}</strong>
              </a>
              <div>
                {% for tag in dag.tags | sort(attribute='name') %}
                  <a class="label label-info"
                     href="?tags={{ tag.name }}"
                     style="margin: 6px 6px 0 0;">
                    {{ tag.name }}
                  </a>
                {% endfor %}
              </div>
            </td>
            <td>
              {% for owner in dag.owners.split(",") %}
                <a class="label label-default"
                 {% if owner_links and owner.strip() in owner_links.get(dag.dag_id, {}) %}
                   href="{{ owner_links[dag.dag_id].get(owner.strip()) }}" target="_blank"
                 {% else %}
                   href="?search={{ owner | trim }}"
                 {% endif %}
                   style="margin: 3px 6px 3px 0;">
                  {{ owner | trim }}
                </a>
              {% endfor %}
            </td>
            <td style="padding:0; width:130px;">
              {{ loading_dots(classes='js-loading-dag-run-stats text-muted') }}
              <svg height="10" width="10" id="dag-run-{{ dag.safe_dag_id }}" style="display: block;"></svg>
            </td>
            <td>
              <a class="label label-default schedule"
                 href="{{ url_for('DagRunModelView.list') }}?_flt_3_dag_id={{ dag.dag_id }}"
                 data-dag-id="{{ dag.dag_id }}">
                {{ dag.schedule_interval }}
              </a>
              {% if dag is defined and dag.timetable_description %}
                <span class="material-icons text-muted js-tooltip" aria-hidden="true"
                      data-original-title="Schedule: {{ dag.timetable_description|string }}">info</span>
              {% endif %}
            </td>
            <td id="last-run-{{ dag.safe_dag_id }}" class="text-nowrap latest_dag_run">
              {{ loading_dots(classes='js-loading-last-run text-muted') }}
              <a></a>
              <span
                aria-hidden="true"
                title=""
                class="material-icons text-muted js-tooltip js-last-run-tooltip"
                data-html="true"
                style="display:none"
              >
                    info
                  </span>
            </td>
            <td class="text-nowrap {{ 'js-dataset-triggered' if dag.dag_id in dataset_triggered_next_run_info }}">
              {% if dag.dag_id in dataset_triggered_next_run_info %}
                {%- with ds_info = dataset_triggered_next_run_info[dag.dag_id] -%}
                  <span
                    data-dag-id="{{ dag.dag_id }}"
                    class="js-tooltip js-next-dataset-run-tooltip"
                    title="Click to see dataset details."
                    data-html="true"
                    data-uri="{{ ds_info.uri }}"
                  >
                    <div
                      class="label label-default next-dataset-triggered"
                      data-dag-id="{{ dag.dag_id }}"
                      data-uri="{{ ds_info.uri }}"
                      data-summary="
                      {%- if ds_info.total == 1 -%}
                      On {{ ds_info.uri }}
                      {%- else -%}
                      {{ ds_info.ready }} of {{ ds_info.total }} datasets updated
                      {%- endif -%}"
                    >
                      {% if ds_info.total == 1 -%}
                        On {{ ds_info.uri[0:40] + '…' if ds_info.uri and ds_info.uri|length > 40 else ds_info.uri|default('', true) }}
                      {%- else -%}
                        {{ ds_info.ready }} of {{ ds_info.total }} datasets updated
                      {%- endif %}
                    </div>
                  </span>
                {%- endwith -%}
              {% endif %}
              {% if dag.next_dagrun is not none %}
                <time datetime="{{ dag.next_dagrun }}">{{ dag.next_dagrun }}</time>
              {% endif %}
              {% if dag.next_dagrun_create_after %}
                {# data-nextrun is being used to pass next_dagrun dates to js to build the full tooltip #}
                <span
                  aria-hidden="true"
                  data-nextrun="{{ dag.next_dagrun_create_after }},{{ dag.next_dagrun_data_interval_start }},{{ dag.next_dagrun_data_interval_end }}"
                  title=""
                  class="material-icons text-muted js-tooltip js-next-run-tooltip"
                  data-html="true"
                >
                      info
                    </span>
              {% endif %}
            </td>
            <td style="padding:0; width:323px; height:10px;">
              {{ loading_dots(classes='js-loading-task-instance-stats text-muted') }}
              <svg height="10" width="10" id='task-instance-{{ dag.safe_dag_id }}' style="display: block;"></svg>
            </td>
            <td class="text-center">
              <div class="btn-group">
                {% if dag %}
                  <div class="dropdown">
                    <a aria-label="Trigger DAG"
                       class="btn btn-sm btn-default btn-icon-only{{ ' disabled' if not dag.can_trigger }} trigger-dropdown-btn"
                       data-toggle="dropdown">
                      <span class="material-icons" aria-hidden="true">play_arrow</span>
                    </a>
                    <ul class="dropdown-menu trigger-dropdown-menu">
                      <li>
                        <form method="POST" action="{{ url_for('Airflow.trigger') }}">
                          <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
                          <input type="hidden" name="dag_id" value="{{ dag.dag_id }}">
                          <input type="hidden" name="unpause" value="True">
                          <button type="submit" class="dropdown-form-btn">Trigger DAG</button>
                        </form>
                      </li>
                      <li><a href="{{ url_for('Airflow.trigger', dag_id=dag.dag_id) }}">Trigger DAG w/ config</a></li>
                    </ul>
                  </div>
                {% endif %}
                {# Use dag_id instead of dag.dag_id, because the DAG might not exist in the webserver's DagBag #}
                <a href="{{ url_for('Airflow.delete', dag_id=dag.dag_id, redirect_url=url_for(request.endpoint)) }}"
                   onclick="return confirmDeleteDag(this, '{{ dag.dag_id }}')" title="Delete&nbsp;DAG"
                   aria-label="Delete DAG"
                   class="btn btn-sm btn-default btn-icon-only {{ ' disabled' if not dag.can_delete }}">
                  <span class="material-icons text-danger" aria-hidden="true">delete_outline</span>
                </a>
              </div>
            </td>
            <td class="dags-table-more">
              {% if dag %}
                <div class="dags-table-more__menu">
                  <div class="dags-table-more__links">
                    <a href="{{ url_for('Airflow.code', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">code</span>
                      Code
                    </a>
                    <a href="{{ url_for('Airflow.dag_details', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">details</span>
                      Details
                    </a>
                    <a href="{{ url_for('Airflow.gantt', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">vertical_distribute</span>
                      Gantt
                    </a>
                    <a href="{{ url_for('Airflow.landing_times', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">flight_land</span>
                      Landing
                    </a>
                    <a href="{{ url_for('Airflow.tries', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">repeat</span>
                      Tries
                    </a>
                    <a href="{{ url_for('Airflow.duration', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">hourglass_bottom</span>
                      Duration
                    </a>
                    <a href="{{ url_for('Airflow.calendar', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">event</span>
                      Calendar
                    </a>
                    <a href="{{ url_for('Airflow.graph', dag_id=dag.dag_id) }}" class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">account_tree</span>
                      Graph
                    </a>
                    <a href="{{ url_for('Airflow.grid', dag_id=dag.dag_id, num_runs=num_runs) }}"
                       class="dags-table-more__link">
                      <span class="material-icons" aria-hidden="true">grid_on</span>
                      Grid
                    </a>
                  </div>
                  <span class="dags-table-more__toggle"><span class="material-icons">more_horiz</span></span>
                </div>
              {% endif %}
            </td>
          </tr>
        {% endfor %}
        </tbody>
      </table>
    </div>
  </div>
  <div class="row">
    <div class="col-sm-6">
      {{ paging }}
    </div>
    <div class="col-sm-6 text-right">
      Showing <strong>{{ num_dag_from }}-{{ num_dag_to }}</strong> of <strong>{{ num_of_all_dags }}</strong> DAGs
    </div>
  </div>
  </div>
  <div id="svg-tooltip" class="tooltip top" style="position: fixed; display: none; opacity: 1; pointer-events: none;">
    <div class="tooltip-arrow"></div>
    <div class="tooltip-inner"></div>
  </div>
  <!-- Modal for dataset-triggered next run -->
  {{ dataset_next_run_modal(id='dataset-next-run-modal') }}
{% endblock %}

{% block tail %}
  {{ super() }}
  <script src="{{ url_for_asset('d3.min.js') }}"></script>
  <script src="{{ url_for_asset('dags.js') }}"></script>
  <script>
    const STATE_COLOR = {{ state_color|tojson }};
    const autoRefreshInterval = {{ auto_refresh_interval }};

    // Tests rely on confirmDeleteDag to be in the html
    function confirmDeleteDag(link, dagId) {
      if (confirm(`Are you sure you want to delete '${dagId}' now?\n\
        This option will delete ALL metadata, DAG runs, etc.\n\
        EXCEPT Log.\n\
        This cannot be undone.`)) {
        postAsForm(link.href, {});
      }
      return false;
    }
  </script>
{% endblock %}
