Return a dictionary of changes between dashboards.

def _dashboard_diff(_new_dashboard, _old_dashboard):
    '''Return a dictionary of changes between dashboards.'''
    diff = {}

    # Dashboard diff
    new_dashboard = copy.deepcopy(_new_dashboard)
    old_dashboard = copy.deepcopy(_old_dashboard)
    dashboard_diff = DictDiffer(new_dashboard, old_dashboard)
    diff['dashboard'] = _stripped({
        'changed': list(dashboard_diff.changed()) or None,
        'added': list(dashboard_diff.added()) or None,
        'removed': list(dashboard_diff.removed()) or None,
    })

    # Row diff
    new_rows = new_dashboard.get('rows', [])
    old_rows = old_dashboard.get('rows', [])
    new_rows_by_title = {}
    old_rows_by_title = {}
    for row in new_rows:
        if 'title' in row:
            new_rows_by_title[row['title']] = row
    for row in old_rows:
        if 'title' in row:
            old_rows_by_title[row['title']] = row
    rows_diff = DictDiffer(new_rows_by_title, old_rows_by_title)
    diff['rows'] = _stripped({
        'added': list(rows_diff.added()) or None,
        'removed': list(rows_diff.removed()) or None,
    })
    for changed_row_title in rows_diff.changed():
        old_row = old_rows_by_title[changed_row_title]
        new_row = new_rows_by_title[changed_row_title]
        row_diff = DictDiffer(new_row, old_row)
        diff['rows'].setdefault('changed', {})
        diff['rows']['changed'][changed_row_title] = _stripped({
            'changed': list(row_diff.changed()) or None,
            'added': list(row_diff.added()) or None,
            'removed': list(row_diff.removed()) or None,
        })

    # Panel diff
    old_panels_by_id = {}
    new_panels_by_id = {}
    for row in old_dashboard.get('rows', []):
        for panel in row.get('panels', []):
            if 'id' in panel:
                old_panels_by_id[panel['id']] = panel
    for row in new_dashboard.get('rows', []):
        for panel in row.get('panels', []):
            if 'id' in panel:
                new_panels_by_id[panel['id']] = panel
    panels_diff = DictDiffer(new_panels_by_id, old_panels_by_id)
    diff['panels'] = _stripped({
        'added': list(panels_diff.added()) or None,
        'removed': list(panels_diff.removed()) or None,
    })
    for changed_panel_id in panels_diff.changed():
        old_panel = old_panels_by_id[changed_panel_id]
        new_panel = new_panels_by_id[changed_panel_id]
        panels_diff = DictDiffer(new_panel, old_panel)
        diff['panels'].setdefault('changed', {})
        diff['panels']['changed'][changed_panel_id] = _stripped({
            'changed': list(panels_diff.changed()) or None,
            'added': list(panels_diff.added()) or None,
            'removed': list(panels_diff.removed()) or None,
        })

    return diff