{#
 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 "airflow/master.html" %}

{% block title %}Airflow - DAGs{% endblock %}

{% block head_css %}
{{ super() }}
<link href="{{ url_for("static", filename="dataTables.bootstrap.css") }}" rel="stylesheet" type="text/css" >
<link href="{{ url_for("static", filename="bootstrap-toggle.min.css") }}" rel="stylesheet" type="text/css">
{% endblock %}

{% block body %}
  <h2>DAGs</h2>

  <div id="main_content">
    <div class="row">
      <div class="col-sm-2">
      </div>
      <div class="col-sm-10">
        <form id="search_form" class="form-inline" style="width: 100%; text-align: right;">
            <div id="dags_filter" class="form-group" style="width: 100%;">
              <label for="dag_query" style="width:20%; text-align: right;">Search:</label>
              <input id="dag_query" type="text" class="typeahead form-control" data-provide="typeahead" style="width:50%;" value="{{search_query}}">
            </div>
        </form>
      </div>
    </div>
    <table id="dags" class="table table-striped table-bordered">
        <thead>
            <tr>
                <th></th>
                <th width="12"><span id="pause_header" class="glyphicon glyphicon-info-sign" title="Use this toggle to pause a DAG. The scheduler won't schedule new tasks instances for a paused DAG. Tasks already running at pause time won't be affected."></span></th>
                <th>DAG</th>
                <th>Schedule</th>
                <th>Owner</th>
                <th style="padding-left: 5px;">Recent Tasks
                  <span id="statuses_info" class="glyphicon glyphicon-info-sign" aria-hidden="true" title="Status of tasks from all active DAG runs or, if not currently active, from most recent run."></span>
                  <img id="loading" width="15" src="{{ url_for("static", filename="loading.gif") }}">
                </th>
                <th style="padding-left: 5px;">Last Run <span id="statuses_info" class="glyphicon glyphicon-info-sign" aria-hidden="true" title="Execution Date/Time of Highest Dag Run."></span>
                </th>
                <th style="padding-left: 5px;">DAG Runs
                  <span id="statuses_info" class="glyphicon glyphicon-info-sign" aria-hidden="true" title="Status of all previous DAG runs."></span>
                  <img id="loading" width="15" src="{{ url_for("static", filename="loading.gif") }}">
                </th>
                <th class="text-center">Links</th>
            </tr>
        </thead>
        <tbody>
        {% for dag in dags %}
            <tr>
                <!-- Column 1: Edit dag -->
                <td class="text-center" style="width:10px;">
                  <a href="{{ url_for('dagmodel.edit_view') }}?id={{ dag.dag_id }}" title="Info">
                        <span class="glyphicon glyphicon-edit" aria-hidden="true"></span>
                    </a>
                </td>

                <!-- Column 2: Turn dag on/off -->
                <td>
                  <input id="toggle-{{ dag.dag_id }}" dag_id="{{ dag.dag_id }}" type="checkbox" {{ "checked" if not dag.is_paused else "" }} data-toggle="toggle" data-size="mini" method="post">
                </td>

                <!-- Column 3: Name -->
                <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) }}">
                          {{ dag.dag_id }}
                  </a>
                </td>

                <!-- Column 4: Dag Schedule -->
                <td>
                  <a class="label label-default schedule {{ dag.dag_id }}" href="{{ url_for('dagrun.index_view') }}?flt2_dag_id_equals={{ dag.dag_id }}">
                        {{ dag.schedule_interval | string }}
                    </a>
                </td>

                <!-- Column 5: Dag Owners -->
                <td>
                  {{ dag.owners }}
                </td>

                <!-- Column 6: Recent Tasks -->
                <td style="padding:0px; width:200px; height:10px;">
                    <svg height="10" width="10" id='task-run-{{ dag.safe_dag_id }}' style="display: block;"></svg>
                </td>

                <!-- Column 7: Last Run -->
                <td class="text-nowrap latest_dag_run {{ dag.dag_id }}">
                  {% set last_run = dag.get_last_dagrun(include_externally_triggered=True) %}
                  {% if last_run and last_run.execution_date %}
                    <a href="{{ url_for('airflow.graph', dag_id=dag.dag_id, execution_date=last_run.execution_date) }}">
                      {{ last_run.execution_date.strftime("%Y-%m-%d %H:%M") }}
                    </a>
                    <span aria-hidden="true" id="statuses_info" title="Start Date: {{ last_run.start_date.strftime("%Y-%m-%d %H:%M") }}" class="glyphicon glyphicon-info-sign"></span>
                  {% endif %}
                </td>

                <!-- Column 8: Dag Runs -->
                <td style="padding:0px; width:120px; height:10px;">
                    <svg height="10" width="10" id='dag-run-{{ dag.safe_dag_id }}' style="display: block;"></svg>
                </td>

                <!-- Column 9: Links -->
                <td class="text-center" style="display:flex; flex-direction:row; justify-content:space-around;">
                {% if dag %}

                <!-- Trigger Dag -->
                <a href="{{ url_for('airflow.trigger', dag_id=dag.dag_id) }}&origin={{ request.base_url }}">
                    <span class="glyphicon glyphicon-play-circle" aria-hidden="true" data-original-title="Trigger Dag"></span>
                </a>

                <!-- Tree -->
                <a href="{{ url_for('airflow.tree', dag_id=dag.dag_id, num_runs=num_runs) }}">
                    <span class="glyphicon glyphicon-tree-deciduous" aria-hidden="true" data-original-title="Tree View"></span>
                </a>

                <!-- Graph -->
                <a href="{{ url_for('airflow.graph', dag_id=dag.dag_id) }}">
                    <span class="glyphicon glyphicon-certificate" aria-hidden="true" data-original-title="Graph View"></span>
                </a>

                <!-- Duration -->
                <a href="{{ url_for('airflow.duration', dag_id=dag.dag_id) }}">
                    <span class="glyphicon glyphicon-stats" aria-hidden="true" data-original-title="Tasks Duration"></span>
                </a>

                <!-- Retries -->
                <a href="{{ url_for('airflow.tries', dag_id=dag.dag_id) }}">
                    <span class="glyphicon glyphicon-duplicate" aria-hidden="true" data-original-title="Task Tries"></span>
                </a>

                <!-- Landing Times -->
                <a href="{{ url_for("airflow.landing_times", dag_id=dag.dag_id) }}">
                    <span class="glyphicon glyphicon-plane" aria-hidden="true" data-original-title="Landing Times"></span>
                </a>

                <!-- Gantt -->
                <a href="{{ url_for("airflow.gantt", dag_id=dag.dag_id) }}">
                    <span class="glyphicon glyphicon-align-left" aria-hidden="true" data-original-title="Gantt View"></span>
                </a>

                <!-- Code -->
                <a href="{{ url_for("airflow.code", dag_id=dag.dag_id) }}">
                    <span class="glyphicon glyphicon-file" aria-hidden="true" data-original-title="Code View"></span>
                </a>

                <!-- Logs -->
                <a href="{{ url_for('log.index_view') }}?sort=1&amp;desc=1&amp;flt1_dag_id_equals={{ dag.dag_id }}">
                    <span class="glyphicon glyphicon-align-justify" aria-hidden="true" data-original-title="Logs"></span>
                </a>
                {% endif %}

                <!-- Refresh -->
                <a href="{{ url_for("airflow.refresh", dag_id=dag.dag_id) }}" onclick="postAsForm(this.href); return false">
                  <span class="glyphicon glyphicon-refresh" aria-hidden="true" data-original-title="Refresh"></span>
                </a>

                <!-- Delete -->
                <!-- 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) }}"
                  onclick="return confirmDeleteDag(this, '{{ dag.dag_id }}')">
                   <span class="glyphicon glyphicon-remove-circle" style="color:red" aria-hidden="true" data-original-title="Delete Dag"></span>
                </a>
                </td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
    <div class="row">
      <div class="col-sm-12" style="text-align:right;">
        <div class="dataTables_info" id="dags_info" role="status" aria-live="polite" style="padding-top: 0px;">Showing {{num_dag_from}} to {{num_dag_to}} of {{num_of_all_dags}} entries</div>
      </div>
    </div>
    <div class="row">
      <div class="col-sm-12" style="text-align:left;">
        <div class="dataTables_info" id="dags_paginate">
          {{paging}}
        </div>
      </div>

    </div>
    {% if not hide_paused %}
    <a href="{{ url_for('admin.index') }}?showPaused=False">Hide Paused DAGs</a>
    {% else %}
    <a href="{{ url_for('admin.index') }}?showPaused=True">Show Paused DAGs</a>
    {% endif %}
  </div>
{% endblock %}

