%22%22%22Plot%20queue%20manager.%0A%0ATrack%20which%20pieces%20are%20queued%20for%20physical%20plotting%2C%20mark%20when%20plotted.%0A-%20Browse%20top%20scoring%20pieces%0A-%20Add%20to%20plot%20queue%20(writes%20to%20plot_queue.tsv)%0A-%20See%20estimated%20plot%20time%0A-%20Mark%20pieces%20as%20%22plotted%22%20with%20timestamp%0A-%20View%20plotted%20gallery%0A%22%22%22%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.22.5%22%0Aapp%20%3D%20marimo.App(width%3D%22full%22%2C%20app_title%3D%22Plot%20Queue%20Manager%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20csv%0A%20%20%20%20from%20datetime%20import%20datetime%2C%20timezone%0A%20%20%20%20from%20pathlib%20import%20Path%0A%20%20%20%20import%20sys%0A%20%20%20%20sys.path.insert(0%2C%20str(Path(__file__).parent))%0A%20%20%20%20import%20_lib%20as%20lib%0A%0A%20%20%20%20return%20csv%2C%20datetime%2C%20lib%2C%20mo%2C%20timezone%0A%0A%0A%40app.cell%0Adef%20_(lib)%3A%0A%20%20%20%20PLOT_QUEUE_TSV%20%3D%20lib.AUTORESEARCH_DIR%20%2F%20%22plot_queue.tsv%22%0A%20%20%20%20PLOT_QUEUE_HEADER%20%3D%20%22id%5Ctadded_at%5Ctstatus%5Ctplotted_at%5Ctnotes%5Ctest_min%22%0A%20%20%20%20return%20PLOT_QUEUE_HEADER%2C%20PLOT_QUEUE_TSV%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%20Plot%20Queue%20Manager%0A%0A%20%20%20%20Curate%20which%20pieces%20to%20physically%20plot%2C%20track%20plot%20time%2C%20mark%20completion.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(PLOT_QUEUE_HEADER%2C%20PLOT_QUEUE_TSV%2C%20csv)%3A%0A%20%20%20%20def%20load_queue()%3A%0A%20%20%20%20%20%20%20%20if%20not%20PLOT_QUEUE_TSV.exists()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%5B%5D%0A%20%20%20%20%20%20%20%20with%20open(PLOT_QUEUE_TSV)%20as%20fh%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20list(csv.DictReader(fh%2C%20delimiter%3D%22%5Ct%22))%0A%0A%20%20%20%20def%20save_queue(rows)%3A%0A%20%20%20%20%20%20%20%20with%20open(PLOT_QUEUE_TSV%2C%20%22w%22%2C%20newline%3D%22%22)%20as%20fh%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20fh.write(PLOT_QUEUE_HEADER%20%2B%20%22%5Cn%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20rows%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fh.write(%22%5Ct%22.join(str(r.get(k%2C%20%22%22))%20for%20k%20in%20PLOT_QUEUE_HEADER.split(%22%5Ct%22))%20%2B%20%22%5Cn%22)%0A%0A%20%20%20%20return%20load_queue%2C%20save_queue%0A%0A%0A%40app.cell%0Adef%20_(load_queue)%3A%0A%20%20%20%20queue%20%3D%20load_queue()%0A%20%20%20%20queued_ids%20%3D%20%7Br%5B%22id%22%5D%20for%20r%20in%20queue%20if%20r%5B%22status%22%5D%20%3D%3D%20%22queued%22%7D%0A%20%20%20%20plotted_ids%20%3D%20%7Br%5B%22id%22%5D%20for%20r%20in%20queue%20if%20r%5B%22status%22%5D%20%3D%3D%20%22plotted%22%7D%0A%20%20%20%20return%20plotted_ids%2C%20queue%2C%20queued_ids%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20plotted_ids%2C%20queue%2C%20queued_ids)%3A%0A%20%20%20%20mo.hstack(%5B%0A%20%20%20%20%20%20%20%20mo.stat(label%3D%22Queued%22%2C%20value%3Dstr(len(queued_ids)))%2C%0A%20%20%20%20%20%20%20%20mo.stat(label%3D%22Plotted%22%2C%20value%3Dstr(len(plotted_ids)))%2C%0A%20%20%20%20%20%20%20%20mo.stat(label%3D%22Total%20entries%22%2C%20value%3Dstr(len(queue)))%2C%0A%20%20%20%20%5D%2C%20gap%3D2)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(lib%2C%20mo)%3A%0A%20%20%20%20browse_factory%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20options%3D%5B%22(any)%22%5D%20%2B%20lib.FACTORIES%2C%0A%20%20%20%20%20%20%20%20value%3D%22(any)%22%2C%0A%20%20%20%20%20%20%20%20label%3D%22Factory%22%2C%0A%20%20%20%20)%0A%20%20%20%20min_score%20%3D%20mo.ui.slider(50%2C%20100%2C%20value%3D80%2C%20label%3D%22Min%20score%22%2C%20show_value%3DTrue)%0A%20%20%20%20n_show%20%3D%20mo.ui.slider(6%2C%2036%2C%20value%3D12%2C%20label%3D%22Show%20top%20N%22%2C%20show_value%3DTrue)%0A%20%20%20%20mo.hstack(%5Bbrowse_factory%2C%20min_score%2C%20n_show%5D%2C%20gap%3D2%2C%20justify%3D%22start%22)%0A%20%20%20%20return%20browse_factory%2C%20min_score%2C%20n_show%0A%0A%0A%40app.cell%0Adef%20_(browse_factory%2C%20lib%2C%20min_score%2C%20n_show)%3A%0A%20%20%20%20factory%20%3D%20None%20if%20browse_factory.value%20%3D%3D%20%22(any)%22%20else%20browse_factory.value%0A%20%20%20%20candidates%20%3D%20lib.load_experiments(factory)%0A%20%20%20%20candidates%20%3D%20%5Bc%20for%20c%20in%20candidates%20if%20c%5B%22kept%22%5D%20%3D%3D%20%22yes%22%20and%20float(c%5B%22composite_score%22%5D)%20%3E%3D%20min_score.value%5D%0A%20%20%20%20candidates.sort(key%3Dlambda%20r%3A%20-float(r%5B%22composite_score%22%5D))%0A%20%20%20%20candidates%20%3D%20candidates%5B%3An_show.value%5D%0A%20%20%20%20return%20(candidates%2C)%0A%0A%0A%40app.cell%0Adef%20_(candidates%2C%20lib%2C%20mo%2C%20queued_ids)%3A%0A%20%20%20%20if%20not%20candidates%3A%0A%20%20%20%20%20%20%20%20browse_grid%20%3D%20mo.md(%22_No%20pieces%20match%20the%20filter._%22)%0A%20%20%20%20%20%20%20%20add_buttons%20%3D%20%7B%7D%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20add_buttons%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20cand%5B%22id%22%5D%3A%20mo.ui.run_button(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label%3D%22%E2%9C%93%20in%20queue%22%20if%20cand%5B%22id%22%5D%20in%20queued_ids%20else%20%22%2B%20add%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20cand%20in%20candidates%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20browse_cards%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.Html(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cdiv%20style%3D%22text-align%3Acenter%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cimg%20src%3D%22%7Blib.png_data_uri(lib.piece_path(cand%5B%22id%22%5D%2C%20%22png%22))%7D%22%20'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'style%3D%22width%3A160px%3B%20border%3A1px%20solid%20%23444%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cdiv%20style%3D%22font-size%3A11px%3B%20font-family%3Amonospace%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%7Bcand%5B%22id%22%5D%7D%3Cbr%3E%7Bfloat(cand%5B%22composite_score%22%5D)%3A.1f%7D'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3C%2Fdiv%3E%3C%2Fdiv%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20add_buttons%5Bcand%5B%22id%22%5D%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%20gap%3D0.3)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20cand%20in%20candidates%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20lib.piece_path(cand%5B%22id%22%5D%2C%20%22png%22).exists()%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20browse_grid%20%3D%20mo.hstack(browse_cards%2C%20wrap%3DTrue%2C%20gap%3D1%2C%20justify%3D%22start%22)%0A%20%20%20%20browse_grid%0A%20%20%20%20return%20(add_buttons%2C)%0A%0A%0A%40app.cell%0Adef%20_(add_buttons%2C%20datetime%2C%20load_queue%2C%20save_queue%2C%20timezone)%3A%0A%20%20%20%20%23%20Process%20any%20add-button%20clicks%0A%20%20%20%20add_log%20%3D%20%5B%5D%0A%20%20%20%20if%20add_buttons%3A%0A%20%20%20%20%20%20%20%20existing%20%3D%20load_queue()%0A%20%20%20%20%20%20%20%20existing_ids%20%3D%20%7Br%5B%22id%22%5D%20for%20r%20in%20existing%7D%0A%20%20%20%20%20%20%20%20for%20cand_id%2C%20btn%20in%20add_buttons.items()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20btn.value%20and%20cand_id%20not%20in%20existing_ids%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ts%20%3D%20datetime.now(timezone.utc).strftime(%22%25Y-%25m-%25dT%25H%3A%25M%3A%25SZ%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20existing.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22id%22%3A%20cand_id%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22added_at%22%3A%20ts%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22status%22%3A%20%22queued%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22plotted_at%22%3A%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22notes%22%3A%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22est_min%22%3A%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20add_log.append(cand_id)%0A%20%20%20%20%20%20%20%20if%20add_log%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20save_queue(existing)%0A%20%20%20%20return%20(add_log%2C)%0A%0A%0A%40app.cell%0Adef%20_(add_log%2C%20mo)%3A%0A%20%20%20%20if%20add_log%3A%0A%20%20%20%20%20%20%20%20msg%20%3D%20mo.md(f%22%E2%9C%85%20Added%20%7Blen(add_log)%7D%20to%20queue%3A%20%22%20%2B%20%22%2C%20%22.join(add_log))%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20msg%20%3D%20mo.md(%22%22)%0A%20%20%20%20msg%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(lib%2C%20mo%2C%20queue)%3A%0A%20%20%20%20mo.md(%22---%22)%0A%20%20%20%20queued%20%3D%20%5Br%20for%20r%20in%20queue%20if%20r%5B%22status%22%5D%20%3D%3D%20%22queued%22%5D%0A%20%20%20%20if%20not%20queued%3A%0A%20%20%20%20%20%20%20%20queue_view%20%3D%20mo.md(%22%23%23%23%20Queue%5Cn%5Cn_Empty._%22)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20queue_cards%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mo.Html(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cdiv%20style%3D%22text-align%3Acenter%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cimg%20src%3D%22%7Blib.png_data_uri(lib.piece_path(q%5B%22id%22%5D%2C%20%22png%22))%7D%22%20'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'style%3D%22width%3A160px%3B%20border%3A2px%20solid%20%234ade80%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cdiv%20style%3D%22font-size%3A11px%3B%20font-family%3Amonospace%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%7Bq%5B%22id%22%5D%7D%3Cbr%3Eadded%20%7Bq%5B%22added_at%22%5D%5B%3A10%5D%7D'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3C%2Fdiv%3E%3C%2Fdiv%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20q%20in%20queued%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20lib.piece_path(q%5B%22id%22%5D%2C%20%22png%22).exists()%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20queue_view%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(f%22%23%23%23%20Queue%20(%7Blen(queued)%7D%20pieces)%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.hstack(queue_cards%2C%20wrap%3DTrue%2C%20gap%3D1%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20%5D)%0A%20%20%20%20queue_view%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(lib%2C%20mo%2C%20queue)%3A%0A%20%20%20%20plotted%20%3D%20%5Br%20for%20r%20in%20queue%20if%20r%5B%22status%22%5D%20%3D%3D%20%22plotted%22%5D%0A%20%20%20%20if%20not%20plotted%3A%0A%20%20%20%20%20%20%20%20plotted_view%20%3D%20mo.md(%22%23%23%23%20Plotted%5Cn%5Cn_Nothing%20plotted%20yet._%22)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20plotted_cards%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.Html(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cdiv%20style%3D%22text-align%3Acenter%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cimg%20src%3D%22%7Blib.png_data_uri(lib.piece_path(p%5B%22id%22%5D%2C%20%22png%22))%7D%22%20'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'style%3D%22width%3A120px%3B%20border%3A2px%20solid%20%23fbbf24%3B%20opacity%3A0.85%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3Cdiv%20style%3D%22font-size%3A10px%3B%20font-family%3Amonospace%3B%22%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%7Bp%5B%22id%22%5D%7D%3Cbr%3E%7Bp.get(%22plotted_at%22%2C%20%22%22)%5B%3A10%5D%7D'%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f'%3C%2Fdiv%3E%3C%2Fdiv%3E'%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20p%20in%20plotted%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20lib.piece_path(p%5B%22id%22%5D%2C%20%22png%22).exists()%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20plotted_view%20%3D%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(f%22%23%23%23%20Plotted%20(%7Blen(plotted)%7D%20pieces)%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.hstack(plotted_cards%2C%20wrap%3DTrue%2C%20gap%3D1%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20%5D)%0A%20%20%20%20plotted_view%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
8796306d16c68f9bdfc565d0c8d2df71