code: def upgrade():
      bind = op.get_bind()
      session = db.Session(bind=bind)
  
      dashboards = session.query(Dashboard).all()
      for i, dashboard in enumerate(dashboards):
          print(f"scanning dashboard ({i + 1}/{len(dashboards)}) >>>>")
          position_json = json.loads(dashboard.position_json or "[]")
          if not is_v2_dash(position_json):
              print(f"Converting dashboard... dash_id: {dashboard.id}")
              position_dict = {}
              positions = []
              slices = dashboard.slices
  
              if position_json:
                  # scan and fix positions data: extra spaces, dup rows, .etc
                  position_json = scan_dashboard_positions_data(
                      position_json
                  )
                  position_dict = {
                      str(position["slice_id"]): position
                      for position in position_json
                  }
  
              last_row_id = (
                  max(
                      [
                          pos["row"] + pos["size_y"]
                          for pos in position_json
                      ]
                  )
                  if position_json
                  else 0
              )
              new_slice_counter = 0
              for slice in slices:
                  position = position_dict.get(str(slice.id))
  
                  # some dashboard didn't have position_json
                  # place 3 charts in a row
                  if not position:
                      position = {
                          "col": (
                              new_slice_counter
                              % NUMBER_OF_CHARTS_PER_ROW
                              * DEFAULT_CHART_WIDTH
                              + 1
                          ),
                          "row": (
                              last_row_id
                              + int(
                                  new_slice_counter
                                  / NUMBER_OF_CHARTS_PER_ROW
                              )
                              * DEFAULT_CHART_WIDTH
                          ),
                          "size_x": DEFAULT_CHART_WIDTH,
                          "size_y": DEFAULT_CHART_WIDTH,
                          "slice_id": str(slice.id),
                      }
                      new_slice_counter += 1
  
                  # attach additional parameters to position dict,
                  # prepare to replace markup and separator viz_type
                  # to dashboard UI component
                  form_data = json.loads(slice.params or "{}")
                  viz_type = slice.viz_type
                  if form_data and viz_type in ["markup", "separator"]:
                      position["code"] = form_data.get("code")
                      position["slice_name"] = slice.slice_name
  
                  positions.append(position)
  
              v2_layout = convert_to_layout(positions)
              v2_layout[DASHBOARD_HEADER_ID] = get_header_component(
                  dashboard.dashboard_title
              )
  
              sorted_by_key = collections.OrderedDict(
                  sorted(v2_layout.items())
              )
              dashboard.position_json = json.dumps(
                  sorted_by_key, indent=2
              )
              session.commit()
          else:
              print(f"Skip converted dash_id: {dashboard.id}")
  
      session.close()
  
imports: import collections
  import json
  import sys
  import uuid
  from functools import reduce
  from alembic import op
  from sqlalchemy import 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 21 for calling a global function with example usage: upgrade() without return types