code: def upgrade():
      bind = op.get_bind()
      session = db.Session(bind=bind)
  
      dash_to_migrate = defaultdict(list)
      iframe_urls = defaultdict(list)
  
      try:
          # find iframe viz_type and its url
          iframes = (
              session.query(Slice).filter_by(viz_type="iframe").all()
          )
          iframe_ids = [slc.id for slc in iframes]
  
          for iframe in iframes:
              iframe_params = json.loads(iframe.params or "{}")
              url = iframe_params.get("url")
              iframe_urls[iframe.id] = url
  
          # find iframe viz_type that used in dashboard
          dash_slc = (
              session.query(dashboard_slices)
              .filter(dashboard_slices.c.slice_id.in_(iframe_ids))
              .all()
          )
          for entry in dash_slc:
              dash_to_migrate[entry.dashboard_id].append(entry.slice_id)
          dashboard_ids = list(dash_to_migrate.keys())
  
          # replace iframe in dashboard metadata
          dashboards = (
              session.query(Dashboard)
              .filter(Dashboard.id.in_(dashboard_ids))
              .all()
          )
          for i, dashboard in enumerate(dashboards):
              print(
                  f"scanning dashboard ({i + 1}/{len(dashboards)}) dashboard: {dashboard.id} >>>>"
              )
  
              # remove iframe slices from dashboard
              dashboard.slices = [
                  slc
                  for slc in dashboard.slices
                  if slc.id not in iframe_ids
              ]
  
              # find iframe chart position in metadata
              # and replace it with markdown component
              position_dict = json.loads(
                  dashboard.position_json or "{}"
              )
              keys_to_remove = []
              for key, chart_position in position_dict.items():
                  if (
                      chart_position
                      and isinstance(chart_position, dict)
                      and chart_position["type"] == "CHART"
                      and chart_position["meta"]
                      and chart_position["meta"]["chartId"]
                      in iframe_ids
                  ):
                      iframe_id = chart_position["meta"]["chartId"]
                      # make new markdown component
                      markdown = create_new_markdown_component(
                          chart_position, iframe_urls[iframe_id]
                      )
                      keys_to_remove.append(key)
                      position_dict[markdown["id"]] = markdown
  
                      # add markdown to layout tree
                      parent_id = markdown["parents"][-1]
                      children = position_dict[parent_id]["children"]
                      children.remove(key)
                      children.append(markdown["id"])
  
              if keys_to_remove:
                  for key_to_remove in keys_to_remove:
                      del position_dict[key_to_remove]
                  dashboard.position_json = json.dumps(
                      position_dict,
                      indent=None,
                      separators=(",", ":"),
                      sort_keys=True,
                  )
  
          # remove iframe, separator and markup charts
          slices_to_remove = (
              session.query(Slice)
              .filter(
                  Slice.viz_type.in_(["iframe", "separator", "markup"])
              )
              .all()
          )
          slices_ids = [slc.id for slc in slices_to_remove]
  
          # remove dependencies first
          session.query(dashboard_slices).filter(
              dashboard_slices.c.slice_id.in_(slices_ids)
          ).delete(synchronize_session=False)
  
          session.query(slice_user).filter(
              slice_user.c.slice_id.in_(slices_ids)
          ).delete(synchronize_session=False)
  
          # remove slices
          session.query(Slice).filter(Slice.id.in_(slices_ids)).delete(
              synchronize_session=False
          )
  
      except Exception as ex:
          logging.exception(f"dashboard {dashboard.id} has error: {ex}")
  
      session.commit()
      session.close()
  
file_path: superset/superset/migrations/versions/2020-08-12_00-24_978245563a02_migrate_iframe_to_dash_markdown.py
imports: import json
  import logging
  import uuid
  from collections import defaultdict
  from alembic import op
  from sqlalchemy import and_, Column, ForeignKey, Integer, String, Table, Text
  from sqlalchemy.ext.declarative import declarative_base
  from sqlalchemy.orm import relationship
  from superset import db
label_desc: Use image node_id 2 for calling a global function with example usage: upgrade() without return types