Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- __init__.py +0 -0
- agent.md +60 -0
- allowed_chart_types.json +17 -0
- allowed_chart_types.json.example +19 -0
- batch_run.py +28 -0
- extracted_info.json +0 -0
- passed_templates.txt +49 -0
- passed_templates_audit.csv +51 -0
- passed_templates_ever_pass.txt +50 -0
- passed_templates_final_status_contains_pass.txt +50 -0
- pipeline.py +630 -0
- record.txt +0 -0
- requirements.txt +15 -0
- setup.sh +100 -0
__init__.py
ADDED
|
File without changes
|
agent.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Agent Notes
|
| 2 |
+
|
| 3 |
+
- Use the `edit` conda environment for ChartPipeline commands:
|
| 4 |
+
`/data/wangzheng/miniconda3/envs/edit/bin/python`.
|
| 5 |
+
- When invoking Python commands directly, prefer:
|
| 6 |
+
`PYTHONPATH=. /data/wangzheng/miniconda3/envs/edit/bin/python ...`.
|
| 7 |
+
- For rendering, set:
|
| 8 |
+
`PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome` and `RENDER_LONGEST_SIDE=1600`.
|
| 9 |
+
- For full pipeline runs in a non-login shell, include both runtime dirs in PATH:
|
| 10 |
+
`export PATH=/data/wangzheng/miniconda3/envs/edit/bin:/data/wangzheng/miniconda3/envs/edit/lib/python3.12/site-packages/playwright/driver:$PATH`.
|
| 11 |
+
- The post-generation icon/image module is `modules.asset_regenerator.asset_regenerator`.
|
| 12 |
+
It runs after `infographics_generator`, builds one contact sheet containing the full infographic
|
| 13 |
+
plus all selected SVG `<image>` assets, calls `gpt-image-2`, chroma-keys the generated cells,
|
| 14 |
+
and replaces only the original SVG `<image href>` / `xlink:href` values with data URI PNGs.
|
| 15 |
+
- During variation repair, do not call `gpt-image-2` while iterating on template/data fixes.
|
| 16 |
+
First regenerate with `--modules infographics_generator` only, or run asset regeneration with
|
| 17 |
+
`--dry-run` if candidate discovery needs checking. Run the real `asset_regenerator` only after
|
| 18 |
+
the template-side repair passes structural/rendered validation.
|
| 19 |
+
- Keep before/after evidence for each repaired variation. Preserve the original sample SVG/PNG or
|
| 20 |
+
render a baseline from the unmodified template, then store side-by-side comparison artifacts
|
| 21 |
+
under an `output/chart_repair_comparison_*` directory.
|
| 22 |
+
- Keep complete code backups for every full variation-repair or style-reference iteration. Before
|
| 23 |
+
editing a variation, create a timestamped backup directory under `backups/` and include every
|
| 24 |
+
variation template plus every support file that may be changed in the iteration, such as docs,
|
| 25 |
+
agent notes, pipeline modules, generator utilities, asset-regenerator code, and helper scripts.
|
| 26 |
+
- Each complete iteration backup must keep three separate trees:
|
| 27 |
+
- `before_head`: the original repair-before version from `HEAD:<path>` for tracked files, or a
|
| 28 |
+
missing marker for files added during repair work.
|
| 29 |
+
- `before`: the exact workspace state immediately before the iteration edits begin.
|
| 30 |
+
- `after`: the exact workspace state after patching, regenerating, verifying, and documenting the
|
| 31 |
+
iteration.
|
| 32 |
+
- Never overwrite an older backup tree. If a file was already modified before the next iteration,
|
| 33 |
+
preserve that pre-edit copy in `before` so the new iteration can be separated from earlier repairs.
|
| 34 |
+
For untracked support files, write a missing marker in `before_head`, copy the pre-edit file into
|
| 35 |
+
`before`, and copy the final file into `after`.
|
| 36 |
+
- Every backup directory must include `files.tsv`, `manifest.md`, pre/post `git status --short`,
|
| 37 |
+
relevant `git diff --stat`, generated output paths, comparison evidence paths, verification
|
| 38 |
+
evidence paths, and whether `gpt-image-2` or `asset_regenerator` was called. The manifest must make
|
| 39 |
+
clear which tree is the repair-before code and which tree is the current/final code.
|
| 40 |
+
- Run it in the pipeline with:
|
| 41 |
+
`--modules infographics_generator asset_regenerator`.
|
| 42 |
+
- To test without calling the image model:
|
| 43 |
+
`PYTHONPATH=. /data/wangzheng/miniconda3/envs/edit/bin/python -m modules.asset_regenerator.asset_regenerator --svg <final.svg> --png <final.png> --dry-run`.
|
| 44 |
+
- The image edit call defaults to `--model gpt-image-2 --input-fidelity high` so the model sees
|
| 45 |
+
the rendered infographic as context while only regenerating the green contact-sheet asset cells.
|
| 46 |
+
- Keep this module scoped to decorative icon/image assets. Do not use it to raster-overwrite the
|
| 47 |
+
chart, title, labels, axes, legends, data values, chart marks, or background unless explicitly
|
| 48 |
+
passing `--include-background` for a controlled experiment.
|
| 49 |
+
- For style-reference iterations, compare the current baseline for a variation against existing
|
| 50 |
+
PNGs under `../chart_template_samples_20260527_083945/gpt_image_2_improved/<variation>`. If the
|
| 51 |
+
variation was repaired in `docs/variation_repair_status.md`, use the repaired output as the
|
| 52 |
+
baseline; otherwise use `../data/all_variations_check/template_variations_20260527/<variation>`.
|
| 53 |
+
Choose one simple, readable, low-decoration GPT-improved reference as the style anchor, but do
|
| 54 |
+
not copy its chart geometry or numeric values when they conflict with source data.
|
| 55 |
+
- During style-reference iterations, do not call `gpt-image-2`; existing GPT-improved PNGs are
|
| 56 |
+
references only. Keep title/icon/image generation on the existing pipeline path for now.
|
| 57 |
+
- For every style-reference iteration, keep a timestamped code backup under `backups/`, regenerate
|
| 58 |
+
5 outputs, write a baseline/reference/after comparison under `output/style_ref_comparison_*`,
|
| 59 |
+
verify chart type and data mappings structurally, and record the iteration in
|
| 60 |
+
`docs/style_reference_repair_status.md`.
|
allowed_chart_types.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"chart_types": [
|
| 3 |
+
"donut chart",
|
| 4 |
+
"pie chart",
|
| 5 |
+
"multiple donut chart",
|
| 6 |
+
"multiple pie chart",
|
| 7 |
+
"line graph",
|
| 8 |
+
"multiple line graph",
|
| 9 |
+
"spline graph",
|
| 10 |
+
"multiple spline graph",
|
| 11 |
+
"step line graph",
|
| 12 |
+
"stepped line graph",
|
| 13 |
+
"multiple step line graph",
|
| 14 |
+
"sorted lines chart",
|
| 15 |
+
"slope chart"
|
| 16 |
+
]
|
| 17 |
+
}
|
allowed_chart_types.json.example
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_comment": "Whitelist of chart_type names the infographics_generator is allowed to pick from.",
|
| 3 |
+
"_usage": [
|
| 4 |
+
"1) Copy this file to allowed_chart_types.json (same directory), OR",
|
| 5 |
+
" set ALLOWED_CHART_TYPES_FILE=<path> to point at a custom location.",
|
| 6 |
+
"2) Edit the 'chart_types' list below to the chart_type values you care about.",
|
| 7 |
+
"3) Empty list / missing file means 'no filtering' (legacy behaviour).",
|
| 8 |
+
"Granularity is chart_type (NOT chart_name). All chart_name variations whose",
|
| 9 |
+
"directory lives under the listed chart_type folder will become selectable.",
|
| 10 |
+
"Run `awk -F' / ' '{print $1}' template_list.txt | sort -u` to see the full set."
|
| 11 |
+
],
|
| 12 |
+
"chart_types": [
|
| 13 |
+
"bump chart",
|
| 14 |
+
"bar chart",
|
| 15 |
+
"line graph",
|
| 16 |
+
"bubble chart",
|
| 17 |
+
"scatterplot"
|
| 18 |
+
]
|
| 19 |
+
}
|
batch_run.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import subprocess
|
| 3 |
+
from multiprocessing import Pool
|
| 4 |
+
from tqdm import tqdm
|
| 5 |
+
|
| 6 |
+
def run_command(item):
|
| 7 |
+
data_source = item["data_source"]
|
| 8 |
+
chart_variation = item["chart_variation"]
|
| 9 |
+
|
| 10 |
+
cmd = [
|
| 11 |
+
"python", "pipeline.py",
|
| 12 |
+
"--modules", "infographics_generator",
|
| 13 |
+
"--input", data_source,
|
| 14 |
+
"--output", "./test_output_new_new",
|
| 15 |
+
"--chart-name", chart_variation
|
| 16 |
+
]
|
| 17 |
+
|
| 18 |
+
subprocess.run(cmd)
|
| 19 |
+
return item
|
| 20 |
+
|
| 21 |
+
start_id = 0
|
| 22 |
+
|
| 23 |
+
with open("extracted_info.json", "r") as f:
|
| 24 |
+
data = json.load(f)[start_id:start_id+1]
|
| 25 |
+
|
| 26 |
+
with Pool(processes=8) as pool:
|
| 27 |
+
list(tqdm(pool.imap(run_command, data), total=len(data)))
|
| 28 |
+
|
extracted_info.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
passed_templates.txt
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
bump_plain_chart_02
|
| 2 |
+
bump_plain_chart_03
|
| 3 |
+
circular_bar_chart_01
|
| 4 |
+
circular_bar_chart_03
|
| 5 |
+
circular_bar_chart_04
|
| 6 |
+
circular_bar_chart_icons_01
|
| 7 |
+
circular_bar_chart_icons_02
|
| 8 |
+
horizontal_bar_chart_0
|
| 9 |
+
horizontal_bar_chart_01
|
| 10 |
+
horizontal_bar_chart_0_weilai
|
| 11 |
+
horizontal_bar_chart_1
|
| 12 |
+
horizontal_bar_chart_10
|
| 13 |
+
horizontal_bar_chart_11
|
| 14 |
+
horizontal_bar_chart_12
|
| 15 |
+
horizontal_diverging_bar_chart_05
|
| 16 |
+
horizontal_diverging_bar_chart_07
|
| 17 |
+
horizontal_diverging_bar_chart_08
|
| 18 |
+
horizontal_diverging_bar_chart_10
|
| 19 |
+
horizontal_diverging_bar_chart_11
|
| 20 |
+
horizontal_diverging_bar_chart_12
|
| 21 |
+
line_graph_plain_chart_03
|
| 22 |
+
lollipop_icon_chart_01
|
| 23 |
+
lollipop_icon_chart_02
|
| 24 |
+
lollipop_plain_chart_01
|
| 25 |
+
multiple_gauge_chart_01
|
| 26 |
+
multiple_gauge_chart_02
|
| 27 |
+
multiple_gauge_chart_03
|
| 28 |
+
multiple_gauge_chart_03_hand
|
| 29 |
+
multiple_line_graph_01
|
| 30 |
+
proportional_area_chart_hexagon_01
|
| 31 |
+
proportional_area_chart_hexagon_02
|
| 32 |
+
proportional_area_chart_other_01
|
| 33 |
+
proportional_area_chart_pentagram_01
|
| 34 |
+
range_area_chart_icons_01
|
| 35 |
+
range_area_plain_chart_01
|
| 36 |
+
rose_chart_01
|
| 37 |
+
rose_chart_new_00
|
| 38 |
+
rose_chart_new_00_hand
|
| 39 |
+
rose_plain_chart_01
|
| 40 |
+
stacked_area_chart_01
|
| 41 |
+
stacked_area_chart_02
|
| 42 |
+
stacked_area_chart_03
|
| 43 |
+
stacked_area_chart_icons_01
|
| 44 |
+
stacked_area_plain_chart_02
|
| 45 |
+
vertical_group_bar_chart_12
|
| 46 |
+
vertical_group_bar_chart_13
|
| 47 |
+
vertical_group_bar_chart_14
|
| 48 |
+
vertical_group_bar_chart_15
|
| 49 |
+
vertical_group_bar_chart_16
|
passed_templates_audit.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
variation,final_status,final_stage,final_worker,final_line,pass_line,pass_stage,pass_worker,worker_verdict_is_provisional,controller_fresh_review_required,controller_review_required,review_report
|
| 2 |
+
bump_plain_chart_02,pass,controller_fresh_review_round6,controller,51,51,controller_fresh_review_round6,controller,,False,,data/output/variation_skill_runs/bump_plain_chart_02/20260603_220532/variation_review_round6_controller_fresh_pass.md
|
| 3 |
+
bump_plain_chart_03,pass,controller_fresh_review_round4,controller,124,124,controller_fresh_review_round4,controller,,False,,data/output/variation_skill_runs/bump_plain_chart_03/20260603_224758/variation_review_round4_controller_fresh_pass.md
|
| 4 |
+
circular_bar_chart_01,pass,controller_fresh_pass_round14,,277,277,controller_fresh_pass_round14,,,False,False,data/output/variation_skill_runs/circular_bar_chart_01/20260603_231601/variation_review_round14_controller_fresh_pass.md
|
| 5 |
+
circular_bar_chart_03,pass,controller_fresh_pass_round32,controller,542,542,controller_fresh_pass_round32,controller,,,,
|
| 6 |
+
circular_bar_chart_04,pass,controller_fresh_pass_round8,controller,580,580,controller_fresh_pass_round8,controller,,,,
|
| 7 |
+
circular_bar_chart_icons_01,pass,controller_fresh_pass_round5,controller,667,667,controller_fresh_pass_round5,controller,,,,
|
| 8 |
+
circular_bar_chart_icons_02,pass,worker_final_round5_provisional,session_01,724,724,worker_final_round5_provisional,session_01,,True,,
|
| 9 |
+
horizontal_bar_chart_0,pass,controller_fresh_review_round7,controller,60,60,controller_fresh_review_round7,controller,,False,,data/output/variation_skill_runs/horizontal_bar_chart_0/20260603_215905/variation_review_round7_controller_fresh_pass.md
|
| 10 |
+
horizontal_bar_chart_01,pass,,,192,192,,,True,True,True,data/output/variation_skill_runs/horizontal_bar_chart_01/20260603_225157/variation_review_round6.md
|
| 11 |
+
horizontal_bar_chart_0_weilai,pass,controller_fresh_pass_round2,,299,299,controller_fresh_pass_round2,,,False,False,data/output/variation_skill_runs/horizontal_bar_chart_0_weilai/20260603_235404/variation_review_round2_controller_fresh_pass.md
|
| 12 |
+
horizontal_bar_chart_1,pass,controller_fresh_pass_round6,session_02,408,408,controller_fresh_pass_round6,session_02,,False,,
|
| 13 |
+
horizontal_bar_chart_10,pass,controller_fresh_pass_round14,controller,534,534,controller_fresh_pass_round14,controller,,,,
|
| 14 |
+
horizontal_bar_chart_11,pass,controller_fresh_pass_round7,controller,581,581,controller_fresh_pass_round7,controller,,,,
|
| 15 |
+
horizontal_bar_chart_12,pass,worker_final_round12_provisional,session_02,688,688,worker_final_round12_provisional,session_02,,True,,data/output/variation_skill_runs/horizontal_bar_chart_12/20260604_051705/variation_review_round12.md
|
| 16 |
+
horizontal_diverging_bar_chart_05,pass,worker_provisional_pass_round16_controller_review_required,session_03,218,218,worker_provisional_pass_round16_controller_review_required,session_03,,True,True,data/output/variation_skill_runs/horizontal_diverging_bar_chart_05/20260603_202638/variation_review_round16.md
|
| 17 |
+
horizontal_diverging_bar_chart_07,pass,controller_fresh_pass_round12,session_03,388,388,controller_fresh_pass_round12,session_03,,False,,
|
| 18 |
+
horizontal_diverging_bar_chart_08,pass,controller_fresh_pass_round12,controller,522,522,controller_fresh_pass_round12,controller,,,,
|
| 19 |
+
horizontal_diverging_bar_chart_10,pass,worker_provisional_pass_round6_controller_required,session_03,576,576,worker_provisional_pass_round6_controller_required,session_03,True,True,,data/output/variation_skill_runs/horizontal_diverging_bar_chart_10/20260604_033039/variation_review_round6.md
|
| 20 |
+
horizontal_diverging_bar_chart_11,pass,controller_fresh_pass_round8,controller,652,652,controller_fresh_pass_round8,controller,,,,
|
| 21 |
+
horizontal_diverging_bar_chart_12,pass,worker_final_round5_provisional,session_03,705,705,worker_final_round5_provisional,session_03,,True,,data/output/variation_skill_runs/horizontal_diverging_bar_chart_12/20260604_062826/variation_review_round5.md
|
| 22 |
+
line_graph_plain_chart_03,pass,controller_fresh_pass_round2,controller,546,546,controller_fresh_pass_round2,controller,,,,
|
| 23 |
+
lollipop_icon_chart_01,pass,worker_provisional_pass_round11_controller_required_after_round8_fail,session_04,587,587,worker_provisional_pass_round11_controller_required_after_round8_fail,session_04,True,True,,data/output/variation_skill_runs/lollipop_icon_chart_01/20260604_040357/variation_review_round11.md
|
| 24 |
+
lollipop_icon_chart_02,pass,controller_fresh_pass_round7,controller,638,638,controller_fresh_pass_round7,controller,,,,
|
| 25 |
+
lollipop_plain_chart_01,pass,final_worker_record_round8_provisional,session_04,723,723,final_worker_record_round8_provisional,session_04,,True,,data/output/variation_skill_runs/lollipop_plain_chart_01/20260604_061905/variation_review_round8.md
|
| 26 |
+
multiple_gauge_chart_01,pass,controller_fresh_review_round7,controller,37,37,controller_fresh_review_round7,controller,,False,,data/output/variation_skill_runs/multiple_gauge_chart_01/20260603_214437/variation_review_round7_controller_fresh_pass.md
|
| 27 |
+
multiple_gauge_chart_02,pass,controller_update_endpoint_corner_modifier_notes_confirmed,session_04,161,161,controller_update_endpoint_corner_modifier_notes_confirmed,session_04,True,True,True,data/output/variation_skill_runs/multiple_gauge_chart_02/20260603_224205/variation_review_round12.md
|
| 28 |
+
multiple_gauge_chart_03,pass,controller_fresh_pass_round10,session_04,392,392,controller_fresh_pass_round10,session_04,,False,,
|
| 29 |
+
multiple_gauge_chart_03_hand,pass,controller_fresh_pass_round4,controller,438,438,controller_fresh_pass_round4,controller,,,,
|
| 30 |
+
multiple_line_graph_01,pass,controller_fresh_pass_round7,controller,485,485,controller_fresh_pass_round7,controller,,,,
|
| 31 |
+
proportional_area_chart_hexagon_01,pass,controller_fresh_pass_round16,,306,306,controller_fresh_pass_round16,,,False,False,data/output/variation_skill_runs/proportional_area_chart_hexagon_01/20260603_221214/variation_review_round16_controller_fresh_pass.md
|
| 32 |
+
proportional_area_chart_hexagon_02,pass,controller_fresh_pass_round13,controller,448,448,controller_fresh_pass_round13,controller,,,,
|
| 33 |
+
proportional_area_chart_other_01,pass,controller_fresh_pass_round34,controller,627,627,controller_fresh_pass_round34,controller,,,,
|
| 34 |
+
proportional_area_chart_pentagram_01,pass,final_worker_record_round5,session_05,713,713,final_worker_record_round5,session_05,,True,,data/output/variation_skill_runs/proportional_area_chart_pentagram_01/20260604_060611/variation_review_round5.md
|
| 35 |
+
range_area_chart_icons_01,pass,controller_fresh_review_round6,controller,129,129,controller_fresh_review_round6,controller,,False,,data/output/variation_skill_runs/range_area_chart_icons_01/20260603_222013/variation_review_round6_controller_fresh_pass.md
|
| 36 |
+
range_area_plain_chart_01,pass,controller_fresh_pass_round10,,331,331,controller_fresh_pass_round10,,,False,False,data/output/variation_skill_runs/range_area_plain_chart_01/20260603_232034/variation_review_round10_controller_fresh_pass.md
|
| 37 |
+
rose_chart_01,pass,controller_fresh_pass_round6_after_worker_provisional_pass,controller,428,428,controller_fresh_pass_round6_after_worker_provisional_pass,controller,,,,
|
| 38 |
+
rose_chart_new_00,pass,controller_fresh_pass_round14,controller,544,544,controller_fresh_pass_round14,controller,,,,
|
| 39 |
+
rose_chart_new_00_hand,pass,worker_provisional_pass_round10_controller_required,session_06,588,588,worker_provisional_pass_round10_controller_required,session_06,True,True,,data/output/variation_skill_runs/rose_chart_new_00_hand/20260604_035901/variation_review_round10.md
|
| 40 |
+
rose_plain_chart_01,pass,final_worker_record_round9_provisional,session_06,686,686,final_worker_record_round9_provisional,session_06,,True,,data/output/variation_skill_runs/rose_plain_chart_01/20260604_052842/variation_review_round9.md
|
| 41 |
+
stacked_area_chart_01,pass,controller_fresh_review_round8,controller,79,79,controller_fresh_review_round8,controller,,False,,data/output/variation_skill_runs/stacked_area_chart_01/20260603_213619/variation_review_round8_controller_fresh_pass.md
|
| 42 |
+
stacked_area_chart_02,pass,completion_worker_provisional_pass_controller_required_round8,session_07,180,180,completion_worker_provisional_pass_controller_required_round8,session_07,True,True,True,data/output/variation_skill_runs/stacked_area_chart_02/20260603_225749/variation_review_round8.md
|
| 43 |
+
stacked_area_chart_03,pass,controller_fresh_pass_round15,controller,454,454,controller_fresh_pass_round15,controller,,,,
|
| 44 |
+
stacked_area_chart_icons_01,pass,variation_review_round7,,563,563,variation_review_round7,,,True,,data/output/variation_skill_runs/stacked_area_chart_icons_01/20260604_024630/variation_review_round7.md
|
| 45 |
+
stacked_area_plain_chart_01,worker_provisional_pass_round13_controller_required,final_worker_record,session_07,687,680,controller_fresh_pass_round13,controller,,,,
|
| 46 |
+
stacked_area_plain_chart_02,pass,,session_07,725,725,,session_07,,True,,data/output/variation_skill_runs/stacked_area_plain_chart_02/20260604_070936/variation_review_round3.md
|
| 47 |
+
vertical_group_bar_chart_12,pass,worker_provisional_pass_round13_controller_fresh_review_required,session_08,236,236,worker_provisional_pass_round13_controller_fresh_review_required,session_08,,True,True,data/output/variation_skill_runs/vertical_group_bar_chart_12/20260603_215309/variation_review_round13.md
|
| 48 |
+
vertical_group_bar_chart_13,pass,controller_fresh_pass_round10_after_worker_provisional_pass,controller,424,424,controller_fresh_pass_round10_after_worker_provisional_pass,controller,,,,
|
| 49 |
+
vertical_group_bar_chart_14,pass,controller_fresh_pass_round4,controller,518,518,controller_fresh_pass_round4,controller,,,,
|
| 50 |
+
vertical_group_bar_chart_15,pass,controller_fresh_pass_round5,controller,606,606,controller_fresh_pass_round5,controller,,,,
|
| 51 |
+
vertical_group_bar_chart_16,pass,final_worker_record_round10_provisional,session_08,696,696,final_worker_record_round10_provisional,session_08,,True,,data/output/variation_skill_runs/vertical_group_bar_chart_16/20260604_053941/variation_review_round10.md
|
passed_templates_ever_pass.txt
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
bump_plain_chart_02
|
| 2 |
+
bump_plain_chart_03
|
| 3 |
+
circular_bar_chart_01
|
| 4 |
+
circular_bar_chart_03
|
| 5 |
+
circular_bar_chart_04
|
| 6 |
+
circular_bar_chart_icons_01
|
| 7 |
+
circular_bar_chart_icons_02
|
| 8 |
+
horizontal_bar_chart_0
|
| 9 |
+
horizontal_bar_chart_01
|
| 10 |
+
horizontal_bar_chart_0_weilai
|
| 11 |
+
horizontal_bar_chart_1
|
| 12 |
+
horizontal_bar_chart_10
|
| 13 |
+
horizontal_bar_chart_11
|
| 14 |
+
horizontal_bar_chart_12
|
| 15 |
+
horizontal_diverging_bar_chart_05
|
| 16 |
+
horizontal_diverging_bar_chart_07
|
| 17 |
+
horizontal_diverging_bar_chart_08
|
| 18 |
+
horizontal_diverging_bar_chart_10
|
| 19 |
+
horizontal_diverging_bar_chart_11
|
| 20 |
+
horizontal_diverging_bar_chart_12
|
| 21 |
+
line_graph_plain_chart_03
|
| 22 |
+
lollipop_icon_chart_01
|
| 23 |
+
lollipop_icon_chart_02
|
| 24 |
+
lollipop_plain_chart_01
|
| 25 |
+
multiple_gauge_chart_01
|
| 26 |
+
multiple_gauge_chart_02
|
| 27 |
+
multiple_gauge_chart_03
|
| 28 |
+
multiple_gauge_chart_03_hand
|
| 29 |
+
multiple_line_graph_01
|
| 30 |
+
proportional_area_chart_hexagon_01
|
| 31 |
+
proportional_area_chart_hexagon_02
|
| 32 |
+
proportional_area_chart_other_01
|
| 33 |
+
proportional_area_chart_pentagram_01
|
| 34 |
+
range_area_chart_icons_01
|
| 35 |
+
range_area_plain_chart_01
|
| 36 |
+
rose_chart_01
|
| 37 |
+
rose_chart_new_00
|
| 38 |
+
rose_chart_new_00_hand
|
| 39 |
+
rose_plain_chart_01
|
| 40 |
+
stacked_area_chart_01
|
| 41 |
+
stacked_area_chart_02
|
| 42 |
+
stacked_area_chart_03
|
| 43 |
+
stacked_area_chart_icons_01
|
| 44 |
+
stacked_area_plain_chart_01
|
| 45 |
+
stacked_area_plain_chart_02
|
| 46 |
+
vertical_group_bar_chart_12
|
| 47 |
+
vertical_group_bar_chart_13
|
| 48 |
+
vertical_group_bar_chart_14
|
| 49 |
+
vertical_group_bar_chart_15
|
| 50 |
+
vertical_group_bar_chart_16
|
passed_templates_final_status_contains_pass.txt
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
bump_plain_chart_02
|
| 2 |
+
bump_plain_chart_03
|
| 3 |
+
circular_bar_chart_01
|
| 4 |
+
circular_bar_chart_03
|
| 5 |
+
circular_bar_chart_04
|
| 6 |
+
circular_bar_chart_icons_01
|
| 7 |
+
circular_bar_chart_icons_02
|
| 8 |
+
horizontal_bar_chart_0
|
| 9 |
+
horizontal_bar_chart_01
|
| 10 |
+
horizontal_bar_chart_0_weilai
|
| 11 |
+
horizontal_bar_chart_1
|
| 12 |
+
horizontal_bar_chart_10
|
| 13 |
+
horizontal_bar_chart_11
|
| 14 |
+
horizontal_bar_chart_12
|
| 15 |
+
horizontal_diverging_bar_chart_05
|
| 16 |
+
horizontal_diverging_bar_chart_07
|
| 17 |
+
horizontal_diverging_bar_chart_08
|
| 18 |
+
horizontal_diverging_bar_chart_10
|
| 19 |
+
horizontal_diverging_bar_chart_11
|
| 20 |
+
horizontal_diverging_bar_chart_12
|
| 21 |
+
line_graph_plain_chart_03
|
| 22 |
+
lollipop_icon_chart_01
|
| 23 |
+
lollipop_icon_chart_02
|
| 24 |
+
lollipop_plain_chart_01
|
| 25 |
+
multiple_gauge_chart_01
|
| 26 |
+
multiple_gauge_chart_02
|
| 27 |
+
multiple_gauge_chart_03
|
| 28 |
+
multiple_gauge_chart_03_hand
|
| 29 |
+
multiple_line_graph_01
|
| 30 |
+
proportional_area_chart_hexagon_01
|
| 31 |
+
proportional_area_chart_hexagon_02
|
| 32 |
+
proportional_area_chart_other_01
|
| 33 |
+
proportional_area_chart_pentagram_01
|
| 34 |
+
range_area_chart_icons_01
|
| 35 |
+
range_area_plain_chart_01
|
| 36 |
+
rose_chart_01
|
| 37 |
+
rose_chart_new_00
|
| 38 |
+
rose_chart_new_00_hand
|
| 39 |
+
rose_plain_chart_01
|
| 40 |
+
stacked_area_chart_01
|
| 41 |
+
stacked_area_chart_02
|
| 42 |
+
stacked_area_chart_03
|
| 43 |
+
stacked_area_chart_icons_01
|
| 44 |
+
stacked_area_plain_chart_01
|
| 45 |
+
stacked_area_plain_chart_02
|
| 46 |
+
vertical_group_bar_chart_12
|
| 47 |
+
vertical_group_bar_chart_13
|
| 48 |
+
vertical_group_bar_chart_14
|
| 49 |
+
vertical_group_bar_chart_15
|
| 50 |
+
vertical_group_bar_chart_16
|
pipeline.py
ADDED
|
@@ -0,0 +1,630 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
ChartPipeline: 完整可视化生成管道
|
| 5 |
+
此脚本执行完整的数据可视化生成流程,依次调用各模块实现从数据到最终图表的转换
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import json
|
| 10 |
+
import argparse
|
| 11 |
+
import logging
|
| 12 |
+
import time
|
| 13 |
+
from datetime import datetime
|
| 14 |
+
from importlib import import_module
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from config import (
|
| 17 |
+
base_url,
|
| 18 |
+
api_key,
|
| 19 |
+
embed_model_path,
|
| 20 |
+
data_resource_path,
|
| 21 |
+
topk,
|
| 22 |
+
text_data_path,
|
| 23 |
+
text_index_path,
|
| 24 |
+
color_data_path,
|
| 25 |
+
color_index_path,
|
| 26 |
+
image_data_path,
|
| 27 |
+
image_index_path,
|
| 28 |
+
image_list_path,
|
| 29 |
+
image_resource_path
|
| 30 |
+
)
|
| 31 |
+
import random
|
| 32 |
+
from concurrent.futures import ProcessPoolExecutor
|
| 33 |
+
|
| 34 |
+
# 配置日志
|
| 35 |
+
logging.basicConfig(
|
| 36 |
+
level=logging.INFO,
|
| 37 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
| 38 |
+
)
|
| 39 |
+
logger = logging.getLogger("ChartPipeline")
|
| 40 |
+
|
| 41 |
+
# 模块配置
|
| 42 |
+
MODULES = [
|
| 43 |
+
{
|
| 44 |
+
"name": "create_index",
|
| 45 |
+
"description": "索引创建模块",
|
| 46 |
+
"input_type": "none",
|
| 47 |
+
"output_type": "none"
|
| 48 |
+
},
|
| 49 |
+
{
|
| 50 |
+
"name": "preprocess",
|
| 51 |
+
"description": "数据预处理模块",
|
| 52 |
+
"input_type": "json",
|
| 53 |
+
"output_type": "json"
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"name": "chart_type_recommender",
|
| 57 |
+
"description": "图表类型推荐模块",
|
| 58 |
+
"input_type": "json",
|
| 59 |
+
"output_type": "json"
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
"name": "datafact_generator",
|
| 63 |
+
"description": "数据洞察模块",
|
| 64 |
+
"input_type": "json",
|
| 65 |
+
"output_type": "json"
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"name": "title_generator",
|
| 69 |
+
"description": "标题生成模块",
|
| 70 |
+
"input_type": "json",
|
| 71 |
+
"output_type": "json"
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"name": "color_recommender",
|
| 75 |
+
"description": "色彩推荐模块",
|
| 76 |
+
"input_type": "json",
|
| 77 |
+
"output_type": "json"
|
| 78 |
+
},
|
| 79 |
+
{
|
| 80 |
+
"name": "image_recommender",
|
| 81 |
+
"description": "图像推荐模块",
|
| 82 |
+
"input_type": "json",
|
| 83 |
+
"output_type": "json"
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"name": "all",
|
| 87 |
+
"description": "综合模块(datafact+title+color+image)",
|
| 88 |
+
"input_type": "json",
|
| 89 |
+
"output_type": "json"
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"name": "infographics_generator",
|
| 93 |
+
"description": "信息图表生成模块",
|
| 94 |
+
"input_type": "json",
|
| 95 |
+
"output_type": "json"
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"name": "asset_regenerator",
|
| 99 |
+
"description": "图像/图标统一生图后处理模块",
|
| 100 |
+
"input_type": "svg",
|
| 101 |
+
"output_type": "svg"
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"name": "full_image_polisher",
|
| 105 |
+
"description": "整图GPT Image润色后处理模块",
|
| 106 |
+
"input_type": "png",
|
| 107 |
+
"output_type": "png"
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"name": "chart_engine",
|
| 111 |
+
"description": "图表模板实现引擎",
|
| 112 |
+
"input_type": "json",
|
| 113 |
+
"output_type": "svg"
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"name": "title_styler",
|
| 117 |
+
"description": "标题元素生成模块",
|
| 118 |
+
"input_type": "json",
|
| 119 |
+
"output_type": "svg"
|
| 120 |
+
}
|
| 121 |
+
]
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def run_pipeline(input_path, output_path=None, temp_dir=None, modules_to_run=None, threads=None, chart_name=None, chart_only=False, output_png=False):
|
| 125 |
+
"""
|
| 126 |
+
执行完整的图表生成管道
|
| 127 |
+
|
| 128 |
+
Args:
|
| 129 |
+
input_path (str): 输入数据文件路径(可以是文件或目录)
|
| 130 |
+
output_path (str, optional): 输出文件路径(可以是文件或目录),如果为None则原地修改
|
| 131 |
+
temp_dir (str, optional): 临时文件目录,默认使用./tmp
|
| 132 |
+
modules_to_run (list, optional): 要运行的模块列表,默认运行所有模块
|
| 133 |
+
threads (int, optional): 处理目录时的并发线程数,仅在input_path为目录时生效
|
| 134 |
+
chart_name (str, optional): 指定图表名称,仅对infographics_generator模块有效
|
| 135 |
+
chart_only (bool, optional): 只输出 chart SVG(跳过 title/image/layout),仅对 infographics_generator 有效
|
| 136 |
+
output_png (bool, optional): 同时输出 PNG,仅对 infographics_generator 有效
|
| 137 |
+
"""
|
| 138 |
+
# try:
|
| 139 |
+
if chart_name is not None:
|
| 140 |
+
output_path = os.path.join(output_path, chart_name)
|
| 141 |
+
import shutil
|
| 142 |
+
shutil.rmtree(output_path, ignore_errors=True)
|
| 143 |
+
os.makedirs(output_path, exist_ok=True)
|
| 144 |
+
print("modules_to_run: ", modules_to_run)
|
| 145 |
+
# 如果是create_index模块,单独处理
|
| 146 |
+
if modules_to_run and 'create_index' in modules_to_run:
|
| 147 |
+
return run_single_file(
|
| 148 |
+
input_path=input_path,
|
| 149 |
+
output_path=output_path,
|
| 150 |
+
temp_dir=temp_dir,
|
| 151 |
+
modules_to_run=modules_to_run,
|
| 152 |
+
chart_name=chart_name,
|
| 153 |
+
chart_only=chart_only,
|
| 154 |
+
output_png=output_png,
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
input_path = Path(input_path)
|
| 158 |
+
output_path = Path(output_path) if output_path else input_path
|
| 159 |
+
temp_dir = Path(temp_dir) if temp_dir else Path("./tmp")
|
| 160 |
+
|
| 161 |
+
if input_path.is_dir():
|
| 162 |
+
# 如果指定了输出目录且不同于输入目录,创建输出目录
|
| 163 |
+
if output_path != input_path:
|
| 164 |
+
output_path.mkdir(parents=True, exist_ok=True)
|
| 165 |
+
|
| 166 |
+
# 获取输入目录下所有JSON文件
|
| 167 |
+
input_files = list(input_path.glob('*.json'))
|
| 168 |
+
random.shuffle(input_files) # 随机打乱文件顺序
|
| 169 |
+
|
| 170 |
+
if threads and threads > 1:
|
| 171 |
+
# 使用线程池并行处理文件
|
| 172 |
+
with ProcessPoolExecutor(max_workers=threads) as executor:
|
| 173 |
+
futures = []
|
| 174 |
+
for input_file in input_files:
|
| 175 |
+
# 如果是inplace处理,输出路径就是输入路径
|
| 176 |
+
if output_path == input_path:
|
| 177 |
+
output_file = input_file
|
| 178 |
+
else:
|
| 179 |
+
# 确保输出文件保持相同的文件名
|
| 180 |
+
output_file = output_path / input_file.name
|
| 181 |
+
|
| 182 |
+
future = executor.submit(
|
| 183 |
+
run_single_file,
|
| 184 |
+
input_path=input_file,
|
| 185 |
+
output_path=output_file,
|
| 186 |
+
temp_dir=temp_dir,
|
| 187 |
+
modules_to_run=modules_to_run,
|
| 188 |
+
chart_name=chart_name,
|
| 189 |
+
chart_only=chart_only,
|
| 190 |
+
output_png=output_png,
|
| 191 |
+
)
|
| 192 |
+
futures.append(future)
|
| 193 |
+
|
| 194 |
+
# 等待所有任务完成并检查结果
|
| 195 |
+
results = [future.result() for future in futures]
|
| 196 |
+
return all(results)
|
| 197 |
+
else:
|
| 198 |
+
print("单线程顺序处理")
|
| 199 |
+
# 单线程顺序处理
|
| 200 |
+
success = True
|
| 201 |
+
for input_file in input_files:
|
| 202 |
+
if output_path == input_path:
|
| 203 |
+
output_file = input_file
|
| 204 |
+
else:
|
| 205 |
+
output_file = output_path / input_file.name
|
| 206 |
+
|
| 207 |
+
success &= run_single_file(
|
| 208 |
+
input_path=input_file,
|
| 209 |
+
output_path=output_file,
|
| 210 |
+
temp_dir=temp_dir,
|
| 211 |
+
modules_to_run=modules_to_run,
|
| 212 |
+
chart_name=chart_name,
|
| 213 |
+
chart_only=chart_only,
|
| 214 |
+
output_png=output_png,
|
| 215 |
+
)
|
| 216 |
+
return success
|
| 217 |
+
else:
|
| 218 |
+
# 单文件处理
|
| 219 |
+
if output_path == input_path:
|
| 220 |
+
output_file = input_path
|
| 221 |
+
else:
|
| 222 |
+
# 如果指定了不同的输出路径,确保它的父目录存在
|
| 223 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 224 |
+
output_file = output_path
|
| 225 |
+
|
| 226 |
+
return run_single_file(
|
| 227 |
+
input_path=input_path,
|
| 228 |
+
output_path=output_file,
|
| 229 |
+
temp_dir=temp_dir,
|
| 230 |
+
modules_to_run=modules_to_run,
|
| 231 |
+
chart_name=chart_name,
|
| 232 |
+
chart_only=chart_only,
|
| 233 |
+
output_png=output_png,
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
# except Exception as e:
|
| 237 |
+
# logger.error(f"管道执行失败: {str(e)}")
|
| 238 |
+
# return False
|
| 239 |
+
|
| 240 |
+
def run_single_file(input_path, output_path, temp_dir=None, modules_to_run=None, chart_name=None, chart_only=False, output_png=False, return_timing=False):
|
| 241 |
+
"""
|
| 242 |
+
处理单个文件的管道逻辑
|
| 243 |
+
|
| 244 |
+
Args:
|
| 245 |
+
input_path (Path): 输入JSON文件路径
|
| 246 |
+
output_path (Path): 输出路径
|
| 247 |
+
temp_dir (Path): 临时文件目录,默认为./tmp
|
| 248 |
+
modules_to_run (list): 要运行的模块列表
|
| 249 |
+
chart_name (str, optional): 指定图表名称,仅对infographics_generator模块有效
|
| 250 |
+
chart_only (bool, optional): 只输出 chart SVG,仅对 infographics_generator 有效
|
| 251 |
+
output_png (bool, optional): 同时输出 PNG,仅对 infographics_generator 有效
|
| 252 |
+
"""
|
| 253 |
+
# try:
|
| 254 |
+
run_start_time = time.time()
|
| 255 |
+
module_timings = {}
|
| 256 |
+
|
| 257 |
+
def finish(success: bool):
|
| 258 |
+
total_seconds = time.time() - run_start_time
|
| 259 |
+
logger.info(
|
| 260 |
+
"run_single_file timing: total=%.3fs modules=%s",
|
| 261 |
+
total_seconds,
|
| 262 |
+
{k: round(v, 3) for k, v in module_timings.items()},
|
| 263 |
+
)
|
| 264 |
+
if return_timing:
|
| 265 |
+
return {
|
| 266 |
+
"success": success,
|
| 267 |
+
"total_seconds": total_seconds,
|
| 268 |
+
"module_seconds": module_timings,
|
| 269 |
+
}
|
| 270 |
+
return success
|
| 271 |
+
|
| 272 |
+
def fail_module(module_name: str):
|
| 273 |
+
logger.error("模块执行失败: %s", module_name)
|
| 274 |
+
return finish(False)
|
| 275 |
+
|
| 276 |
+
# 确定要运行的模块
|
| 277 |
+
if not modules_to_run:
|
| 278 |
+
modules_to_run = [m["name"] for m in MODULES]
|
| 279 |
+
|
| 280 |
+
# 如果要运行create_index,单独处理并直接返回
|
| 281 |
+
if 'create_index' in modules_to_run:
|
| 282 |
+
logger.info("执行create_index模块")
|
| 283 |
+
# 依次调用三个模块的create_index
|
| 284 |
+
for module_type in ['title', 'color', 'image']:
|
| 285 |
+
if module_type == 'title':
|
| 286 |
+
print("Running modules.title_generator.create_index")
|
| 287 |
+
module = import_module('modules.title_generator.create_index')
|
| 288 |
+
if not Path(text_index_path).exists():
|
| 289 |
+
module.process(
|
| 290 |
+
data=text_data_path,
|
| 291 |
+
index_path=text_index_path,
|
| 292 |
+
data_path=text_data_path,
|
| 293 |
+
embed_model_path=embed_model_path
|
| 294 |
+
)
|
| 295 |
+
elif module_type == 'color':
|
| 296 |
+
print("Running modules.color_recommender.create_index")
|
| 297 |
+
module = import_module('modules.color_recommender.create_index')
|
| 298 |
+
if not Path(color_index_path).exists():
|
| 299 |
+
module.main(
|
| 300 |
+
input=color_data_path,
|
| 301 |
+
output=color_index_path,
|
| 302 |
+
embed_model_path=embed_model_path
|
| 303 |
+
)
|
| 304 |
+
else: # image
|
| 305 |
+
print("Running modules.image_recommender.create_index")
|
| 306 |
+
module = import_module('modules.image_recommender.create_index')
|
| 307 |
+
if not Path(image_index_path).exists():
|
| 308 |
+
module.main(
|
| 309 |
+
image_list_path=image_list_path,
|
| 310 |
+
image_resource_path=image_resource_path,
|
| 311 |
+
index_path=image_index_path,
|
| 312 |
+
data_path=image_data_path,
|
| 313 |
+
embed_model_path=embed_model_path
|
| 314 |
+
)
|
| 315 |
+
total_seconds = time.time() - run_start_time
|
| 316 |
+
if return_timing:
|
| 317 |
+
return {
|
| 318 |
+
"success": True,
|
| 319 |
+
"total_seconds": total_seconds,
|
| 320 |
+
"module_seconds": module_timings,
|
| 321 |
+
}
|
| 322 |
+
return True
|
| 323 |
+
|
| 324 |
+
# 创建临时目录
|
| 325 |
+
if temp_dir is None:
|
| 326 |
+
temp_dir = Path("./tmp")
|
| 327 |
+
temp_dir.mkdir(parents=True, exist_ok=True)
|
| 328 |
+
|
| 329 |
+
# 确保输出目录存在
|
| 330 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 331 |
+
|
| 332 |
+
# 当前输入文件
|
| 333 |
+
current_input = input_path
|
| 334 |
+
|
| 335 |
+
# 获取最后一个模块的配置
|
| 336 |
+
last_module = [m for m in MODULES if m["name"] in modules_to_run][-1]
|
| 337 |
+
|
| 338 |
+
# 根据最后一个模块的输出类型决定最终输出文件的扩展名
|
| 339 |
+
if last_module["name"] in ["chart_engine", "title_styler", "asset_regenerator"]:
|
| 340 |
+
final_output = output_path.with_suffix('.svg')
|
| 341 |
+
elif last_module["name"] == "full_image_polisher":
|
| 342 |
+
final_output = output_path.with_suffix('.png')
|
| 343 |
+
elif last_module["name"] == "infographics_generator":
|
| 344 |
+
final_output = output_path.parent / f"{output_path.stem}_final.svg"
|
| 345 |
+
else:
|
| 346 |
+
final_output = output_path.with_suffix('.json')
|
| 347 |
+
|
| 348 |
+
# 记录执行过程
|
| 349 |
+
# 依次执行各模块
|
| 350 |
+
for i, module_config in enumerate([m for m in MODULES if m["name"] in modules_to_run]):
|
| 351 |
+
module_name = module_config["name"]
|
| 352 |
+
module_desc = module_config["description"]
|
| 353 |
+
module_start_time = time.time()
|
| 354 |
+
# logger.info(f"执行模块 {i+1}/{len(modules_to_run)}: {module_name} - {module_desc}")
|
| 355 |
+
|
| 356 |
+
# 特殊处理all模块,依次执行datafact_generator、title_generator、color_recommender和image_recommender
|
| 357 |
+
if module_name == "all":
|
| 358 |
+
# 数据洞察模块
|
| 359 |
+
preprocess_module = import_module(f"modules.preprocess.preprocess")
|
| 360 |
+
if not should_skip_module(module_name, output_path):
|
| 361 |
+
step_start_time = time.time()
|
| 362 |
+
preprocess_module.process(input=str(current_input), output=str(output_path))
|
| 363 |
+
module_timings["all.preprocess"] = time.time() - step_start_time
|
| 364 |
+
current_input = output_path
|
| 365 |
+
|
| 366 |
+
datafact_module = import_module("modules.datafact_generator.datafact_generator")
|
| 367 |
+
if not should_skip_module("datafact_generator", output_path):
|
| 368 |
+
step_start_time = time.time()
|
| 369 |
+
datafact_module.process(input=str(current_input), output=str(output_path))
|
| 370 |
+
module_timings["all.datafact_generator"] = time.time() - step_start_time
|
| 371 |
+
current_input = output_path
|
| 372 |
+
|
| 373 |
+
# 标题生成模块
|
| 374 |
+
title_module = import_module("modules.title_generator.title_generator")
|
| 375 |
+
if not should_skip_module("title_generator", output_path):
|
| 376 |
+
step_start_time = time.time()
|
| 377 |
+
title_module.process(
|
| 378 |
+
input=str(current_input),
|
| 379 |
+
output=str(output_path),
|
| 380 |
+
base_url=base_url,
|
| 381 |
+
api_key=api_key,
|
| 382 |
+
embed_model_path=embed_model_path,
|
| 383 |
+
topk=topk,
|
| 384 |
+
data_path=text_data_path,
|
| 385 |
+
index_path=text_index_path
|
| 386 |
+
)
|
| 387 |
+
module_timings["all.title_generator"] = time.time() - step_start_time
|
| 388 |
+
current_input = output_path
|
| 389 |
+
|
| 390 |
+
# 色彩推荐模块
|
| 391 |
+
color_module = import_module("modules.color_recommender.color_recommender")
|
| 392 |
+
if not should_skip_module("color_recommender", output_path):
|
| 393 |
+
step_start_time = time.time()
|
| 394 |
+
color_module.process(
|
| 395 |
+
input=str(current_input),
|
| 396 |
+
output=str(output_path),
|
| 397 |
+
base_url=base_url,
|
| 398 |
+
api_key=api_key,
|
| 399 |
+
embed_model_path=embed_model_path,
|
| 400 |
+
data_path=color_data_path,
|
| 401 |
+
index_path=color_index_path
|
| 402 |
+
)
|
| 403 |
+
module_timings["all.color_recommender"] = time.time() - step_start_time
|
| 404 |
+
current_input = output_path
|
| 405 |
+
|
| 406 |
+
# 图像推荐模块
|
| 407 |
+
image_module = import_module("modules.image_recommender.image_recommender")
|
| 408 |
+
if not should_skip_module("image_recommender", output_path):
|
| 409 |
+
step_start_time = time.time()
|
| 410 |
+
image_module.process(
|
| 411 |
+
input=str(current_input),
|
| 412 |
+
output=str(output_path),
|
| 413 |
+
base_url=base_url,
|
| 414 |
+
api_key=api_key,
|
| 415 |
+
embed_model_path=embed_model_path,
|
| 416 |
+
data_path=image_data_path,
|
| 417 |
+
index_path=image_index_path,
|
| 418 |
+
resource_path=image_resource_path
|
| 419 |
+
)
|
| 420 |
+
module_timings["all.image_recommender"] = time.time() - step_start_time
|
| 421 |
+
current_input = output_path
|
| 422 |
+
|
| 423 |
+
# 特殊处理title_generator模块,传入配置参数
|
| 424 |
+
elif module_name == "title_generator":
|
| 425 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 426 |
+
if not should_skip_module(module_name, output_path):
|
| 427 |
+
step_start_time = time.time()
|
| 428 |
+
module.process(
|
| 429 |
+
input=str(current_input),
|
| 430 |
+
output=str(output_path),
|
| 431 |
+
base_url=base_url,
|
| 432 |
+
api_key=api_key,
|
| 433 |
+
embed_model_path=embed_model_path,
|
| 434 |
+
topk=topk,
|
| 435 |
+
data_path=text_data_path,
|
| 436 |
+
index_path=text_index_path
|
| 437 |
+
)
|
| 438 |
+
module_timings["title_generator"] = time.time() - step_start_time
|
| 439 |
+
current_input = output_path
|
| 440 |
+
elif module_name == "color_recommender":
|
| 441 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 442 |
+
if not should_skip_module(module_name, output_path):
|
| 443 |
+
step_start_time = time.time()
|
| 444 |
+
module.process(
|
| 445 |
+
input=str(current_input),
|
| 446 |
+
output=str(output_path),
|
| 447 |
+
base_url=base_url,
|
| 448 |
+
api_key=api_key,
|
| 449 |
+
embed_model_path=embed_model_path,
|
| 450 |
+
data_path=color_data_path,
|
| 451 |
+
index_path=color_index_path
|
| 452 |
+
)
|
| 453 |
+
module_timings["color_recommender"] = time.time() - step_start_time
|
| 454 |
+
current_input = output_path
|
| 455 |
+
elif module_name == "image_recommender":
|
| 456 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 457 |
+
print("image_recommender")
|
| 458 |
+
if not should_skip_module(module_name, output_path):
|
| 459 |
+
print("process")
|
| 460 |
+
step_start_time = time.time()
|
| 461 |
+
module.process(
|
| 462 |
+
input=str(current_input),
|
| 463 |
+
output=str(output_path),
|
| 464 |
+
base_url=base_url,
|
| 465 |
+
api_key=api_key,
|
| 466 |
+
embed_model_path=embed_model_path,
|
| 467 |
+
data_path=image_data_path,
|
| 468 |
+
index_path=image_index_path,
|
| 469 |
+
resource_path=image_resource_path
|
| 470 |
+
)
|
| 471 |
+
module_timings["image_recommender"] = time.time() - step_start_time
|
| 472 |
+
current_input = output_path
|
| 473 |
+
elif module_name == "infographics_generator":
|
| 474 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 475 |
+
if not should_skip_module(module_name, output_path):
|
| 476 |
+
step_start_time = time.time()
|
| 477 |
+
result = module.process(
|
| 478 |
+
input=str(current_input),
|
| 479 |
+
output=str(output_path),
|
| 480 |
+
base_url=base_url,
|
| 481 |
+
api_key=api_key,
|
| 482 |
+
chart_name=chart_name,
|
| 483 |
+
chart_only=chart_only,
|
| 484 |
+
output_png=output_png,
|
| 485 |
+
)
|
| 486 |
+
module_timings["infographics_generator"] = time.time() - step_start_time
|
| 487 |
+
if result is False:
|
| 488 |
+
return fail_module(module_name)
|
| 489 |
+
current_input = output_path
|
| 490 |
+
elif module_name == "asset_regenerator":
|
| 491 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 492 |
+
if not should_skip_module(module_name, output_path):
|
| 493 |
+
ok = module.process(
|
| 494 |
+
input=str(current_input),
|
| 495 |
+
output=str(output_path),
|
| 496 |
+
base_url=base_url,
|
| 497 |
+
api_key=api_key,
|
| 498 |
+
)
|
| 499 |
+
if not ok:
|
| 500 |
+
return False
|
| 501 |
+
current_input = output_path
|
| 502 |
+
elif module_name == "full_image_polisher":
|
| 503 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 504 |
+
if not should_skip_module(module_name, output_path):
|
| 505 |
+
ok = module.process(
|
| 506 |
+
input=str(current_input),
|
| 507 |
+
output=str(output_path),
|
| 508 |
+
base_url=base_url,
|
| 509 |
+
api_key=api_key,
|
| 510 |
+
)
|
| 511 |
+
if not ok:
|
| 512 |
+
return False
|
| 513 |
+
current_input = output_path
|
| 514 |
+
elif module_name == "chart_engine":
|
| 515 |
+
# 输入是JSON,输出是SVG
|
| 516 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 517 |
+
svg_output = output_path.with_suffix('.svg')
|
| 518 |
+
if True: # not svg_output.exists():
|
| 519 |
+
step_start_time = time.time()
|
| 520 |
+
result = module.process(input=str(current_input), output=str(svg_output))
|
| 521 |
+
module_timings["chart_engine"] = time.time() - step_start_time
|
| 522 |
+
if result is False:
|
| 523 |
+
return fail_module(module_name)
|
| 524 |
+
current_input = svg_output # 更新为SVG文件作为下一个模块的输入
|
| 525 |
+
|
| 526 |
+
elif module_name == "title_styler":
|
| 527 |
+
# 输入是JSON,输出是SVG
|
| 528 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 529 |
+
title_svg = output_path.parent / f"{output_path.stem}_title.svg"
|
| 530 |
+
if not title_svg.exists():
|
| 531 |
+
step_start_time = time.time()
|
| 532 |
+
result = module.process(input=str(current_input), output=str(title_svg))
|
| 533 |
+
module_timings["title_styler"] = time.time() - step_start_time
|
| 534 |
+
if result is False:
|
| 535 |
+
return fail_module(module_name)
|
| 536 |
+
current_input = title_svg # 更新为标题SVG作为下一个模块的输入
|
| 537 |
+
|
| 538 |
+
else:
|
| 539 |
+
# 普通模块:输入JSON,输出JSON
|
| 540 |
+
module = import_module(f"modules.{module_name}.{module_name}")
|
| 541 |
+
if not should_skip_module(module_name, output_path):
|
| 542 |
+
step_start_time = time.time()
|
| 543 |
+
result = module.process(input=str(current_input), output=str(output_path))
|
| 544 |
+
module_timings[module_name] = time.time() - step_start_time
|
| 545 |
+
if result is False:
|
| 546 |
+
return fail_module(module_name)
|
| 547 |
+
current_input = output_path
|
| 548 |
+
|
| 549 |
+
module_timings[f"{module_name}.total"] = time.time() - module_start_time
|
| 550 |
+
|
| 551 |
+
return finish(True)
|
| 552 |
+
|
| 553 |
+
# except Exception as e:
|
| 554 |
+
# logger.error(f"文件处理失败 {input_path}: {str(e)}")
|
| 555 |
+
# return False
|
| 556 |
+
|
| 557 |
+
def should_skip_module(module_name: str, output_path: Path) -> bool:
|
| 558 |
+
"""检查是否需要跳过模块执行"""
|
| 559 |
+
try:
|
| 560 |
+
if not output_path.exists():
|
| 561 |
+
return False
|
| 562 |
+
|
| 563 |
+
# 检查JSON文件中的特定字段
|
| 564 |
+
with open(output_path) as f:
|
| 565 |
+
data = json.load(f)
|
| 566 |
+
|
| 567 |
+
skip_conditions = {
|
| 568 |
+
"preprocess": lambda d: "metadata" in d and "data" in d and "variables" in d and "processed" in d,
|
| 569 |
+
"chart_type_recommender": lambda d: "chart_type" in d,
|
| 570 |
+
"datafact_generator": lambda d: "datafacts" in d,
|
| 571 |
+
"title_generator": lambda d: False,#"titles" in d,
|
| 572 |
+
"color_recommender": lambda d: "colors" in d,
|
| 573 |
+
"image_recommender": lambda d: "images" in d
|
| 574 |
+
}
|
| 575 |
+
|
| 576 |
+
if module_name in skip_conditions:
|
| 577 |
+
return skip_conditions[module_name](data)
|
| 578 |
+
|
| 579 |
+
return False
|
| 580 |
+
|
| 581 |
+
except Exception as e:
|
| 582 |
+
logger.warning(f"检查跳过条件时出错: {str(e)}")
|
| 583 |
+
return False
|
| 584 |
+
|
| 585 |
+
def parse_args():
|
| 586 |
+
parser = argparse.ArgumentParser()
|
| 587 |
+
parser.add_argument('--input', type=str,
|
| 588 |
+
help='Input json file path or directory (default: config.data_resource_path = data_resource_dirs[0])',
|
| 589 |
+
default=data_resource_path)
|
| 590 |
+
parser.add_argument('--output', type=str, help='Output json file path', default='output')
|
| 591 |
+
parser.add_argument('--temp-dir', type=str, default='tmp')
|
| 592 |
+
parser.add_argument('--modules', type=str, nargs='+', help='Modules to run', default=['infographics_generator'])
|
| 593 |
+
parser.add_argument('--threads', type=int, help='Number of threads for directory processing', default=1)
|
| 594 |
+
parser.add_argument('--chart-name', type=str, help='Specific chart name to use for infographics_generator')
|
| 595 |
+
parser.add_argument('--chart-only', action='store_true', help='Only output the chart SVG; skip title/image/layout (infographics_generator only)')
|
| 596 |
+
parser.add_argument('--output-png', action='store_true', help='Also write a PNG next to the final SVG (uses rsvg-convert)')
|
| 597 |
+
|
| 598 |
+
args = parser.parse_args()
|
| 599 |
+
# 如果没有指定input,从data_resource_path随机选择
|
| 600 |
+
if args.input is None and 'create_index' not in args.modules:
|
| 601 |
+
json_files = [f for f in os.listdir(data_resource_path) if f.endswith('.json')]
|
| 602 |
+
if not json_files:
|
| 603 |
+
raise ValueError(f"在 {data_resource_path} 目录下没有找到json文件")
|
| 604 |
+
args.input = os.path.join(data_resource_path, random.choice(json_files))
|
| 605 |
+
print(f"随机��择输入文件: {args.input}")
|
| 606 |
+
args.output = "tmp.json"
|
| 607 |
+
print(f"使用默认输出文件: {args.output}")
|
| 608 |
+
|
| 609 |
+
return args
|
| 610 |
+
|
| 611 |
+
def main():
|
| 612 |
+
args = parse_args()
|
| 613 |
+
modules_to_run = None
|
| 614 |
+
if args.modules:
|
| 615 |
+
modules_to_run = [m.strip() for m in args.modules]
|
| 616 |
+
|
| 617 |
+
ok = run_pipeline(
|
| 618 |
+
input_path=args.input,
|
| 619 |
+
output_path=args.output,
|
| 620 |
+
temp_dir=args.temp_dir,
|
| 621 |
+
modules_to_run=modules_to_run,
|
| 622 |
+
threads=args.threads,
|
| 623 |
+
chart_name=args.chart_name,
|
| 624 |
+
chart_only=args.chart_only,
|
| 625 |
+
output_png=args.output_png,
|
| 626 |
+
)
|
| 627 |
+
raise SystemExit(0 if ok else 1)
|
| 628 |
+
|
| 629 |
+
if __name__ == "__main__":
|
| 630 |
+
main()
|
record.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
requirements.txt
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pandas>=1.3.0
|
| 2 |
+
numpy>=1.20.0
|
| 3 |
+
opencv-python-headless>=4.8.0
|
| 4 |
+
IPython
|
| 5 |
+
pyecharts
|
| 6 |
+
snapshot_selenium
|
| 7 |
+
imgkit
|
| 8 |
+
networkx
|
| 9 |
+
matplotlib
|
| 10 |
+
openai
|
| 11 |
+
serpapi
|
| 12 |
+
|
| 13 |
+
clip
|
| 14 |
+
torch
|
| 15 |
+
faiss
|
setup.sh
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Setup script for ChartPipeline on a fresh machine.
|
| 3 |
+
# Run from the project root (the directory this script is in).
|
| 4 |
+
#
|
| 5 |
+
# Prereqs you have to handle BEFORE running this script:
|
| 6 |
+
# 1. Python 3.10+ installed (conda or system).
|
| 7 |
+
# 2. Node.js 18+ installed (`node -v`).
|
| 8 |
+
# 3. Google Chrome or Chromium installed at /usr/bin/google-chrome
|
| 9 |
+
# (or set PUPPETEER_EXECUTABLE_PATH to its real path).
|
| 10 |
+
# 4. librsvg2-bin installed (provides rsvg-convert). On Ubuntu:
|
| 11 |
+
# sudo apt install librsvg2-bin tmux
|
| 12 |
+
# If you have no sudo, install via conda:
|
| 13 |
+
# conda install -c conda-forge librsvg tmux
|
| 14 |
+
# 5. Input data pools available at the paths config.py points to,
|
| 15 |
+
# e.g. /data/liduan/resources/claude_data_v2 and claude_new.
|
| 16 |
+
# Edit config.data_resource_dirs to match this machine if needed.
|
| 17 |
+
|
| 18 |
+
set -u
|
| 19 |
+
cd "$(dirname "$0")"
|
| 20 |
+
ROOT="$(pwd)"
|
| 21 |
+
echo "[setup] cwd=$ROOT"
|
| 22 |
+
|
| 23 |
+
# ---- 1. Python deps ----
|
| 24 |
+
echo
|
| 25 |
+
echo "============================================================"
|
| 26 |
+
echo "[setup] installing python deps from requirements.txt"
|
| 27 |
+
echo "============================================================"
|
| 28 |
+
pip install -r requirements.txt
|
| 29 |
+
# Things requirements.txt was missing on the source machine; install
|
| 30 |
+
# defensively so chart_engine + selenium-based screenshot both work.
|
| 31 |
+
pip install pyecharts snapshot_selenium selenium
|
| 32 |
+
|
| 33 |
+
# ---- 2. Node deps ----
|
| 34 |
+
echo
|
| 35 |
+
echo "============================================================"
|
| 36 |
+
echo "[setup] installing node deps (puppeteer / echarts ...)"
|
| 37 |
+
echo "============================================================"
|
| 38 |
+
if [[ -f package.json ]]; then
|
| 39 |
+
# IMPORTANT: ChartPipeline's puppeteer was patched to *re-use* an existing
|
| 40 |
+
# Chrome via PUPPETEER_EXECUTABLE_PATH; skip the bundled chromium download
|
| 41 |
+
# to save ~300 MB and avoid network friction.
|
| 42 |
+
PUPPETEER_SKIP_DOWNLOAD=1 npm install
|
| 43 |
+
else
|
| 44 |
+
echo "[setup] package.json missing, skipping npm install"
|
| 45 |
+
fi
|
| 46 |
+
|
| 47 |
+
# ---- 3. System binaries sanity check ----
|
| 48 |
+
echo
|
| 49 |
+
echo "============================================================"
|
| 50 |
+
echo "[setup] checking system binaries"
|
| 51 |
+
echo "============================================================"
|
| 52 |
+
for bin in google-chrome rsvg-convert tmux node npm python; do
|
| 53 |
+
loc="$(command -v "$bin" 2>/dev/null || true)"
|
| 54 |
+
if [[ -n "$loc" ]]; then
|
| 55 |
+
printf ' %-14s OK %s\n' "$bin" "$loc"
|
| 56 |
+
else
|
| 57 |
+
printf ' %-14s MISSING -- you need to install this before running pipeline.py\n' "$bin"
|
| 58 |
+
fi
|
| 59 |
+
done
|
| 60 |
+
|
| 61 |
+
# ---- 4. PUPPETEER_EXECUTABLE_PATH ----
|
| 62 |
+
echo
|
| 63 |
+
echo "============================================================"
|
| 64 |
+
echo "[setup] puppeteer chrome path"
|
| 65 |
+
echo "============================================================"
|
| 66 |
+
CHROME_BIN="${PUPPETEER_EXECUTABLE_PATH:-$(command -v google-chrome || command -v chromium || true)}"
|
| 67 |
+
if [[ -n "$CHROME_BIN" ]]; then
|
| 68 |
+
echo " PUPPETEER_EXECUTABLE_PATH = $CHROME_BIN (export this before running pipeline.py)"
|
| 69 |
+
else
|
| 70 |
+
echo " WARNING: no chrome/chromium found; puppeteer will fail at runtime"
|
| 71 |
+
fi
|
| 72 |
+
|
| 73 |
+
# ---- 5. Quick import test ----
|
| 74 |
+
echo
|
| 75 |
+
echo "============================================================"
|
| 76 |
+
echo "[setup] verifying config + key imports"
|
| 77 |
+
echo "============================================================"
|
| 78 |
+
python - <<'PY'
|
| 79 |
+
import importlib
|
| 80 |
+
import config
|
| 81 |
+
print(f" config.resource_path = {config.resource_path}")
|
| 82 |
+
print(f" config.data_resource_dirs[0]= {config.data_resource_dirs[0]}")
|
| 83 |
+
for mod in (
|
| 84 |
+
"modules.chart_engine.template.template_registry",
|
| 85 |
+
"modules.infographics_generator.template_utils",
|
| 86 |
+
"modules.infographics_generator.infographics_generator",
|
| 87 |
+
):
|
| 88 |
+
importlib.import_module(mod)
|
| 89 |
+
print(f" import {mod} OK")
|
| 90 |
+
PY
|
| 91 |
+
|
| 92 |
+
echo
|
| 93 |
+
echo "[setup] DONE."
|
| 94 |
+
echo "Next steps:"
|
| 95 |
+
echo " 1. Edit config.py 's data_resource_dirs if data pools live elsewhere on this host."
|
| 96 |
+
echo " 2. (optional) Edit allowed_chart_types.json to restrict which chart_type are eligible."
|
| 97 |
+
echo " 3. Launch a chart-only run, e.g.:"
|
| 98 |
+
echo " export PUPPETEER_EXECUTABLE_PATH=$CHROME_BIN"
|
| 99 |
+
echo " tmux new-session -d -s chart_10h \\"
|
| 100 |
+
echo " \"PRIOR_SESSION=__none__ THREADS=48 DEADLINE_SEC=36000 bash scripts/run_10h_loop.sh\""
|