Data Analysis Dashboard Development Skill
Provides full data analysis dashboard (instrument panel) development capabilities—project creation, card plan, data cleaning, dashboard development, validation, and delivery—as one end-to-end workflow. Data cleaning is an important part of dashboard development.
Code Execution Method
All tool calls via from sdk.tool import tool in this skill must be executed by passing code to run_sdk_snippet's python_code parameter.
When a skill snippet calls tools through the SDK, always use result.ok to determine success and read failure details from result.content. Do not read or call result.error, because SDK Result stores failure text in content.
Quick Start
Important: Detailed rules are inlined later in this document; while executing steps, consult Workflow Summary, Project Setup, Dashboard Development, and Dashboard Data Cleaning Guide.
Runtime base files: Do not modify, overwrite, or delete index.html, dashboard.js, or index.css; violations make the data dashboard unusable.
Default card counts for new dashboards — MANDATORY unless the user explicitly requests a reduced scope:
| Card type |
Required count |
Notes |
| metric |
≥ 6 |
Key KPI overview cards |
| echarts |
26 – 30 |
Core of the dashboard; pad with same-type charts across different dimensions |
| table |
2 – 3 |
At least 1 must be a detail-level table |
| markdown |
0 |
Omit unless the user asks for notes/commentary |
| Total |
34 – 39 |
Self-check before delivery |
Enforcement rules (non-negotiable):
- You must list every card in
cards_plan before calling create_dashboard_project; do not defer cards to later.
card_id in cards_plan must exactly match the id used in subsequent create_dashboard_cards calls.
- If echarts count falls short, add same-type charts covering different dimensions or time ranges — do not lower the floor.
- Before delivery, call
query_dashboard_cards and count by type; if any type is below the required count, create the missing cards before proceeding.
- These counts are overridden only when the user explicitly states they want a smaller dashboard.
Workflow Summary
Path overview
- New dashboard: Planning prep (brainstorm + read sources) → Create dashboard project → Data cleaning → Dashboard development → Validate → Complete delivery
- Edit dashboard: Project identification → Data cleaning (as needed) → Dashboard editing and card-tool maintenance → Validate → Complete delivery
Step details
- Project identification: Understand user needs and identify the target project (edit scenarios)
- Planning prep (new dashboards): Before
create_dashboard_project, brainstorm questions and angles; read sources in depth (fields, grain, definitions, time, distributions, missingness, comparable dimensions), then author cards_plan strictly following the mandatory counts in Quick Start (metric ≥6, echarts 26–30, table 2–3, total 34–39)—every card must be listed before project creation; a handful of representative charts is not acceptable
- Create project: Must call
create_dashboard_project with the required cards_plan in the same call; the tool writes cards_plan.md from it; plan card identifiers must match id in later create_dashboard_cards
- Data cleaning: From
cards_plan.md and data goals, create and run data_cleaning.py in the project to supply data for the dashboard
- Dashboard editing: Change allowed files as needed (e.g. data_cleaning.py, cleaned_data/, config.js; never modify or delete index.html, dashboard.js, index.css) (edit scenarios)
- Dashboard development: Per
cards_plan.md, use card tools (create_dashboard_cards, update_dashboard_cards, delete_dashboard_cards, query_dashboard_cards) to create or maintain cards
- Validate dashboard: Call
validate_dashboard(project_path="PROJECT_NAME"); fix errors and re-run until passing (do not delete cards to pass validation)
- Complete delivery: Summarize the project and analysis results, then close the task
Core principles
Follow the steps in order; validation must pass before delivery or the page will not work.
Never modify, overwrite, or delete index.html, dashboard.js, or index.css, or the data dashboard becomes unusable (maintain allowed files such as data.js only via card tools and other permitted paths).
Preferences
- New dashboards: planning prep first, then
cards_plan; strictly follow the mandatory card counts table in Quick Start — metric ≥6, echarts 26–30, table 2–3, total 34–39; self-check with query_dashboard_cards before delivery
- Prefer setting
title on cards (skip on metric cards when it would duplicate the metric label)
- Prefer ECharts; many charts → same-type charts across dimensions
- Card standard size examples (24-column grid): metric
{w:4,h:3}, chart {w:8,h:8}, table {w:12,h:8}, Markdown {w:12,h:(calculate height based on content)}
Decision Tree
New or edit dashboard?
├─ New → Planning prep (brainstorm, read sources) → Author cards_plan [metric ≥6, echarts 26–30, table 2–3, total 34–39, ALL cards listed] → create_dashboard_project → Data cleaning → Dashboard development → Validate → Complete delivery
└─ Edit → Identify existing dashboard project → Data cleaning (as needed) → Dashboard development/editing → Validate → Complete delivery
Need data cleaning?
├─ New dashboard → Must execute data_cleaning.py
├─ Edit dashboard with data/requirement changes → Execute as needed
└─ Edit dashboard without changes → Can skip
Card count check before delivery?
├─ call query_dashboard_cards → count by type
├─ metric < 6 or echarts < 26 or table < 2 → create missing cards first
└─ counts met → proceed to validate
Validation failed? → Fix issues and re-run validate_dashboard until result.ok with no errors
Core Tools
create_dashboard_project — Create Dashboard Project
| Param |
Required |
Type |
Description |
name |
Yes |
string |
Project name; the dashboard is created under this directory in the workspace |
cards_plan |
Yes |
array |
Card plan list (at least one item); the tool generates cards_plan.md from this; field definitions and how to author entries are in the tool usage example below |
Finish Planning prep, then author cards_plan. For new dashboards, the mandatory per-type counts are defined in Quick Start under Default card counts for new dashboards (metric ≥6, echarts 26–30, table 2–3, total 34–39). You must list every card in cards_plan before calling this tool — partial plans are not allowed. Only if the user explicitly requests a reduced dashboard may you go below these counts; otherwise every card must be listed upfront. You may call create_dashboard_cards in batches after project creation. This tool does not validate counts — it is your responsibility to meet them.
Return (result): On success result.ok is true and result.content includes the created path and related information; on failure result.ok is false and result.content contains the error (e.g. "Directory already exists").
validate_dashboard — Validate Dashboard
| Param |
Required |
Type |
Description |
project_path |
Yes |
string |
Dashboard project directory path, relative to workspace root |
Return (result): On success result.ok is true with no errors; on failure result.ok is false and result.content contains validation failure reasons. Validation must pass before delivery; otherwise the page will not be accessible.
Card Management Tools
You must use the card management tools to work on data.js; direct editing is strictly prohibited. Card fields and CardData are defined under Card Data DSL below; layout workflow and validation are under Dashboard Development.
| Tool |
Description |
| create_dashboard_cards |
Batch create; prefer ≤6 cards/call; auto_layout (may omit layout) |
| update_dashboard_cards |
Batch update; 1–10 updates/call, prefer ≤6; single-field edits supported |
| delete_dashboard_cards |
Batch delete; card_ids 1–20; auto-compacts layout after delete |
| query_dashboard_cards |
Query all or by id; optional fields to filter returned properties |
create_dashboard_cards
auto_layout (default false): When true, omit per-card layout; the tool generates gap-free layout by type order to reduce validate churn. Recommend auto_layout=true for new dashboards. Prefer about 6 or fewer cards per call when feasible (not a hard cap).
| Param |
Required |
Type |
Description |
project_path |
Yes |
string |
Dashboard project path |
cards |
Yes |
List |
Card list; each item has id, type, source, getCardData; when auto_layout=true, layout may be omitted |
auto_layout |
No |
boolean |
When true, omit layout and let the tool fill the grid |
update_dashboard_cards
Each update must include id and at least one other field to change (type, source, title, titleAlign, layout, getCardData, etc.). layout accepts partial objects (e.g. only y).
| Param |
Required |
Type |
Description |
project_path |
Yes |
string |
Dashboard project path |
updates |
Yes |
List |
Update objects, 1–10 items; prefer ≤6 per call |
delete_dashboard_cards
| Param |
Required |
Type |
Description |
project_path |
Yes |
string |
Dashboard project path |
card_ids |
Yes |
List[str] |
IDs to delete, 1–20 items, no duplicates |
query_dashboard_cards
| Param |
Required |
Type |
Description |
project_path |
Yes |
string |
Dashboard project path |
card_ids |
No |
List[str] |
Omit for all cards; if set, 1–20 IDs |
fields |
No |
List[str] |
Omit for all fields; else a subset of id, type, title, source, layout, titleAlign, getCardData |
Tool Usage Example
# Create dashboard project (see structure below)
# Abbreviated example: shows one cards_plan row shape; real new dashboards must list the full per-type plan per the skill.
result = tool.call('create_dashboard_project', {
"name": "Sales Data Dashboard",
"cards_plan": [
{
"display_name": "Total Sales",
"card_id": "total_sales",
"type": "metric",
"data_detail": "Sum of sales amount",
},
{
"display_name": "Monthly Sales Trend",
"card_id": "monthly_trend",
"type": "echarts",
"data_detail": "Sales by month",
},
],
})
if result.ok:
# Read project path and other info from result.content
pass
else:
# result.content explains failure, e.g. "Directory already exists"
pass
# create_dashboard_cards: auto_layout=True; prefer ≤6 cards/call
result = tool.call('create_dashboard_cards', {
"project_path": "Sales Data Dashboard",
"auto_layout": True,
"cards": [...]
})
# query_dashboard_cards: omit card_ids for all cards; optional fields, card_ids (1–20)
result = tool.call('query_dashboard_cards', {
"project_path": "Sales Data Dashboard",
})
# update_dashboard_cards: each update needs id and ≥1 other field; prefer ≤6 updates/call
result = tool.call('update_dashboard_cards', {
"project_path": "Sales Data Dashboard",
"updates": [...]
})
# delete_dashboard_cards: card_ids 1–20; do not delete to pass validation
result = tool.call('delete_dashboard_cards', {
"project_path": "Sales Data Dashboard",
"card_ids": [...]
})
# validate_dashboard: delivery requires result.ok with no errors
result = tool.call('validate_dashboard', {"project_path": "Sales Data Dashboard"})
if not result.ok:
# Fix issues from result.content, then validate again
pass
# download_dashboard_maps (when needed)
result = tool.call('download_dashboard_maps', {
"project_path": "Sales Data Dashboard",
"area_names": ["China", "Guangdong", "Shenzhen"]
})
File Naming Rules
File and directory naming intelligently determined based on file content, business domain, and user preferred language, e.g.:
- User preferred language is Chinese: "Sales Data Dashboard" (in Chinese), "sales_data.csv" (in Chinese)
- User preferred language is English: "Sales Data Dashboard", "Sales Data.csv"
Key Constraints
- Do not generate any images with Python scripts (matplotlib, seaborn, plotly, etc.); implement all charts with ECharts
- Do not modify data source files; read-only access only
- You must use the card management tools (create_dashboard_cards, update_dashboard_cards, delete_dashboard_cards) for those operations
- Temporary files start with temp_, must delete before task end
- File naming determined by content, business domain, user preferred language
data.js must be maintained only through those card tools; direct edits or overwriting data.js are prohibited
- Never modify, overwrite, or delete
index.html, dashboard.js, or index.css; any change or removal makes the data dashboard unusable
Data Sources
Data source role: Provide foundational data support for data analysis dashboard development.
Supported type examples: Excel, CSV, JSON, plain text, PDF, web-sourced data, MCP tool data.
Operating rule: Do not change user-uploaded source files; read-only.
Data source identification and validation:
- Understand the request; identify the source type and content.
- If data comes from the web or MCP tools, persist it to a JSON file before analysis.
- Source inspection—if any of the following applies, enter the exception-handling flow immediately: unreadable source; unsupported format; empty template; headers only; no valid business data; unacceptably poor data quality.
- Exception-handling flow: Tell the user why and end the task.
Project Structure, Files, and Editing Rules
The tree matches the table: path, purpose, and editing rules for each item.
Project Directory/
├── geo/ # Map GeoJSON data
├── cleaned_data/ # Cleaned data (CSV)
├── data_cleaning.py # Data cleaning script
├── data.js # Card config (DASHBOARD_CARDS)
├── config.js # Global config (colors, themes, etc.)
├── index.html # Page markup (system-managed; do not modify or delete)
├── index.css # Styles (system-managed; do not modify or delete)
├── dashboard.js # Dashboard runtime (system-managed; do not modify or delete)
└── magic.project.js # Project configuration file
| Path |
Purpose |
Editing rules |
| geo/ |
GeoJSON for maps |
System-managed; do not edit |
| cleaned_data/ |
Cleaned CSV for card data sources |
Editable: add or modify files |
| data_cleaning.py |
Raw data → cleaned_data/ |
Editable: adjust cleaning logic as needed |
| data.js |
DASHBOARD_CARDS definitions |
Tools only: create_dashboard_cards, update_dashboard_cards, delete_dashboard_cards; no direct file edits |
| config.js |
Global colors, themes, fonts, etc. |
Restricted: field values only; do not add, remove, rename fields, or change structure |
| index.html |
Page HTML |
System-managed; do not edit, delete, or overwrite or the data dashboard becomes unusable |
| index.css |
Visual styling |
System-managed; do not edit, delete, or overwrite or the data dashboard becomes unusable |
| dashboard.js |
Rendering, loading, charts |
System-managed; do not edit, delete, or overwrite or the data dashboard becomes unusable |
| magic.project.js |
Project metadata |
Prohibited: system-managed |
Card Data DSL
Card Basic Structure:
- id: String, card unique identifier (required)
- type: Card type, strictly follow CardType types (required)
- source: String, data source path, e.g. "./cleaned_data/filename.csv" (required)
- layout: react-grid-layout layout object, contains {x: integer, y: integer, w: integer, h: integer} (required)
- getCardData: Async function, used to load data and process card data, returns CardData (required)
- title: Optional string, card title
- titleAlign: Optional string, title alignment ("left"|"center"|"right")
CardType Card Types:
- metric: Single metric card, displays metric value
- table: Data table card, displays structured data
- markdown: Markdown document card
- echarts: ECharts chart card
CardData Data Structure Specification:
- MetricCard (Metric card):
- label: String, metric name (required)
- value: String or number, metric value (required)
- change: Optional string, change value or percentage
- unit: Optional string, unit
- icon: Optional string, use icon name from tabler-icon, e.g., "ti-chart-bar"
- iconColor: Optional string, icon color; required whenever
icon is set
- TableCard (Table card):
- columns: Column config array, each item contains:
- title: String, column title
- dataIndex: String, data field name
- dataType: Optional, data type ("string"|"number"|"date"|"time"), default "string"
- width: Optional, column width (string or number)
- formatter: Optional, column formatter function to customize how a cell is shown; parameter
value is the current cell value as a string; return value must be a string; plain text only
- sortable: Optional, boolean, whether to enable sorting
- filterable: Optional, boolean, whether to enable filtering
- data: Data array, each item is object containing each column's data (use raw data as much as possible, then format via formatter function)
- MarkdownCard (Markdown card):
- content: String, Markdown format text content (required)
- echarts (Chart card): ECharts Options (version: v6.0.0) config
getCardData Data Loading Function:
- Async function, parameter is csv object, returns data conforming to CardData specification
- Core methods:
csv.load("filename") loads CSV file in cleaned_data directory (without .csv extension)
- Returns:
{data: row array, fields: column name array, name: filename, url: path}
- Key specifications:
- Field access: Use
row["field_name"], avoid special character issues
- Data conversion:
parseFloat(row["field_name"]) converts string to number, CSV data defaults to string
- Numeric processing: Avoid floating point precision issues, use
Math.round() or .toFixed() when necessary; percentages use .toFixed(2), amounts use .toLocaleString()
- Theme config access: Before using
window.DASHBOARD_CONFIG, read config.js to confirm real key names and value types; example: window.DASHBOARD_CONFIG.COLORS_PRIMARY
Examples
// MetricCard example
getCardData: async (csv) => {
const result = await csv.load("sales_data");
const totalSales = result.data.reduce((sum, row) => {
return sum + parseFloat(row["sales_amount"]);
}, 0);
return {
label: "Total Sales",
value: totalSales,
unit: "USD",
icon: "ti-currency-dollar",
};
};
// TableCard example
getCardData: async (csv) => {
const result = await csv.load("sales_data");
return {
columns: [
{
title: "Region",
dataIndex: "region",
dataType: "string",
sortable: false,
filterable: false,
},
{
title: "Sales",
dataIndex: "sales",
dataType: "number",
sortable: true,
filterable: false,
formatter: (value) => `$${parseFloat(value).toLocaleString()}`,
},
{
title: "Customers",
dataIndex: "customers",
dataType: "number",
sortable: true,
filterable: false,
},
],
data: result.data,
};
};
// ECharts chart example
getCardData: async (csv) => {
const result = await csv.load("sales_data");
return {
grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false },
tooltip: {
trigger: "axis",
formatter: function (params) {
return (
params[0].name + ": " + params[0].value.toLocaleString() + " USD"
);
},
},
xAxis: {
type: "category",
data: result.data.map((row) => row["region"]),
},
yAxis: {
type: "value",
axisLabel: { formatter: (value) => value.toLocaleString() },
},
series: [
{
type: "bar",
data: result.data.map((row) => parseFloat(row["sales"])),
label: {
show: true,
formatter: (params) => params.value.toLocaleString(),
},
},
],
};
};
// MarkdownCard example
getCardData: async (csv) => {
const salesData = await csv.load("sales_base_data");
const productData = await csv.load("product_sales_ranking");
const totalSales = salesData.data.reduce(
(sum, row) => sum + parseFloat(row["sales_amount"]),
0,
);
const topProduct = productData.data.sort(
(a, b) => parseFloat(b["sales_amount"]) - parseFloat(a["sales_amount"]),
)[0];
return {
content: `### Sales Analysis Report\n\n**Total Sales**: ${totalSales.toLocaleString()} USD\n**Top Product**: ${topProduct["product_name"]}`,
};
};
Data processing example (same CSV shape as the English examples above):
// CSV file: sales_data.csv
// Example content:
// region,sales,customers
// East,120000,150
// South,95000,120
const parsedData = [
{ region: "East", sales: "120000", customers: "150" },
{ region: "South", sales: "95000", customers: "120" },
];
const parsedFields = ["region", "sales", "customers"];
Appearance and Layout
Card Actual Size Calculation Logic:
- Width: Based on GRID_COLS column grid system in config.js
- Height: Card height = card rows × card row height (GRID_DEFAULT_ROW_HEIGHT in config.js)
UI theme customization (only when the user explicitly asks): config.js (global theme).
Card Layout:
- Hierarchical arrangement: Metric cards (top overview) → Chart cards (core analysis) → Table cards (detailed data) → Markdown cards (notes)
- Layout principles: Must fully utilize (GRID_COLS value in config.js) column grid system, horizontal-vertical complementary fill, compact continuous filling with no gaps, coordinated width-height ratio
ECharts v6.0.0 Configuration
Dashboard development only
ECharts v6.0.0 key settings:
- Map: series.map uses Chinese region names from GeoJSON, e.g. "China", "Guangdong", "Shenzhen" (pass the exact names from downloaded GeoJSON); series.nameProperty is "fullname"
- grid:
- Mandatory config:
{ left: 0, right: 0, top: 0, bottom: 0, containLabel: false }, in v6.0.0 this config already allows axis and axis labels to display fully edge-aligned, so no need to reserve any space for XY axis labels or axis titles
- outerBounds:
- Use case: Reserve space for legend, visualMap components
- Use condition: Only need to set when legend, visualMap components configured
- Core principle: outerBounds direction must match component position direction
- Config examples:
- Bottom horizontal legend:
{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { bottom: 30 } }, legend: { type: "scroll", bottom: 0 } }
- Left vertical legend:
{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { left: 50 } }, legend: { type: "scroll", orient: "vertical", left: 0 } }
- Bottom horizontal visualMap:
{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { bottom: 50 } }, visualMap: { orient: "horizontal", bottom: 0, left: "center" } }
- Left bottom vertical visualMap:
{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { left: 50 } }, visualMap: { orient: "vertical", left: 0, bottom: 0 } }
- Wrong examples:
outerBounds: { right: 120 }, legend: { orient: "vertical", right: 0 } (legend should be on left)
outerBounds: { left: 60 } but no left component configured (meaningless space reservation)
outerBounds: { top: 30 } but component at bottom (direction mismatch)
- legend:
- Use condition: Only configure when multi-series or pie charts need legend, single-series charts don't need legend
- Recommended config: Horizontal legend use
{ bottom: 0, type: "scroll" }, vertical legend use { left: 0, orient: "vertical", type: "scroll" }
- Avoid using right, top positions, prioritize bottom, left positions
- visualMap:
- visualMap recommended config: Horizontal use
{ orient: "horizontal", bottom: 0, left: "center" }, vertical use { orient: "vertical", left: 0, bottom: 0 }
- Map visualMap suggest using vertical direction:
{ orient: "vertical", left: 0, bottom: 0 } + outerBounds: { left: 50 }
- tooltip: Configure a tooltip for every graphic/series where it helps
- dataZoom: Strictly do not configure
dataZoom; ECharts dataZoom is visually poor and its use is not recommended here
- label:
label.formatter receives a params object; use params.value for the numeric value; set labelLayout.hideOverlap: true where needed to reduce overlap; consider font stroke for readability
- title: Do not set an ECharts
title in options (duplicates the card title)
- axis: Prefer a
name on numeric value axes; avoid name on category axes. Prefer the vertical layout pattern: Y = value axis, X = category axis
- Use the chart area fully; avoid unnecessary empty margins
- Formatter arguments:
label.formatter and tooltip.formatter take params; axisLabel.formatter takes value
- Keep theming consistent with
config.js; read config.js before using window.DASHBOARD_CONFIG to confirm key names and value types
Card Management Tools — Quick Reference
Parameter tables, auto_layout, and batch limits are under Core Tools, Card Management Tools above. Typical usage (pick as needed):
- Create:
create_dashboard_cards; ≤6/call, auto_layout=True
- Edit:
update_dashboard_cards; ≤6 updates/call; partial fields OK (e.g. title, layout.y)
- Delete:
delete_dashboard_cards; do not delete to pass validation
- Browse:
query_dashboard_cards without card_ids (and optional fields) for a quick full summary
- Detail: same tool with
card_ids for full config including getCardData
- Trim payload:
query_dashboard_cards with fields (e.g. id, type, layout only)
Dashboard Data Cleaning Guide
- Script file: data_cleaning.py
- Script example:
import os
import pandas as pd
# Required statements (strictly follow this format)
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
OUTPUT_DIR = os.path.join(PROJECT_ROOT, "cleaned_data")
os.makedirs(OUTPUT_DIR, exist_ok=True)
# Data source definition (if file data sources exist)
FILE_DATA_SOURCES = {
'main_data': os.path.join(PROJECT_ROOT, "..", "data_source.csv"),
'additional_data': os.path.join(PROJECT_ROOT, "..", "additional_data.csv")
}
def main():
# 1. Data loading
df = pd.read_csv(FILE_DATA_SOURCES['main_data'])
# 2. Data cleaning: Handle missing values, remove duplicates, type conversion, outlier treatment
# 3. Data splitting: Split into multiple thematic files by business logic, time dimension, geographic region, etc.
# 4. Metric calculation: Descriptive statistics, group aggregation, derived metrics, advanced analysis
# 5. Data output: CSV format to cleaned_data directory
df.to_csv(os.path.join(OUTPUT_DIR, "cleaned_data.csv"), index=False, encoding='utf-8')
if __name__ == "__main__":
main()
Data cleaning core principles:
- You may write CSV into
cleaned_data only by executing the data_cleaning.py script
- Run cleaning on the full dataset
- Do not modify or overwrite user-uploaded source files; read-only access only
- All output files must use UTF-8 encoding and CSV format
- Handle missing values explicitly (fill, drop, or flag as appropriate)
- Convert data types as needed (e.g. strings to numeric, date parsing)
- Remove duplicate rows before output
- Split data into multiple thematic files by business logic where beneficial; each file serves one or more cards
- Derived metrics (ratios, growth rates, rankings) should be computed in this script
1---2name: develop-data-analysis-dashboard3description: Data analysis dashboard (instrument panel) development skill. Use when users need to develop data dashboards, create/edit Dashboard projects, build large-screen data boards, or perform dashboard data cleaning. Includes dashboard project creation, card plan, data cleaning (data_cleaning.py), card management tools (create_dashboard_cards, update_dashboard_cards, delete_dashboard_cards, query_dashboard_cards), map download tool (download_dashboard_maps), dashboard development, and validation.4---5
6# Data Analysis Dashboard Development Skill
7
8Provides full data analysis dashboard (instrument panel) development capabilities—project creation, card plan, data cleaning, dashboard development, validation, and delivery—as one end-to-end workflow. Data cleaning is an important part of dashboard development.
9
10---
11
12## Code Execution Method
13
14All tool calls via `from sdk.tool import tool` in this skill must be executed by passing code to `run_sdk_snippet`'s `python_code` parameter.
15
16When a skill snippet calls tools through the SDK, always use `result.ok` to determine success and read failure details from `result.content`. Do not read or call `result.error`, because SDK `Result` stores failure text in `content`.
17
18---
19
20## Quick Start
21
22**Important**: Detailed rules are inlined later in this document; while executing steps, consult Workflow Summary, Project Setup, Dashboard Development, and Dashboard Data Cleaning Guide.
23
24**Runtime base files**: Do not modify, overwrite, or delete `index.html`, `dashboard.js`, or `index.css`; violations make the data dashboard unusable.
25
26**Default card counts for new dashboards — MANDATORY unless the user explicitly requests a reduced scope:**
27
28| Card type | Required count | Notes |
29| --------- | -------------- | ---------------------------------------------------------------------------- |
30| metric | **≥ 6** | Key KPI overview cards |
31| echarts | **26 – 30** | Core of the dashboard; pad with same-type charts across different dimensions |
32| table | **2 – 3** | At least 1 must be a detail-level table |
33| markdown | 0 | Omit unless the user asks for notes/commentary |
34| **Total** | **34 – 39** | Self-check before delivery |
35
36**Enforcement rules (non-negotiable):**
37
38- You **must** list every card in `cards_plan` before calling `create_dashboard_project`; do not defer cards to later.
39- `card_id` in `cards_plan` must exactly match the `id` used in subsequent `create_dashboard_cards` calls.
40- If echarts count falls short, add same-type charts covering different dimensions or time ranges — do **not** lower the floor.
41- Before delivery, call `query_dashboard_cards` and count by `type`; if any type is below the required count, create the missing cards before proceeding.
42- These counts are overridden **only** when the user explicitly states they want a smaller dashboard.
43
44---
45
46## Workflow Summary
47
48**Path overview**
49
50- **New dashboard**: Planning prep (brainstorm + read sources) → Create dashboard project → Data cleaning → Dashboard development → Validate → Complete delivery
51- **Edit dashboard**: Project identification → Data cleaning (as needed) → Dashboard editing and card-tool maintenance → Validate → Complete delivery
52
53**Step details**
54
55- **Project identification**: Understand user needs and identify the target project (edit scenarios)
56- **Planning prep (new dashboards)**: Before `create_dashboard_project`, brainstorm questions and angles; **read sources in depth** (fields, grain, definitions, time, distributions, missingness, comparable dimensions), then author `cards_plan` strictly following the **mandatory counts in Quick Start** (metric ≥6, echarts 26–30, table 2–3, total 34–39)—**every card must be listed before project creation**; a handful of representative charts is not acceptable
57- **Create project**: Must call `create_dashboard_project` with the required `cards_plan` in the same call; the tool writes `cards_plan.md` from it; plan card identifiers must match `id` in later `create_dashboard_cards`
58- **Data cleaning**: From `cards_plan.md` and data goals, create and run `data_cleaning.py` in the project to supply data for the dashboard
59- **Dashboard editing**: Change allowed files as needed (e.g. data_cleaning.py, cleaned_data/, config.js; never modify or delete index.html, dashboard.js, index.css) (edit scenarios)
60- **Dashboard development**: Per `cards_plan.md`, use card tools (create_dashboard_cards, update_dashboard_cards, delete_dashboard_cards, query_dashboard_cards) to create or maintain cards
61- **Validate dashboard**: Call `validate_dashboard(project_path="PROJECT_NAME")`; fix errors and re-run until passing (do not delete cards to pass validation)
62- **Complete delivery**: Summarize the project and analysis results, then close the task
63
64**Core principles**
65
66Follow the steps in order; validation must pass before delivery or the page will not work.
67Never modify, overwrite, or delete `index.html`, `dashboard.js`, or `index.css`, or the data dashboard becomes unusable (maintain allowed files such as `data.js` only via card tools and other permitted paths).
68
69**Preferences**
70
71- New dashboards: **planning prep** first, then `cards_plan`; **strictly follow the mandatory card counts table in Quick Start** — metric ≥6, echarts 26–30, table 2–3, total 34–39; self-check with `query_dashboard_cards` before delivery
72- Prefer setting `title` on cards (skip on metric cards when it would duplicate the metric `label`)
73- Prefer ECharts; many charts → same-type charts across dimensions
74- Card standard size examples (24-column grid): metric `{w:4,h:3}`, chart `{w:8,h:8}`, table `{w:12,h:8}`, Markdown `{w:12,h:(calculate height based on content)}`
75
76---
77
78## Decision Tree
79
80New or edit dashboard?
81├─ New → Planning prep (brainstorm, read sources) → Author cards_plan [metric ≥6, echarts 26–30, table 2–3, total 34–39, ALL cards listed] → create_dashboard_project → Data cleaning → Dashboard development → Validate → Complete delivery
82└─ Edit → Identify existing dashboard project → Data cleaning (as needed) → Dashboard development/editing → Validate → Complete delivery
83
84Need data cleaning?
85├─ New dashboard → Must execute data_cleaning.py
86├─ Edit dashboard with data/requirement changes → Execute as needed
87└─ Edit dashboard without changes → Can skip
88
89Card count check before delivery?
90├─ call query_dashboard_cards → count by type
91├─ metric < 6 or echarts < 26 or table < 2 → create missing cards first
92└─ counts met → proceed to validate
93
94Validation failed? → Fix issues and re-run validate_dashboard until result.ok with no errors
95
96---
97
98## Core Tools
99
100### create_dashboard_project — Create Dashboard Project
101
102| Param | Required | Type | Description |
103| ------------ | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104| `name` | Yes | string | Project name; the dashboard is created under this directory in the workspace |
105| `cards_plan` | Yes | array | Card plan list (at least one item); the tool generates `cards_plan.md` from this; field definitions and how to author entries are in the tool usage example below |
106
107Finish **Planning prep**, then author `cards_plan`. For new dashboards, the mandatory per-type counts are defined in Quick Start under **Default card counts for new dashboards** (metric ≥6, echarts 26–30, table 2–3, total 34–39). **You must list every card in `cards_plan` before calling this tool — partial plans are not allowed.** Only if the user explicitly requests a reduced dashboard may you go below these counts; otherwise every card must be listed upfront. You may call `create_dashboard_cards` in batches after project creation. This tool does not validate counts — it is your responsibility to meet them.
108
109**Return (`result`)**: On success `result.ok` is true and `result.content` includes the created path and related information; on failure `result.ok` is false and `result.content` contains the error (e.g. "Directory already exists").
110
111### validate_dashboard — Validate Dashboard
112
113| Param | Required | Type | Description |
114| -------------- | -------- | ------ | ------------------------------------------------------------ |
115| `project_path` | Yes | string | Dashboard project directory path, relative to workspace root |
116
117**Return (`result`)**: On success `result.ok` is true with no errors; on failure `result.ok` is false and `result.content` contains validation failure reasons. **Validation must pass before delivery; otherwise the page will not be accessible.**
118
119### Card Management Tools
120
121You must use the card management tools to work on `data.js`; direct editing is strictly prohibited. Card fields and CardData are defined under Card Data DSL below; layout workflow and validation are under Dashboard Development.
122
123| Tool | Description |
124| ---------------------- | -------------------------------------------------------------------------- |
125| create_dashboard_cards | Batch create; prefer ≤6 `cards`/call; `auto_layout` (may omit `layout`) |
126| update_dashboard_cards | Batch update; 1–10 `updates`/call, prefer ≤6; single-field edits supported |
127| delete_dashboard_cards | Batch delete; `card_ids` 1–20; auto-compacts layout after delete |
128| query_dashboard_cards | Query all or by id; optional `fields` to filter returned properties |
129
130**create_dashboard_cards**
131
132`auto_layout` (default false): When true, omit per-card `layout`; the tool generates gap-free layout by type order to reduce validate churn. Recommend `auto_layout=true` for new dashboards. Prefer about 6 or fewer `cards` per call when feasible (not a hard cap).
133
134| Param | Required | Type | Description |
135| -------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------ |
136| `project_path` | Yes | string | Dashboard project path |
137| `cards` | Yes | List | Card list; each item has id, type, source, getCardData; when auto_layout=true, `layout` may be omitted |
138| `auto_layout` | No | boolean | When true, omit `layout` and let the tool fill the grid |
139
140**update_dashboard_cards**
141
142Each update must include `id` and at least one other field to change (type, source, title, titleAlign, layout, getCardData, etc.). `layout` accepts partial objects (e.g. only `y`).
143
144| Param | Required | Type | Description |
145| -------------- | -------- | ------ | ---------------------------------------------- |
146| `project_path` | Yes | string | Dashboard project path |
147| `updates` | Yes | List | Update objects, 1–10 items; prefer ≤6 per call |
148
149**delete_dashboard_cards**
150
151| Param | Required | Type | Description |
152| -------------- | -------- | --------- | ---------------------------------------- |
153| `project_path` | Yes | string | Dashboard project path |
154| `card_ids` | Yes | List[str] | IDs to delete, 1–20 items, no duplicates |
155
156**query_dashboard_cards**
157
158| Param | Required | Type | Description |
159| -------------- | -------- | --------- | ---------------------------------------------------------------------------------------------- |
160| `project_path` | Yes | string | Dashboard project path |
161| `card_ids` | No | List[str] | Omit for all cards; if set, 1–20 IDs |
162| `fields` | No | List[str] | Omit for all fields; else a subset of id, type, title, source, layout, titleAlign, getCardData |
163
164---
165
166## Tool Usage Example
167
168```python
169# Create dashboard project (see structure below)
170# Abbreviated example: shows one cards_plan row shape; real new dashboards must list the full per-type plan per the skill.
171result = tool.call('create_dashboard_project', {
172 "name": "Sales Data Dashboard",
173 "cards_plan": [
174 {
175 "display_name": "Total Sales",
176 "card_id": "total_sales",
177 "type": "metric",
178 "data_detail": "Sum of sales amount",
179 },
180 {
181 "display_name": "Monthly Sales Trend",
182 "card_id": "monthly_trend",
183 "type": "echarts",
184 "data_detail": "Sales by month",
185 },
186 ],
187})
188
189if result.ok:
190 # Read project path and other info from result.content
191 pass
192else:
193 # result.content explains failure, e.g. "Directory already exists"
194 pass
195
196# create_dashboard_cards: auto_layout=True; prefer ≤6 cards/call
197result = tool.call('create_dashboard_cards', {
198 "project_path": "Sales Data Dashboard",
199 "auto_layout": True,
200 "cards": [...]
201})
202
203# query_dashboard_cards: omit card_ids for all cards; optional fields, card_ids (1–20)
204result = tool.call('query_dashboard_cards', {
205 "project_path": "Sales Data Dashboard",
206})
207
208# update_dashboard_cards: each update needs id and ≥1 other field; prefer ≤6 updates/call
209result = tool.call('update_dashboard_cards', {
210 "project_path": "Sales Data Dashboard",
211 "updates": [...]
212})
213
214# delete_dashboard_cards: card_ids 1–20; do not delete to pass validation
215result = tool.call('delete_dashboard_cards', {
216 "project_path": "Sales Data Dashboard",
217 "card_ids": [...]
218})
219
220# validate_dashboard: delivery requires result.ok with no errors
221result = tool.call('validate_dashboard', {"project_path": "Sales Data Dashboard"})
222if not result.ok:
223 # Fix issues from result.content, then validate again
224 pass
225
226# download_dashboard_maps (when needed)
227result = tool.call('download_dashboard_maps', {
228 "project_path": "Sales Data Dashboard",
229 "area_names": ["China", "Guangdong", "Shenzhen"]
230})
231```
232
233---
234
235## File Naming Rules
236
237File and directory naming intelligently determined based on file content, business domain, and user preferred language, e.g.:
238
239- User preferred language is Chinese: "Sales Data Dashboard" (in Chinese), "sales_data.csv" (in Chinese)
240- User preferred language is English: "Sales Data Dashboard", "Sales Data.csv"
241
242---
243
244## Key Constraints
245
246- Do not generate any images with Python scripts (matplotlib, seaborn, plotly, etc.); implement all charts with ECharts
247- Do not modify data source files; read-only access only
248- You must use the card management tools (create_dashboard_cards, update_dashboard_cards, delete_dashboard_cards) for those operations
249- Temporary files start with temp\_, must delete before task end
250- File naming determined by content, business domain, user preferred language
251- `data.js` must be maintained only through those card tools; direct edits or overwriting `data.js` are prohibited
252- Never modify, overwrite, or delete `index.html`, `dashboard.js`, or `index.css`; any change or removal makes the data dashboard unusable
253
254---
255
256### Data Sources
257
258Data source role: Provide foundational data support for data analysis dashboard development.
259Supported type examples: Excel, CSV, JSON, plain text, PDF, web-sourced data, MCP tool data.
260Operating rule: Do not change user-uploaded source files; read-only.
261
262Data source identification and validation:
263
2641. Understand the request; identify the source type and content.
2652. If data comes from the web or MCP tools, persist it to a JSON file before analysis.
2663. Source inspection—if any of the following applies, enter the exception-handling flow immediately: unreadable source; unsupported format; empty template; headers only; no valid business data; unacceptably poor data quality.
2674. Exception-handling flow: Tell the user why and end the task.
268
269---
270
271### Project Structure, Files, and Editing Rules
272
273The tree matches the table: path, purpose, and editing rules for each item.
274
275```
276Project Directory/
277├── geo/ # Map GeoJSON data
278├── cleaned_data/ # Cleaned data (CSV)
279├── data_cleaning.py # Data cleaning script
280├── data.js # Card config (DASHBOARD_CARDS)
281├── config.js # Global config (colors, themes, etc.)
282├── index.html # Page markup (system-managed; do not modify or delete)
283├── index.css # Styles (system-managed; do not modify or delete)
284├── dashboard.js # Dashboard runtime (system-managed; do not modify or delete)
285└── magic.project.js # Project configuration file
286```
287
288| Path | Purpose | Editing rules |
289| ---------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
290| geo/ | GeoJSON for maps | System-managed; do not edit |
291| cleaned_data/ | Cleaned CSV for card data sources | Editable: add or modify files |
292| data_cleaning.py | Raw data → cleaned_data/ | Editable: adjust cleaning logic as needed |
293| data.js | DASHBOARD_CARDS definitions | Tools only: create_dashboard_cards, update_dashboard_cards, delete_dashboard_cards; no direct file edits |
294| config.js | Global colors, themes, fonts, etc. | Restricted: field values only; do not add, remove, rename fields, or change structure |
295| index.html | Page HTML | System-managed; do not edit, delete, or overwrite or the data dashboard becomes unusable |
296| index.css | Visual styling | System-managed; do not edit, delete, or overwrite or the data dashboard becomes unusable |
297| dashboard.js | Rendering, loading, charts | System-managed; do not edit, delete, or overwrite or the data dashboard becomes unusable |
298| magic.project.js | Project metadata | Prohibited: system-managed |
299
300---
301
302### Card Data DSL
303
304Card Basic Structure:
305
306- id: String, card unique identifier (required)
307- type: Card type, strictly follow CardType types (required)
308- source: String, data source path, e.g. "./cleaned_data/filename.csv" (required)
309- layout: react-grid-layout layout object, contains {x: integer, y: integer, w: integer, h: integer} (required)
310- getCardData: Async function, used to load data and process card data, returns CardData (required)
311- title: Optional string, card title
312- titleAlign: Optional string, title alignment ("left"|"center"|"right")
313
314CardType Card Types:
315
316- metric: Single metric card, displays metric value
317- table: Data table card, displays structured data
318- markdown: Markdown document card
319- echarts: ECharts chart card
320
321CardData Data Structure Specification:
322
323- MetricCard (Metric card):
324 - label: String, metric name (required)
325 - value: String or number, metric value (required)
326 - change: Optional string, change value or percentage
327 - unit: Optional string, unit
328 - icon: Optional string, use icon name from tabler-icon, e.g., "ti-chart-bar"
329 - iconColor: Optional string, icon color; required whenever `icon` is set
330- TableCard (Table card):
331 - columns: Column config array, each item contains:
332 - title: String, column title
333 - dataIndex: String, data field name
334 - dataType: Optional, data type ("string"|"number"|"date"|"time"), default "string"
335 - width: Optional, column width (string or number)
336 - formatter: Optional, column formatter function to customize how a cell is shown; parameter `value` is the current cell value as a string; return value must be a string; plain text only
337 - sortable: Optional, boolean, whether to enable sorting
338 - filterable: Optional, boolean, whether to enable filtering
339 - data: Data array, each item is object containing each column's data (use raw data as much as possible, then format via formatter function)
340- MarkdownCard (Markdown card):
341 - content: String, Markdown format text content (required)
342- echarts (Chart card): ECharts Options (version: v6.0.0) config
343
344getCardData Data Loading Function:
345
346- Async function, parameter is csv object, returns data conforming to CardData specification
347- Core methods:
348 - `csv.load("filename")` loads CSV file in cleaned_data directory (without .csv extension)
349 - Returns: `{data: row array, fields: column name array, name: filename, url: path}`
350- Key specifications:
351 - Field access: Use `row["field_name"]`, avoid special character issues
352 - Data conversion: `parseFloat(row["field_name"])` converts string to number, CSV data defaults to string
353 - Numeric processing: Avoid floating point precision issues, use `Math.round()` or `.toFixed()` when necessary; percentages use `.toFixed(2)`, amounts use `.toLocaleString()`
354 - Theme config access: Before using `window.DASHBOARD_CONFIG`, read `config.js` to confirm real key names and value types; example: `window.DASHBOARD_CONFIG.COLORS_PRIMARY`
355
356#### Examples
357
358```javascript
359// MetricCard example
360getCardData: async (csv) => {
361 const result = await csv.load("sales_data");
362 const totalSales = result.data.reduce((sum, row) => {
363 return sum + parseFloat(row["sales_amount"]);
364 }, 0);
365 return {
366 label: "Total Sales",
367 value: totalSales,
368 unit: "USD",
369 icon: "ti-currency-dollar",
370 };
371};
372
373// TableCard example
374getCardData: async (csv) => {
375 const result = await csv.load("sales_data");
376 return {
377 columns: [
378 {
379 title: "Region",
380 dataIndex: "region",
381 dataType: "string",
382 sortable: false,
383 filterable: false,
384 },
385 {
386 title: "Sales",
387 dataIndex: "sales",
388 dataType: "number",
389 sortable: true,
390 filterable: false,
391 formatter: (value) => `$${parseFloat(value).toLocaleString()}`,
392 },
393 {
394 title: "Customers",
395 dataIndex: "customers",
396 dataType: "number",
397 sortable: true,
398 filterable: false,
399 },
400 ],
401 data: result.data,
402 };
403};
404
405// ECharts chart example
406getCardData: async (csv) => {
407 const result = await csv.load("sales_data");
408 return {
409 grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false },
410 tooltip: {
411 trigger: "axis",
412 formatter: function (params) {
413 return (
414 params[0].name + ": " + params[0].value.toLocaleString() + " USD"
415 );
416 },
417 },
418 xAxis: {
419 type: "category",
420 data: result.data.map((row) => row["region"]),
421 },
422 yAxis: {
423 type: "value",
424 axisLabel: { formatter: (value) => value.toLocaleString() },
425 },
426 series: [
427 {
428 type: "bar",
429 data: result.data.map((row) => parseFloat(row["sales"])),
430 label: {
431 show: true,
432 formatter: (params) => params.value.toLocaleString(),
433 },
434 },
435 ],
436 };
437};
438
439// MarkdownCard example
440getCardData: async (csv) => {
441 const salesData = await csv.load("sales_base_data");
442 const productData = await csv.load("product_sales_ranking");
443 const totalSales = salesData.data.reduce(
444 (sum, row) => sum + parseFloat(row["sales_amount"]),
445 0,
446 );
447 const topProduct = productData.data.sort(
448 (a, b) => parseFloat(b["sales_amount"]) - parseFloat(a["sales_amount"]),
449 )[0];
450 return {
451 content: `### Sales Analysis Report\n\n**Total Sales**: ${totalSales.toLocaleString()} USD\n**Top Product**: ${topProduct["product_name"]}`,
452 };
453};
454```
455
456Data processing example (same CSV shape as the English examples above):
457
458```javascript
459// CSV file: sales_data.csv
460// Example content:
461// region,sales,customers
462// East,120000,150
463// South,95000,120
464
465const parsedData = [
466 { region: "East", sales: "120000", customers: "150" },
467 { region: "South", sales: "95000", customers: "120" },
468];
469const parsedFields = ["region", "sales", "customers"];
470```
471
472---
473
474### Appearance and Layout
475
476Card Actual Size Calculation Logic:
477
478- Width: Based on GRID_COLS column grid system in config.js
479- Height: Card height = card rows × card row height (GRID_DEFAULT_ROW_HEIGHT in config.js)
480
481UI theme customization (only when the user explicitly asks): `config.js` (global theme).
482
483Card Layout:
484
485- Hierarchical arrangement: Metric cards (top overview) → Chart cards (core analysis) → Table cards (detailed data) → Markdown cards (notes)
486- Layout principles: Must fully utilize (GRID_COLS value in config.js) column grid system, horizontal-vertical complementary fill, compact continuous filling with no gaps, coordinated width-height ratio
487
488---
489
490### ECharts v6.0.0 Configuration
491
492**Dashboard development only**
493
494ECharts v6.0.0 key settings:
495
496- Map: series.map uses Chinese region names from GeoJSON, e.g. "China", "Guangdong", "Shenzhen" (pass the exact names from downloaded GeoJSON); series.nameProperty is "fullname"
497- grid:
498 - Mandatory config: `{ left: 0, right: 0, top: 0, bottom: 0, containLabel: false }`, in v6.0.0 this config already allows axis and axis labels to display fully edge-aligned, so no need to reserve any space for XY axis labels or axis titles
499 - outerBounds:
500 - Use case: Reserve space for legend, visualMap components
501 - Use condition: Only need to set when legend, visualMap components configured
502 - Core principle: outerBounds direction must match component position direction
503 - Config examples:
504 - Bottom horizontal legend: `{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { bottom: 30 } }, legend: { type: "scroll", bottom: 0 } }`
505 - Left vertical legend: `{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { left: 50 } }, legend: { type: "scroll", orient: "vertical", left: 0 } }`
506 - Bottom horizontal visualMap: `{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { bottom: 50 } }, visualMap: { orient: "horizontal", bottom: 0, left: "center" } }`
507 - Left bottom vertical visualMap: `{ grid: { left: 0, right: 0, top: 0, bottom: 0, containLabel: false, outerBounds: { left: 50 } }, visualMap: { orient: "vertical", left: 0, bottom: 0 } }`
508 - Wrong examples:
509 - `outerBounds: { right: 120 }, legend: { orient: "vertical", right: 0 }` (legend should be on left)
510 - `outerBounds: { left: 60 }` but no left component configured (meaningless space reservation)
511 - `outerBounds: { top: 30 }` but component at bottom (direction mismatch)
512- legend:
513 - Use condition: Only configure when multi-series or pie charts need legend, single-series charts don't need legend
514 - Recommended config: Horizontal legend use `{ bottom: 0, type: "scroll" }`, vertical legend use `{ left: 0, orient: "vertical", type: "scroll" }`
515 - Avoid using right, top positions, prioritize bottom, left positions
516- visualMap:
517 - visualMap recommended config: Horizontal use `{ orient: "horizontal", bottom: 0, left: "center" }`, vertical use `{ orient: "vertical", left: 0, bottom: 0 }`
518 - Map visualMap suggest using vertical direction: `{ orient: "vertical", left: 0, bottom: 0 }` + `outerBounds: { left: 50 }`
519- tooltip: Configure a tooltip for every graphic/series where it helps
520- dataZoom: Strictly do not configure `dataZoom`; ECharts `dataZoom` is visually poor and its use is not recommended here
521- label: `label.formatter` receives a `params` object; use `params.value` for the numeric value; set `labelLayout.hideOverlap: true` where needed to reduce overlap; consider font stroke for readability
522- title: Do not set an ECharts `title` in options (duplicates the card title)
523- axis: Prefer a `name` on numeric value axes; avoid `name` on category axes. Prefer the vertical layout pattern: Y = value axis, X = category axis
524- Use the chart area fully; avoid unnecessary empty margins
525- Formatter arguments: `label.formatter` and `tooltip.formatter` take `params`; `axisLabel.formatter` takes `value`
526- Keep theming consistent with `config.js`; read `config.js` before using `window.DASHBOARD_CONFIG` to confirm key names and value types
527
528---
529
530### Card Management Tools — Quick Reference
531
532Parameter tables, `auto_layout`, and batch limits are under Core Tools, Card Management Tools above. Typical usage (pick as needed):
533
534- Create: `create_dashboard_cards`; ≤6/call, `auto_layout=True`
535- Edit: `update_dashboard_cards`; ≤6 updates/call; partial fields OK (e.g. title, `layout.y`)
536- Delete: `delete_dashboard_cards`; do not delete to pass validation
537- Browse: `query_dashboard_cards` without `card_ids` (and optional `fields`) for a quick full summary
538- Detail: same tool with `card_ids` for full config including getCardData
539- Trim payload: `query_dashboard_cards` with `fields` (e.g. id, type, layout only)
540
541---
542
543## Dashboard Data Cleaning Guide
544
545- Script file: data_cleaning.py
546- Script example:
547
548```python
549import os
550import pandas as pd
551
552# Required statements (strictly follow this format)
553PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
554OUTPUT_DIR = os.path.join(PROJECT_ROOT, "cleaned_data")
555os.makedirs(OUTPUT_DIR, exist_ok=True)
556
557# Data source definition (if file data sources exist)
558FILE_DATA_SOURCES = {
559 'main_data': os.path.join(PROJECT_ROOT, "..", "data_source.csv"),
560 'additional_data': os.path.join(PROJECT_ROOT, "..", "additional_data.csv")
561}
562
563def main():
564 # 1. Data loading
565 df = pd.read_csv(FILE_DATA_SOURCES['main_data'])
566
567 # 2. Data cleaning: Handle missing values, remove duplicates, type conversion, outlier treatment
568
569 # 3. Data splitting: Split into multiple thematic files by business logic, time dimension, geographic region, etc.
570
571 # 4. Metric calculation: Descriptive statistics, group aggregation, derived metrics, advanced analysis
572
573 # 5. Data output: CSV format to cleaned_data directory
574 df.to_csv(os.path.join(OUTPUT_DIR, "cleaned_data.csv"), index=False, encoding='utf-8')
575
576if __name__ == "__main__":
577 main()
578```
579
580Data cleaning core principles:
581
582- You may write CSV into `cleaned_data` only by executing the `data_cleaning.py` script
583- Run cleaning on the full dataset
584- Do not modify or overwrite user-uploaded source files; read-only access only
585- All output files must use UTF-8 encoding and CSV format
586- Handle missing values explicitly (fill, drop, or flag as appropriate)
587- Convert data types as needed (e.g. strings to numeric, date parsing)
588- Remove duplicate rows before output
589- Split data into multiple thematic files by business logic where beneficial; each file serves one or more cards
590- Derived metrics (ratios, growth rates, rankings) should be computed in this script