You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ka...@apache.org on 2020/12/03 02:27:43 UTC

[airflow] branch fix-dagrun-edit created (now 1892420)

This is an automated email from the ASF dual-hosted git repository.

kaxilnaik pushed a change to branch fix-dagrun-edit
in repository https://gitbox.apache.org/repos/asf/airflow.git.


      at 1892420  BugFix: Editing a DAG run or Task Instance on UI causes an Error

This branch includes the following new commits:

     new 1892420  BugFix: Editing a DAG run or Task Instance on UI causes an Error

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[airflow] 01/01: BugFix: Editing a DAG run or Task Instance on UI causes an Error

Posted by ka...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kaxilnaik pushed a commit to branch fix-dagrun-edit
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 1892420b2a363912edcdad32907025e68bf886bc
Author: Kaxil Naik <ka...@gmail.com>
AuthorDate: Thu Dec 3 02:21:49 2020 +0000

    BugFix: Editing a DAG run or Task Instance on UI causes an Error
    
    closes https://github.com/apache/airflow/issues/12489
---
 airflow/www/forms.py   | 62 ++++++++++++++++++++++++++++++++++++++++++++++++--
 airflow/www/views.py   | 18 ++++++++++++++-
 airflow/www/widgets.py | 26 ++++++++++++++++++++-
 3 files changed, 102 insertions(+), 4 deletions(-)

diff --git a/airflow/www/forms.py b/airflow/www/forms.py
index 2485010..4b0f3ed 100644
--- a/airflow/www/forms.py
+++ b/airflow/www/forms.py
@@ -46,7 +46,12 @@ from airflow.configuration import conf
 from airflow.utils import timezone
 from airflow.utils.types import DagRunType
 from airflow.www.validators import ValidJson
-from airflow.www.widgets import AirflowDateTimePickerWidget
+from airflow.www.widgets import (
+    AirflowDateTimePickerROWidget,
+    AirflowDateTimePickerWidget,
+    BS3TextAreaROWidget,
+    BS3TextFieldROWidget,
+)
 
 
 class DateTimeWithTimezoneField(Field):
@@ -127,7 +132,7 @@ class DateTimeWithNumRunsWithDagRunsForm(DateTimeWithNumRunsForm):
 
 
 class DagRunForm(DynamicForm):
-    """Form for editing and adding DAG Run"""
+    """Form for adding DAG Run"""
 
     dag_id = StringField(lazy_gettext('Dag Id'), validators=[DataRequired()], widget=BS3TextFieldWidget())
     start_date = DateTimeWithTimezoneField(lazy_gettext('Start Date'), widget=AirflowDateTimePickerWidget())
@@ -158,6 +163,59 @@ class DagRunForm(DynamicForm):
             item.conf = json.loads(item.conf)
 
 