{% block tail %}
  {{ super() }}
  <script src="{{ url_for('static', filename='d3.v3.min.js') }}"></script>
  <script src="{{ url_for('static', filename='jquery.dataTables.min.js') }}"></script>
  <script src="{{ url_for('static', filename='bootstrap-toggle.min.js') }}"></script>
  <script src="{{ url_for('static', filename='bootstrap3-typeahead.min.js') }}"></script>
  <script>

      const DAGS_INDEX = "{{ url_for('admin.index') }}";
      const ENTER_KEY_CODE = 13;
      const STATE_COLOR = {{ state_color|tojson }};

      $('#dag_query').on('keypress', function (e) {
        // check for key press on ENTER (key code 13) to trigger the search
        if (e.which === ENTER_KEY_CODE) {
          search_query = $('#dag_query').val();
          window.location = DAGS_INDEX + "?search="+ encodeURI(search_query);
          e.preventDefault();
        }
      });

      $('#page_size').on('change', function() {
        p_size = $(this).val();
        window.location = DAGS_INDEX + "?page_size=" + p_size;
      });

      function confirmTriggerDag(link, dag_id){
        if (confirm("Are you sure you want to run '"+dag_id+"' now?")) {
          postAsForm(link.href, {});
        }
        // Never follow the link
        return false;
      }

      function confirmDeleteDag(link, dag_id){
        if (confirm("Are you sure you want to delete '"+dag_id+"' now?\n\
          This option will delete ALL metadata, DAG runs, etc.\n\
          EXCEPT Log.\n\
          This cannot be undone.")) {
          postAsForm(link.href, {});
        }
        return false;
      }

      var encoded_dag_ids = [];

      $.each($("[id^=toggle]"), function(i, v) {
        var dag_id = $(v).attr('dag_id');
        encoded_dag_ids.push(encodeURIComponent(dag_id));

        $(v).change (function() {
          if ($(v).prop('checked')) {
            is_paused = 'true'
          } else {
            is_paused = 'false'
          }
          url = {{ url_for('airflow.paused') | tojson }} + '?is_paused=' + is_paused + '&dag_id=' + encodeURIComponent(dag_id);
          $.post(url);
        });
      });

      var $input = $(".typeahead");
      unique_options_search = new Set([
          {% for token in auto_complete_data %}
            "{{token}}",
          {% endfor %}
        ]);

      $input.typeahead({
        source: [...unique_options_search],
        autoSelect: false,
        afterSelect: function(value) {
          search_query = value.trim()
          if (search_query) {
            window.location = DAGS_INDEX + "?search="+ encodeURI(search_query);
          }
        }
      });

      $input.change(function() {
        var current = $input.typeahead("getActive");
      });

      $input.attr("autocomplete", "off");

      $('#dags').dataTable({
        "iDisplayLength": 500,
        "bSort": false,
        "searching": false,
        "ordering": false,
        "paging": false,
        "info": false
      });
      $("#main_content").show(250);
      diameter = 25;
      circle_margin = 4;
      stroke_width = 2;
      stroke_width_hover = 6;
      d3.json("{{ url_for('airflow.blocked') }}", function(error, json) {
        $.each(json, function() {
          $('.label.schedule.' + this.dag_id)
          .attr('title', this.active_dag_run + '/' + this.max_active_runs + ' active dag runs')
          .tooltip();
          if(this.active_dag_run >= this.max_active_runs) {
            $('.label.schedule.' + this.dag_id)
            .css('background-color', 'red');
          }
        });
      });

      function drawDagStatsForDag(dag_id, stats) {
        g = d3.select('svg#dag-run-' + dag_id.replace(/\./g, '__dot__'))
          .attr('height', diameter + (stroke_width_hover * 2))
          .attr('width', '110px')
          .selectAll("g")
          .data(states)
          .enter()
          .append('g')
          .attr('transform', function(d, i) {
            x = (i * (diameter + circle_margin)) + (diameter/2 + circle_margin);
            y = (diameter/2) + stroke_width_hover;
            return 'translate(' + x + ',' + y + ')';
          });

        g.append('text')
          .attr('fill', 'black')
          .attr('text-anchor', 'middle')
          .attr('vertical-align', 'middle')
          .attr('font-size', 8)
          .attr('y', 3)
          .text(function(d){ return d.count > 0 ? d.count : ''; });

        g.append('circle')
          .attr('stroke-width', function(d) {
              if (d.count > 0)
                return stroke_width;
              else {
                return 1;
              }
          })
          .attr('stroke', function(d) {
              if (d.count > 0)
                return STATE_COLOR[d.state];
              else {
                return 'gainsboro';
              }
          })
          .attr('fill-opacity', 0)
          .attr('r', diameter/2)
          .attr('title', function(d) {return d.state || 'none'})
          .attr('style', function(d) {
            if (d.count > 0)
                return"cursor:pointer;"
          })
          .on('click', function(d, i) {
              if (d.count > 0)
                window.location = "{{ url_for('dagrun.index_view') }}?flt1_dag_id_equals=" + dag_id + "&flt2_state_equals=" + d.state;
          })
          .on('mouseover', function(d, i) {
            if (d.count > 0) {
                d3.select(this).transition().duration(400)
                  .attr('fill-opacity', 0.3)
                  .style("stroke-width", stroke_width_hover);
            }
          })
          .on('mouseout', function(d, i) {
            if (d.count > 0) {
                d3.select(this).transition().duration(400)
                  .attr('fill-opacity', 0)
                  .style("stroke-width", stroke_width);
            }
          })
          .style("opacity", 0)
          .transition()
          .duration(500)
          .delay(function(d, i){return i*50;})
          .style("opacity", 1);
        d3.select("#loading").remove();
      }

      d3.json("{{ url_for('airflow.dag_stats') }}", function(error, json) {
        for(var dag_id in json) {
            states = json[dag_id];
            drawDagStatsForDag(dag_id, states);
        }
        $("#pause_header").tooltip();
        $("#statuses_info").tooltip();

        $("circle").tooltip({
          html: true,
          container: "body",
        });
      });

      function drawTaskStatsForDag(dag_id, states) {
        g = d3.select('svg#task-run-' + dag_id.replace(/\./g, '__dot__'))
          .attr('height', diameter + (stroke_width_hover * 2))
          .attr('width', '300px')
          .selectAll("g")
          .data(states)
          .enter()
          .append('g')
          .attr('transform', function(d, i) {
            x = (i * (diameter + circle_margin)) + (diameter/2 + circle_margin);
            y = (diameter/2) + stroke_width_hover;
            return 'translate(' + x + ',' + y + ')';
          });

        g.append('text')
          .attr('fill', 'black')
          .attr('text-anchor', 'middle')
          .attr('vertical-align', 'middle')
          .attr('font-size', 8)
          .attr('y', 3)
          .text(function(d){ return d.count > 0 ? d.count : ''; });

        g.append('circle')
          .attr('stroke-width', function(d) {
              if (d.count > 0)
                return stroke_width;
              else {
                return 1;
              }
          })
          .attr('stroke', function(d) {
              if (d.count > 0)
                return STATE_COLOR[d.state];
              else {
                return 'gainsboro';
              }
          })
          .attr('fill-opacity', 0)
          .attr('r', diameter/2)
          .attr('title', function(d) {return d.state})
          .attr('style', function(d) {
            if (d.count > 0)
                return"cursor:pointer;"
          })
          .on('click', function(d, i) {
              if (d.count > 0)
                window.location = "{{ url_for('taskinstance.index_view') }}?flt1_dag_id_equals=" + dag_id + "&flt2_state_equals=" + d.state;
          })
          .on('mouseover', function(d, i) {
            if (d.count > 0) {
                d3.select(this).transition().duration(400)
                  .attr('fill-opacity', 0.3)
                  .style("stroke-width", stroke_width_hover);
            }
          })
          .on('mouseout', function(d, i) {
            if (d.count > 0) {
                d3.select(this).transition().duration(400)
                  .attr('fill-opacity', 0)
                  .style("stroke-width", stroke_width);
            }
          })
          .style("opacity", 0)
          .transition()
          .duration(500)
          .delay(function(d, i){return i*50;})
          .style("opacity", 1);
        d3.select("#loading").remove();
      }

      d3.json("{{ url_for('airflow.task_stats') }}?dag_ids=" + (encoded_dag_ids.join(',')), function(error, json) {
        for(var dag_id in json) {
            states = json[dag_id];
            drawTaskStatsForDag(dag_id, states);
        }
        $("#pause_header").tooltip();
        $("#statuses_info").tooltip();

        $("circle").tooltip({
          html: true,
          container: "body",
        });
      });
  </script>
{% endblock %}
