Загрузка данных


{
  "$schema": "https://vega.github.io/schema/vega/v5.json",

  "autosize": {
    "type": "none",
    "contains": "padding"
  },

  "width": 1200,
  "height": 700,
  "padding": 0,

  "signals": [
    {
      "name": "leftWidth",
      "update": "width * 0.5"
    },
    {
      "name": "rightWidth",
      "update": "width - leftWidth"
    },
    {
      "name": "headerHeight",
      "update": "max(35, height * 0.06)"
    },
    {
      "name": "bodyHeight",
      "update": "height - headerHeight"
    },
    {
      "name": "pairHeight",
      "update": "max(48, height * 0.075)"
    },
    {
      "name": "subRowHeight",
      "update": "pairHeight / 2"
    },
    {
      "name": "monthWidth",
      "value": 90
    },
    {
      "name": "bodyContentHeight",
      "update": "length(data('rows')) * pairHeight"
    }
  ],

  "data": [
    {
      "name": "dataset"
    },

    {
      "name": "monthly",
      "source": "dataset",
      "transform": [
        {
          "type": "aggregate",
          "groupby": [
            "Программа",
            "Проект",
            "Группа",
            "Исполнитель",
            "Месяц"
          ],
          "fields": [
            "План",
            "Факт"
          ],
          "ops": [
            "max",
            "max"
          ],
          "as": [
            "План",
            "Факт"
          ]
        },
        {
          "type": "formula",
          "as": "rowKey",
          "expr": "datum['Программа'] + '|' + datum['Проект'] + '|' + datum['Группа'] + '|' + datum['Исполнитель']"
        }
      ]
    },

    {
      "name": "rows",
      "source": "monthly",
      "transform": [
        {
          "type": "aggregate",
          "groupby": [
            "rowKey",
            "Программа",
            "Проект",
            "Группа",
            "Исполнитель"
          ]
        },
        {
          "type": "collect",
          "sort": {
            "field": [
              "Программа",
              "Проект",
              "Группа",
              "Исполнитель"
            ],
            "order": [
              "ascending",
              "ascending",
              "ascending",
              "ascending"
            ]
          }
        }
      ]
    },

    {
      "name": "months",
      "source": "monthly",
      "transform": [
        {
          "type": "aggregate",
          "groupby": [
            "Месяц"
          ]
        },
        {
          "type": "collect",
          "sort": {
            "field": "Месяц",
            "order": "ascending"
          }
        }
      ]
    },

    {
      "name": "cells",
      "source": "monthly",
      "transform": [
        {
          "type": "fold",
          "fields": [
            "План",
            "Факт"
          ],
          "as": [
            "Строка",
            "Часы"
          ]
        }
      ]
    }
  ],

  "scales": [
    {
      "name": "xLeft",
      "type": "band",
      "domain": [
        "Программа",
        "Проект",
        "Группа",
        "Исполнитель",
        "Строка"
      ],
      "range": [
        0,
        {
          "signal": "leftWidth"
        }
      ],
      "paddingInner": 0,
      "paddingOuter": 0
    },

    {
      "name": "yRows",
      "type": "band",
      "domain": {
        "data": "rows",
        "field": "rowKey"
      },
      "range": [
        0,
        {
          "signal": "bodyContentHeight"
        }
      ],
      "paddingInner": 0,
      "paddingOuter": 0
    },

    {
      "name": "xMonths",
      "type": "band",
      "domain": {
        "data": "months",
        "field": "Месяц"
      },
      "range": [
        0,
        {
          "signal": "length(data('months')) * monthWidth"
        }
      ],
      "paddingInner": 0,
      "paddingOuter": 0
    }
  ],

  "marks": [

    {
      "name": "leftHeader",
      "type": "group",

      "encode": {
        "enter": {
          "x": {
            "value": 0
          },
          "y": {
            "value": 0
          }
        },
        "update": {
          "width": {
            "signal": "leftWidth"
          },
          "height": {
            "signal": "headerHeight"
          }
        }
      },

      "marks": [
        {
          "type": "rect",
          "encode": {
            "update": {
              "x": {
                "value": 0
              },
              "y": {
                "value": 0
              },
              "width": {
                "signal": "leftWidth"
              },
              "height": {
                "signal": "headerHeight"
              },
              "fill": {
                "value": "#F3F3F3"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "xLeftHeaders"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', datum.column) + bandwidth('xLeft') / 2"
              },
              "y": {
                "signal": "headerHeight / 2"
              },
              "text": {
                "field": "column"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              },
              "fontWeight": {
                "value": "bold"
              }
            }
          }
        },

        {
          "type": "rule",
          "from": {
            "data": "xLeftHeaders"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', datum.column)"
              },
              "y": {
                "value": 0
              },
              "y2": {
                "signal": "headerHeight"
              },
              "stroke": {
                "value": "#D9D9D9"
              }
            }
          }
        }
      ]
    },

    {
      "name": "rightHeader",
      "type": "group",
      "clip": true,

      "encode": {
        "enter": {
          "y": {
            "value": 0
          }
        },
        "update": {
          "x": {
            "signal": "leftWidth"
          },
          "width": {
            "signal": "rightWidth"
          },
          "height": {
            "signal": "headerHeight"
          }
        }
      },

      "marks": [
        {
          "type": "rect",
          "encode": {
            "update": {
              "x": {
                "value": 0
              },
              "y": {
                "value": 0
              },
              "width": {
                "signal": "rightWidth"
              },
              "height": {
                "signal": "headerHeight"
              },
              "fill": {
                "value": "#F3F3F3"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "months"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xMonths', datum['Месяц']) + bandwidth('xMonths') / 2"
              },
              "y": {
                "signal": "headerHeight / 2"
              },
              "text": {
                "signal": "timeFormat(toDate(datum['Месяц']), '%m.%Y')"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              },
              "fontWeight": {
                "value": "bold"
              }
            }
          }
        },

        {
          "type": "rule",
          "from": {
            "data": "months"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xMonths', datum['Месяц'])"
              },
              "y": {
                "value": 0
              },
              "y2": {
                "signal": "headerHeight"
              },
              "stroke": {
                "value": "#D9D9D9"
              }
            }
          }
        }
      ]
    },

    {
      "name": "leftBody",
      "type": "group",
      "clip": true,

      "encode": {
        "enter": {
          "x": {
            "value": 0
          }
        },
        "update": {
          "y": {
            "signal": "headerHeight"
          },
          "width": {
            "signal": "leftWidth"
          },
          "height": {
            "signal": "bodyHeight"
          }
        }
      },

      "marks": [

        {
          "type": "rule",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "value": 0
              },
              "x2": {
                "signal": "leftWidth"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey)"
              },
              "stroke": {
                "value": "#D9D9D9"
              }
            }
          }
        },

        {
          "type": "rule",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', 'Строка')"
              },
              "x2": {
                "signal": "leftWidth"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') / 2"
              },
              "stroke": {
                "value": "#A0A0A0"
              },
              "strokeDash": {
                "value": [
                  4,
                  3
                ]
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', 'Программа') + bandwidth('xLeft') / 2"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') / 2"
              },
              "text": {
                "field": "Программа"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', 'Проект') + bandwidth('xLeft') / 2"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') / 2"
              },
              "text": {
                "field": "Проект"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', 'Группа') + bandwidth('xLeft') / 2"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') / 2"
              },
              "text": {
                "field": "Группа"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', 'Исполнитель') + bandwidth('xLeft') / 2"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') / 2"
              },
              "text": {
                "field": "Исполнитель"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', 'Строка') + bandwidth('xLeft') / 2"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') / 4"
              },
              "text": {
                "value": "План"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', 'Строка') + bandwidth('xLeft') / 2"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') * 0.75"
              },
              "text": {
                "value": "Факт"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              }
            }
          }
        },

        {
          "type": "rule",
          "from": {
            "data": "xLeftHeaders"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xLeft', datum.column)"
              },
              "y": {
                "value": 0
              },
              "y2": {
                "signal": "bodyContentHeight"
              },
              "stroke": {
                "value": "#D9D9D9"
              }
            }
          }
        }
      ]
    },

    {
      "name": "rightBody",
      "type": "group",
      "clip": true,

      "encode": {
        "update": {
          "x": {
            "signal": "leftWidth"
          },
          "y": {
            "signal": "headerHeight"
          },
          "width": {
            "signal": "rightWidth"
          },
          "height": {
            "signal": "bodyHeight"
          }
        }
      },

      "marks": [

        {
          "type": "rule",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "value": 0
              },
              "x2": {
                "signal": "length(data('months')) * monthWidth"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey)"
              },
              "stroke": {
                "value": "#D9D9D9"
              }
            }
          }
        },

        {
          "type": "rule",
          "from": {
            "data": "rows"
          },
          "encode": {
            "update": {
              "x": {
                "value": 0
              },
              "x2": {
                "signal": "length(data('months')) * monthWidth"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + bandwidth('yRows') / 2"
              },
              "stroke": {
                "value": "#A0A0A0"
              },
              "strokeDash": {
                "value": [
                  4,
                  3
                ]
              }
            }
          }
        },

        {
          "type": "rule",
          "from": {
            "data": "months"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xMonths', datum['Месяц'])"
              },
              "y": {
                "value": 0
              },
              "y2": {
                "signal": "bodyContentHeight"
              },
              "stroke": {
                "value": "#E0E0E0"
              }
            }
          }
        },

        {
          "type": "text",
          "from": {
            "data": "cells"
          },
          "encode": {
            "update": {
              "x": {
                "signal": "scale('xMonths', datum['Месяц']) + bandwidth('xMonths') / 2"
              },
              "y": {
                "signal": "scale('yRows', datum.rowKey) + (datum['Строка'] === 'План' ? bandwidth('yRows') / 4 : bandwidth('yRows') * 0.75)"
              },
              "text": {
                "signal": "datum['Часы'] == null ? '' : format(datum['Часы'], '.1f')"
              },
              "align": {
                "value": "center"
              },
              "baseline": {
                "value": "middle"
              }
            }
          }
        }
      ]
    }
  ]
}