+class DagRunEditForm(DynamicForm):
+    """Form for editing DAG Run"""
+
+    dag_id = StringField(lazy_gettext('Dag Id'), validators=[DataRequired()], widget=BS3TextFieldROWidget())
+    start_date = DateTimeWithTimezoneField(lazy_gettext('Start Date'), widget=AirflowDateTimePickerROWidget())
+    end_date = DateTimeWithTimezoneField(lazy_gettext('End Date'), widget=AirflowDateTimePickerROWidget())
+    run_id = StringField(lazy_gettext('Run Id'), validators=[DataRequired()], widget=BS3TextFieldROWidget())
+    state = SelectField(
+        lazy_gettext('State'),
+        choices=(
+            ('success', 'success'),
+            ('running', 'running'),
+            ('failed', 'failed'),
+        ),
+        widget=Select2Widget(),
+    )
+    execution_date = DateTimeWithTimezoneField(
+        lazy_gettext('Execution Date'), widget=AirflowDateTimePickerROWidget()
+    )
+    conf = TextAreaField(
+        lazy_gettext('Conf'), validators=[ValidJson(), Optional()], widget=BS3TextAreaROWidget()
+    )
+
+    def populate_obj(self, item):
+        """Populates the attributes of the passed obj with data from the form’s fields."""
+        super().populate_obj(item)  # pylint: disable=no-member
+        item.run_type = DagRunType.from_run_id(item.run_id)
+        if item.conf:
+            item.conf = json.loads(item.conf)
+
+
+class TaskInstanceEditForm(DynamicForm):
+    """Form for editing TaskInstance"""
+
+    dag_id = StringField(lazy_gettext('Dag Id'), validators=[DataRequired()], widget=BS3TextFieldROWidget())
+    task_id = StringField(lazy_gettext('Task Id'), validators=[DataRequired()], widget=BS3TextFieldROWidget())
+    start_date = DateTimeWithTimezoneField(lazy_gettext('Start Date'), widget=AirflowDateTimePickerROWidget())
+    end_date = DateTimeWithTimezoneField(lazy_gettext('End Date'), widget=AirflowDateTimePickerROWidget())
+    state = SelectField(
+        lazy_gettext('State'),
+        choices=(
+            ('success', 'success'),
+            ('running', 'running'),
+            ('failed', 'failed'),
+            ('up_for_retry', 'up_for_retry'),
+        ),
+        widget=Select2Widget(),
+    )
+    execution_date = DateTimeWithTimezoneField(
+        lazy_gettext('Execution Date'), widget=AirflowDateTimePickerROWidget()
+    )
+
+
 _connection_types = [
     ('docker', 'Docker Registry'),
     ('elasticsearch', 'Elasticsearch'),
diff --git a/airflow/www/views.py b/airflow/www/views.py
index 1d386a4..70840dd 100644
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -91,10 +91,12 @@ from airflow.www import auth, utils as wwwutils
 from airflow.www.decorators import action_logging, gzipped
 from airflow.www.forms import (
     ConnectionForm,
+    DagRunEditForm,
     DagRunForm,
     DateTimeForm,
     DateTimeWithNumRunsForm,
     DateTimeWithNumRunsWithDagRunsForm,
+    TaskInstanceEditForm,
 )
 from airflow.www.widgets import AirflowModelListWidget
 
@@ -3229,12 +3231,14 @@ class DagRunModelView(AirflowModelView):
     add_columns = ['state', 'dag_id', 'execution_date', 'run_id', 'external_trigger', 'conf']
     list_columns = ['state', 'dag_id', 'execution_date', 'run_id', 'run_type', 'external_trigger', 'conf']
     search_columns = ['state', 'dag_id', 'execution_date', 'run_id', 'run_type', 'external_trigger', 'conf']
+    edit_columns = ['state', 'dag_id', 'execution_date', 'run_id', 'conf']
 
     base_order = ('execution_date', 'desc')
 
     base_filters = [['dag_id', DagFilter, lambda: []]]
 
-    add_form = edit_form = DagRunForm
+    add_form = DagRunForm
+    edit_form = DagRunEditForm
 
     formatters_columns = {
         'execution_date': wwwutils.datetime_f('execution_date'),
@@ -3391,6 +3395,7 @@ class TaskRescheduleModelView(AirflowModelView):
     route_base = '/taskreschedule'
 
     datamodel = AirflowModelView.CustomSQLAInterface(models.TaskReschedule)  # noqa # type: ignore
+    related_views = [DagRunModelView]
 
     class_permission_name = permissions.RESOURCE_TASK_RESCHEDULE
     method_permission_name = {
@@ -3500,6 +3505,17 @@ class TaskInstanceModelView(AirflowModelView):
         'end_date',
     ]
 
+    edit_columns = [
+        'state',
+        'dag_id',
+        'task_id',
+        'execution_date',
+        'start_date',
+        'end_date',
+    ]
+
+    edit_form = TaskInstanceEditForm
+
     base_order = ('job_id', 'asc')
 
     base_filters = [['dag_id', DagFilter, lambda: []]]
diff --git a/airflow/www/widgets.py b/airflow/www/widgets.py
index 172380c..ab2e4cf 100644
--- a/airflow/www/widgets.py
+++ b/airflow/www/widgets.py
@@ -15,7 +15,7 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-
+from flask_appbuilder.fieldwidgets import BS3TextAreaFieldWidget, BS3TextFieldWidget
 from flask_appbuilder.widgets import RenderTemplateWidget
 from markupsafe import Markup
 from wtforms.widgets import html_params
@@ -46,3 +46,27 @@ class AirflowDateTimePickerWidget:
         template = self.data_template
 
         return Markup(template % {"text": html_params(type="text", value=field.data, **kwargs)})
+
+
+class AirflowDateTimePickerROWidget(AirflowDateTimePickerWidget):
+    """Airflow Read-only date time picker widget"""
+
+    def __call__(self, field, **kwargs):
+        kwargs['readonly'] = 'true'
+        return super().__call__(field, **kwargs)
+
+
+class BS3TextFieldROWidget(BS3TextFieldWidget):
+    """Read-only single-line text input Widget (BS3TextFieldWidget)"""
+
+    def __call__(self, field, **kwargs):
+        kwargs['readonly'] = 'true'
+        return super().__call__(field, **kwargs)
+
+
+class BS3TextAreaROWidget(BS3TextAreaFieldWidget):
+    """Read-only multi-line text area Widget (BS3TextAreaROWidget)"""
+
+    def __call__(self, field, **kwargs):
+        kwargs['readonly'] = 'true'
+        return super().__call__(field, **kwargs)