ms-qg 0.12.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ms_qg-0.12.1/.claude/skills/qg-config-authoring/SKILL.md +250 -0
- ms_qg-0.12.1/.dockerignore +26 -0
- ms_qg-0.12.1/.env.example +6 -0
- ms_qg-0.12.1/.github/workflows/ci.yml +237 -0
- ms_qg-0.12.1/.github/workflows/pages.yml +57 -0
- ms_qg-0.12.1/.github/workflows/publish.yml +101 -0
- ms_qg-0.12.1/.github/workflows/security.yml +26 -0
- ms_qg-0.12.1/.gitignore +45 -0
- ms_qg-0.12.1/.gitlab-ci.yml +95 -0
- ms_qg-0.12.1/.pre-commit-config.yaml +119 -0
- ms_qg-0.12.1/.python-version +1 -0
- ms_qg-0.12.1/.qg_settings.toml.example +9 -0
- ms_qg-0.12.1/.zenodo.json +60 -0
- ms_qg-0.12.1/AGENTS.md +620 -0
- ms_qg-0.12.1/CHANGELOG.md +365 -0
- ms_qg-0.12.1/CITATION.cff +53 -0
- ms_qg-0.12.1/CLAUDE.md +7 -0
- ms_qg-0.12.1/CONTRIBUTING.md +52 -0
- ms_qg-0.12.1/Dockerfile +7 -0
- ms_qg-0.12.1/LICENSE +161 -0
- ms_qg-0.12.1/Makefile +121 -0
- ms_qg-0.12.1/PKG-INFO +154 -0
- ms_qg-0.12.1/README.md +111 -0
- ms_qg-0.12.1/Snakefile +16 -0
- ms_qg-0.12.1/docs/Makefile +21 -0
- ms_qg-0.12.1/docs/bfabric.md +85 -0
- ms_qg-0.12.1/docs/developers/deployment.md +72 -0
- ms_qg-0.12.1/docs/developers/forge_sync.md +109 -0
- ms_qg-0.12.1/docs/developers/history.md +132 -0
- ms_qg-0.12.1/docs/developers/marimo_shared_app_architecture.md +247 -0
- ms_qg-0.12.1/docs/developers/user_modes.md +50 -0
- ms_qg-0.12.1/docs/examples/README.md +61 -0
- ms_qg-0.12.1/docs/examples/figures/README.md +45 -0
- ms_qg-0.12.1/docs/examples/figures/editor_overview.png +0 -0
- ms_qg-0.12.1/docs/examples/figures/editor_qc_layouts_well.png +0 -0
- ms_qg-0.12.1/docs/examples/figures/editor_queue_patterns.png +0 -0
- ms_qg-0.12.1/docs/examples/figures/editor_samples.png +0 -0
- ms_qg-0.12.1/docs/examples/figures/imbalance_sweep_stats.csv +28 -0
- ms_qg-0.12.1/docs/examples/figures/make_imbalance_sweep_figure.py +203 -0
- ms_qg-0.12.1/docs/examples/figures/make_plate_figure.py +142 -0
- ms_qg-0.12.1/docs/examples/figures/make_randomization_figure.py +204 -0
- ms_qg-0.12.1/docs/examples/figures/make_seed_sweep_figure.py +293 -0
- ms_qg-0.12.1/docs/examples/figures/seed_sweep_stats.csv +9 -0
- ms_qg-0.12.1/docs/examples/figures/shot_editor.py +63 -0
- ms_qg-0.12.1/docs/examples/figures/shot_local_tabs.py +84 -0
- ms_qg-0.12.1/docs/examples/figures/shot_viz.py +185 -0
- ms_qg-0.12.1/docs/examples/multi_container_samples.csv +31 -0
- ms_qg-0.12.1/docs/examples/plate_different_groups.csv +43 -0
- ms_qg-0.12.1/docs/examples/plate_samples_5x5.csv +26 -0
- ms_qg-0.12.1/docs/examples/plate_samples_80.csv +81 -0
- ms_qg-0.12.1/docs/examples/vial_different_groups.csv +43 -0
- ms_qg-0.12.1/docs/examples/vial_samples_5x5.csv +26 -0
- ms_qg-0.12.1/docs/examples/vial_samples_5x5.xlsx +0 -0
- ms_qg-0.12.1/docs/examples/vial_samples_80.csv +81 -0
- ms_qg-0.12.1/docs/index.md +46 -0
- ms_qg-0.12.1/docs/reference/algorithm.md +241 -0
- ms_qg-0.12.1/docs/reference/config.md +403 -0
- ms_qg-0.12.1/docs/users/editor_guide.md +110 -0
- ms_qg-0.12.1/docs/users/local_app.md +81 -0
- ms_qg-0.12.1/mkdocs.yml +69 -0
- ms_qg-0.12.1/pyproject.toml +209 -0
- ms_qg-0.12.1/qg_configs/README.md +21 -0
- ms_qg-0.12.1/qg_configs/core/formatting/instruments.csv +17 -0
- ms_qg-0.12.1/qg_configs/core/formatting/output_formats.toml +92 -0
- ms_qg-0.12.1/qg_configs/core/methods/Lipidomics/EXPLORIS_3_methods.csv +3 -0
- ms_qg-0.12.1/qg_configs/core/methods/Lipidomics/EXPLORIS_4_methods.csv +3 -0
- ms_qg-0.12.1/qg_configs/core/methods/Lipidomics/QEXACTIVEHF_2_methods.csv +3 -0
- ms_qg-0.12.1/qg_configs/core/methods/Metabolomics/EXPLORIS_3_methods.csv +3 -0
- ms_qg-0.12.1/qg_configs/core/methods/Metabolomics/QEXACTIVEHF_2_methods.csv +3 -0
- ms_qg-0.12.1/qg_configs/core/methods/Metabolomics/QUANTIVA_1_methods.csv +3 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/ASCEND_1_methods.csv +9 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/ASTRAL_1_methods.csv +6 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/EXPLORIS_1_methods.csv +9 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/EXPLORIS_2_methods.csv +5 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/EXPLORIS_5_methods.csv +5 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/LUMOS_2_methods.csv +7 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/QEXACTIVE_1_methods.csv +5 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/TIMSTOFFLEX_1_methods.csv +5 -0
- ms_qg-0.12.1/qg_configs/core/methods/Proteomics/TIMSTOF_1_methods.csv +5 -0
- ms_qg-0.12.1/qg_configs/core/methods/Testing/ASTRAL_1_methods.csv +5 -0
- ms_qg-0.12.1/qg_configs/core/position/plate_layouts.toml +19 -0
- ms_qg-0.12.1/qg_configs/core/position/qc_layouts_tip.csv +6 -0
- ms_qg-0.12.1/qg_configs/core/position/qc_layouts_well.csv +90 -0
- ms_qg-0.12.1/qg_configs/core/position/sampler.toml +23 -0
- ms_qg-0.12.1/qg_configs/core/position/sampler_plate_layouts.csv +6 -0
- ms_qg-0.12.1/qg_configs/core/structure/queue_patterns.toml +121 -0
- ms_qg-0.12.1/qg_configs/core/structure/samples.csv +39 -0
- ms_qg-0.12.1/qg_configs/ui/instrument_config.csv +23 -0
- ms_qg-0.12.1/qg_configs/ui/tech_area_defaults.toml +32 -0
- ms_qg-0.12.1/scripts/core_profile_check.py +43 -0
- ms_qg-0.12.1/scripts/package_smoke.py +60 -0
- ms_qg-0.12.1/scripts/release.py +182 -0
- ms_qg-0.12.1/scripts/sync_forges.py +240 -0
- ms_qg-0.12.1/src/qg/__init__.py +5 -0
- ms_qg-0.12.1/src/qg/apps/__init__.py +1 -0
- ms_qg-0.12.1/src/qg/apps/_bfabric_auth.py +157 -0
- ms_qg-0.12.1/src/qg/apps/bfabric_app.py +28 -0
- ms_qg-0.12.1/src/qg/apps/bfabric_app_editor.py +19 -0
- ms_qg-0.12.1/src/qg/apps/config_editor.py +904 -0
- ms_qg-0.12.1/src/qg/apps/editor_core.py +270 -0
- ms_qg-0.12.1/src/qg/apps/integrations/__init__.py +5 -0
- ms_qg-0.12.1/src/qg/apps/integrations/bfabric_context.py +10 -0
- ms_qg-0.12.1/src/qg/apps/integrations/bfabric_workunit.py +55 -0
- ms_qg-0.12.1/src/qg/apps/integrations/example_params.py +73 -0
- ms_qg-0.12.1/src/qg/apps/integrations/example_samples.py +108 -0
- ms_qg-0.12.1/src/qg/apps/integrations/local_samples.py +134 -0
- ms_qg-0.12.1/src/qg/apps/launcher_local.py +21 -0
- ms_qg-0.12.1/src/qg/apps/queue_app.py +1147 -0
- ms_qg-0.12.1/src/qg/apps/queue_app_local.py +973 -0
- ms_qg-0.12.1/src/qg/apps/queue_app_shared.py +1036 -0
- ms_qg-0.12.1/src/qg/artifacts.py +83 -0
- ms_qg-0.12.1/src/qg/bfabric_samples.py +357 -0
- ms_qg-0.12.1/src/qg/bfabric_utils.py +188 -0
- ms_qg-0.12.1/src/qg/cli/__init__.py +1 -0
- ms_qg-0.12.1/src/qg/cli/assign_positions.py +52 -0
- ms_qg-0.12.1/src/qg/cli/find_projects.py +235 -0
- ms_qg-0.12.1/src/qg/cli/generate_queues.py +69 -0
- ms_qg-0.12.1/src/qg/cli/refresh_cache.py +93 -0
- ms_qg-0.12.1/src/qg/cli/validate_config.py +33 -0
- ms_qg-0.12.1/src/qg/config_models/__init__.py +0 -0
- ms_qg-0.12.1/src/qg/config_models/formatting.py +227 -0
- ms_qg-0.12.1/src/qg/config_models/loader.py +788 -0
- ms_qg-0.12.1/src/qg/config_models/methods.py +215 -0
- ms_qg-0.12.1/src/qg/config_models/positions.py +433 -0
- ms_qg-0.12.1/src/qg/config_models/resolved.py +160 -0
- ms_qg-0.12.1/src/qg/config_models/structure.py +328 -0
- ms_qg-0.12.1/src/qg/config_models/ui.py +183 -0
- ms_qg-0.12.1/src/qg/examples/__init__.py +6 -0
- ms_qg-0.12.1/src/qg/examples/params/__init__.py +1 -0
- ms_qg-0.12.1/src/qg/examples/params/lipidomics_standard.json +446 -0
- ms_qg-0.12.1/src/qg/examples/params/repro_proteomics_12.json +327 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/__init__.py +1 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/multi_container_samples.csv +31 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/plate_different_groups.csv +43 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/plate_samples_5x5.csv +26 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/plate_samples_80.csv +81 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/vial_different_groups.csv +43 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/vial_samples_5x5.csv +26 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/vial_samples_5x5.xlsx +0 -0
- ms_qg-0.12.1/src/qg/examples/sample_tables/vial_samples_80.csv +81 -0
- ms_qg-0.12.1/src/qg/generator.py +477 -0
- ms_qg-0.12.1/src/qg/gitlab/__init__.py +0 -0
- ms_qg-0.12.1/src/qg/gitlab/_git.py +25 -0
- ms_qg-0.12.1/src/qg/gitlab/config_bridge.py +64 -0
- ms_qg-0.12.1/src/qg/gitlab/launcher.py +105 -0
- ms_qg-0.12.1/src/qg/gitlab/service.py +93 -0
- ms_qg-0.12.1/src/qg/gitlab/settings.py +95 -0
- ms_qg-0.12.1/src/qg/hystar_xml_writer.py +50 -0
- ms_qg-0.12.1/src/qg/logging_setup.py +73 -0
- ms_qg-0.12.1/src/qg/params_models.py +354 -0
- ms_qg-0.12.1/src/qg/positionV2.py +549 -0
- ms_qg-0.12.1/src/qg/qc_layout.py +97 -0
- ms_qg-0.12.1/src/qg/qc_positions.py +137 -0
- ms_qg-0.12.1/src/qg/queue_builder.py +137 -0
- ms_qg-0.12.1/src/qg/queue_structure.py +172 -0
- ms_qg-0.12.1/src/qg/randomize.py +163 -0
- ms_qg-0.12.1/src/qg/sample_rows.py +98 -0
- ms_qg-0.12.1/src/qg/utils.py +107 -0
- ms_qg-0.12.1/src/qg/viz/__init__.py +1 -0
- ms_qg-0.12.1/src/qg/viz/balance.py +125 -0
- ms_qg-0.12.1/src/qg/viz/plate.py +299 -0
- ms_qg-0.12.1/src/qg/viz/timeline.py +253 -0
- ms_qg-0.12.1/src/qg/writers.py +52 -0
- ms_qg-0.12.1/tests/__init__.py +1 -0
- ms_qg-0.12.1/tests/gui/AGENTS.md +107 -0
- ms_qg-0.12.1/tests/gui/README.md +117 -0
- ms_qg-0.12.1/tests/gui/__init__.py +0 -0
- ms_qg-0.12.1/tests/gui/_fake_bfabric.py +227 -0
- ms_qg-0.12.1/tests/gui/_helpers.py +288 -0
- ms_qg-0.12.1/tests/gui/_test_app.py +106 -0
- ms_qg-0.12.1/tests/gui/conftest.py +250 -0
- ms_qg-0.12.1/tests/gui/features/concentrations.feature +20 -0
- ms_qg-0.12.1/tests/gui/features/employee_empty_order.feature +10 -0
- ms_qg-0.12.1/tests/gui/features/generation_error.feature +13 -0
- ms_qg-0.12.1/tests/gui/features/happy_path.feature +18 -0
- ms_qg-0.12.1/tests/gui/features/hystar_output.feature +18 -0
- ms_qg-0.12.1/tests/gui/features/launching_order.feature +15 -0
- ms_qg-0.12.1/tests/gui/features/lipidomics.feature +15 -0
- ms_qg-0.12.1/tests/gui/features/metabolomics_download.feature +15 -0
- ms_qg-0.12.1/tests/gui/features/metabolomics_polarity.feature +26 -0
- ms_qg-0.12.1/tests/gui/features/multi_container.feature +21 -0
- ms_qg-0.12.1/tests/gui/features/non_employee.feature +18 -0
- ms_qg-0.12.1/tests/gui/features/non_employee_empty.feature +9 -0
- ms_qg-0.12.1/tests/gui/features/noqc.feature +40 -0
- ms_qg-0.12.1/tests/gui/features/order_queue_type.feature +71 -0
- ms_qg-0.12.1/tests/gui/features/overrides.feature +28 -0
- ms_qg-0.12.1/tests/gui/features/params_json.feature +17 -0
- ms_qg-0.12.1/tests/gui/features/plate_picker.feature +46 -0
- ms_qg-0.12.1/tests/gui/features/project_queue_type.feature +28 -0
- ms_qg-0.12.1/tests/gui/features/qc_visits.feature +14 -0
- ms_qg-0.12.1/tests/gui/features/queue_name.feature +27 -0
- ms_qg-0.12.1/tests/gui/features/randomization.feature +17 -0
- ms_qg-0.12.1/tests/gui/features/sample_exclusion.feature +17 -0
- ms_qg-0.12.1/tests/gui/features/sample_name_suffix.feature +18 -0
- ms_qg-0.12.1/tests/gui/features/sample_source.feature +30 -0
- ms_qg-0.12.1/tests/gui/features/show_plate.feature +18 -0
- ms_qg-0.12.1/tests/gui/features/tray_capacity.feature +22 -0
- ms_qg-0.12.1/tests/gui/features/upload_workunit.feature +15 -0
- ms_qg-0.12.1/tests/gui/features/valid_combinations_tab.feature +10 -0
- ms_qg-0.12.1/tests/gui/features/validation_required.feature +8 -0
- ms_qg-0.12.1/tests/gui/features/vial_mode.feature +26 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/container_37190.json +10 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/orderitems_37180.json +6 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/orderitems_37181.json +3 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/orderitems_37182.json +6 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/orderitems_37183.json +10 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/orderitems_37190.json +3 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/orderitems_37195.json +3 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/orderitems_37200.json +7 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/plates_37180.json +19 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/plates_37181.json +11 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/plates_37183.json +11 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/plates_37190.json +15 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/plates_37195.json +13 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/plates_37200.json +22 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37180.json +14 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37181.json +6 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37182.json +8 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37183.json +10 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37190.json +10 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37195.json +8 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37196.json +5 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_37200.json +12 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/samples_99999.json +1 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/scenario_project_support/plates_37210.json +11 -0
- ms_qg-0.12.1/tests/gui/fixtures/bfabric/scenario_project_support/samples_37210.json +6 -0
- ms_qg-0.12.1/tests/gui/fixtures/expected/proteomics_astral1_37180.csv +22 -0
- ms_qg-0.12.1/tests/gui/fixtures/projects.csv +11 -0
- ms_qg-0.12.1/tests/gui/test_concentrations.py +114 -0
- ms_qg-0.12.1/tests/gui/test_employee_empty_order.py +44 -0
- ms_qg-0.12.1/tests/gui/test_generation_error.py +41 -0
- ms_qg-0.12.1/tests/gui/test_happy_path.py +114 -0
- ms_qg-0.12.1/tests/gui/test_hystar_output.py +93 -0
- ms_qg-0.12.1/tests/gui/test_launching_order.py +56 -0
- ms_qg-0.12.1/tests/gui/test_lipidomics.py +73 -0
- ms_qg-0.12.1/tests/gui/test_metabolomics_download.py +73 -0
- ms_qg-0.12.1/tests/gui/test_metabolomics_polarity.py +80 -0
- ms_qg-0.12.1/tests/gui/test_multi_container.py +158 -0
- ms_qg-0.12.1/tests/gui/test_non_employee.py +52 -0
- ms_qg-0.12.1/tests/gui/test_non_employee_empty.py +34 -0
- ms_qg-0.12.1/tests/gui/test_noqc.py +52 -0
- ms_qg-0.12.1/tests/gui/test_order_queue_type.py +66 -0
- ms_qg-0.12.1/tests/gui/test_overrides.py +101 -0
- ms_qg-0.12.1/tests/gui/test_params_json.py +105 -0
- ms_qg-0.12.1/tests/gui/test_plate_picker.py +53 -0
- ms_qg-0.12.1/tests/gui/test_project_queue_type.py +49 -0
- ms_qg-0.12.1/tests/gui/test_qc_visits.py +65 -0
- ms_qg-0.12.1/tests/gui/test_queue_name.py +60 -0
- ms_qg-0.12.1/tests/gui/test_randomization.py +86 -0
- ms_qg-0.12.1/tests/gui/test_sample_exclusion.py +110 -0
- ms_qg-0.12.1/tests/gui/test_sample_name_suffix.py +111 -0
- ms_qg-0.12.1/tests/gui/test_sample_source.py +60 -0
- ms_qg-0.12.1/tests/gui/test_show_plate.py +74 -0
- ms_qg-0.12.1/tests/gui/test_tray_capacity.py +45 -0
- ms_qg-0.12.1/tests/gui/test_upload_workunit.py +57 -0
- ms_qg-0.12.1/tests/gui/test_valid_combinations_tab.py +47 -0
- ms_qg-0.12.1/tests/gui/test_validation_required.py +25 -0
- ms_qg-0.12.1/tests/gui/test_vial_mode.py +63 -0
- ms_qg-0.12.1/tests/gui_local/__init__.py +0 -0
- ms_qg-0.12.1/tests/gui_local/conftest.py +121 -0
- ms_qg-0.12.1/tests/gui_local/features/local_happy_path.feature +27 -0
- ms_qg-0.12.1/tests/gui_local/features/local_viz.feature +18 -0
- ms_qg-0.12.1/tests/gui_local/test_local_happy_path.py +103 -0
- ms_qg-0.12.1/tests/gui_local/test_local_viz.py +74 -0
- ms_qg-0.12.1/tests/gui_local/test_reproduce_mode.py +61 -0
- ms_qg-0.12.1/tests/helpers.py +139 -0
- ms_qg-0.12.1/tests/test_artifacts.py +117 -0
- ms_qg-0.12.1/tests/test_bfabric_auth.py +132 -0
- ms_qg-0.12.1/tests/test_bfabric_samples.py +400 -0
- ms_qg-0.12.1/tests/test_bfabric_utils.py +323 -0
- ms_qg-0.12.1/tests/test_cli.py +314 -0
- ms_qg-0.12.1/tests/test_config_authoring.py +197 -0
- ms_qg-0.12.1/tests/test_config_integration.py +497 -0
- ms_qg-0.12.1/tests/test_config_models_integration.py +499 -0
- ms_qg-0.12.1/tests/test_editor_core.py +137 -0
- ms_qg-0.12.1/tests/test_example_samples.py +79 -0
- ms_qg-0.12.1/tests/test_generator.py +1740 -0
- ms_qg-0.12.1/tests/test_gitlab.py +421 -0
- ms_qg-0.12.1/tests/test_hystar_xml_writer.py +125 -0
- ms_qg-0.12.1/tests/test_local_samples.py +155 -0
- ms_qg-0.12.1/tests/test_positioning.py +93 -0
- ms_qg-0.12.1/tests/test_positions.py +929 -0
- ms_qg-0.12.1/tests/test_queue_app_shared.py +709 -0
- ms_qg-0.12.1/tests/test_queue_builder.py +241 -0
- ms_qg-0.12.1/tests/test_queue_structure.py +358 -0
- ms_qg-0.12.1/tests/test_queue_structure_explicit.py +133 -0
- ms_qg-0.12.1/tests/test_randomize.py +481 -0
- ms_qg-0.12.1/tests/test_refresh_cache.py +44 -0
- ms_qg-0.12.1/tests/test_resolved_config.py +285 -0
- ms_qg-0.12.1/tests/test_utils.py +76 -0
- ms_qg-0.12.1/tests/test_viz_balance.py +124 -0
- ms_qg-0.12.1/tests/test_viz_plate.py +210 -0
- ms_qg-0.12.1/tests/test_viz_timeline.py +186 -0
- ms_qg-0.12.1/tests/test_writers.py +68 -0
- ms_qg-0.12.1/uv.lock +2876 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qg-config-authoring
|
|
3
|
+
description: Use when adding to or editing the qg queue-generation configuration under qg_configs/ — standing up a new technology, adding an instrument, adding a plate layout with its QC samples and a queue pattern, or adding/adjusting an output format. Triggers when the user mentions qg_configs, instruments.csv, queue_patterns.toml, plate_layouts.toml, qc_layouts, samples.csv, output_formats.toml, adding an instrument / technology / QC sample / plate layout, or keeping the config files consistent. Explains which files each change touches and the cross-file rules qg_configuration() enforces at load.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Authoring qg configuration
|
|
7
|
+
|
|
8
|
+
`qg` is configuration-driven: technologies, instruments, samplers, plate layouts,
|
|
9
|
+
QC samples, patterns, and output formats are all rows in CSVs or tables in TOML
|
|
10
|
+
files under `qg_configs/`. Adding any of them is a config edit, not a code change.
|
|
11
|
+
This skill is the playbook for the recurring edits and — crucially — the cross-file
|
|
12
|
+
dependencies between them.
|
|
13
|
+
|
|
14
|
+
## How to make a change (and the one rule that matters)
|
|
15
|
+
|
|
16
|
+
The config files are **not** cross-validated incrementally as you edit them. They
|
|
17
|
+
are validated as a set, at load, by `qg_configuration()`
|
|
18
|
+
(`src/qg/config_models/loader.py:664`), which reads all twelve config files and then
|
|
19
|
+
runs the cross-reference validators (`loader.py:122-261`). A mismatch surfaces there
|
|
20
|
+
as a `ConfigValidationError` — **except** `ui/instrument_config.csv`, which is not
|
|
21
|
+
cross-validated and instead fails later as a runtime lookup in the app. So:
|
|
22
|
+
|
|
23
|
+
1. Make the edits across **all** the files a change touches (the stories below list them).
|
|
24
|
+
2. Run `uv run qg-validate` (add `-c <dir>` for a non-default config dir). This loads
|
|
25
|
+
and cross-validates everything; a clean exit means the set is consistent.
|
|
26
|
+
3. Keep `ui/instrument_config.csv` in sync by hand — `qg-validate` will not catch a
|
|
27
|
+
missing or mismatched row there.
|
|
28
|
+
|
|
29
|
+
**The consistency rule — the QC-sample → reserved-position cascade.** Every QC sample
|
|
30
|
+
id a queue pattern references (in its `start` / `middle` / `end` / `separation` /
|
|
31
|
+
`middle_extended` lists) must:
|
|
32
|
+
|
|
33
|
+
- exist in `core/structure/samples.csv` for the **same** `tech_area`, and
|
|
34
|
+
- have a **reserved position** in a QC layout
|
|
35
|
+
(`core/position/qc_layouts_well.csv` for well samplers, `qc_layouts_tip.csv` for tip
|
|
36
|
+
samplers) for the `(tech_area, qc_layout_name, plate_layout)` the queue uses.
|
|
37
|
+
|
|
38
|
+
This is why "add a QC sample to a pattern" is never a one-file edit: it cascades into
|
|
39
|
+
the QC layout, and a new layout into the plate-layout and sampler-mapping files. The
|
|
40
|
+
validators that enforce it are `_validate_pattern_sample_refs`,
|
|
41
|
+
`_validate_layout_sample_refs`, and `_validate_pattern_layout_compatibility`
|
|
42
|
+
(`loader.py:122-261`).
|
|
43
|
+
|
|
44
|
+
For the full field-by-field schema of each file, see
|
|
45
|
+
[docs/reference/config.md](../../../docs/reference/config.md). For the instrument-row
|
|
46
|
+
specifics, see "Adding a New Instrument" in [AGENTS.md](../../../AGENTS.md).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Story 1 — Stand up a new technology end-to-end
|
|
51
|
+
|
|
52
|
+
This is the spine: every other story is a slice of it. The worked example below is a
|
|
53
|
+
complete, minimal technology `Testing_v3` (one instrument, the reused Vanquish well
|
|
54
|
+
sampler, the reused `xcalibur` output format, a new 12-well plate layout, three sample
|
|
55
|
+
types, one QC pattern). It is exactly what `tests/test_config_authoring.py` builds and
|
|
56
|
+
asserts is loadable, valid, and usable — copy it and adapt.
|
|
57
|
+
|
|
58
|
+
Author the files in this order (technology → plate layout → pattern):
|
|
59
|
+
|
|
60
|
+
**1. Declare the technology** — `qg_configs/ui/tech_area_defaults.toml`:
|
|
61
|
+
|
|
62
|
+
```toml
|
|
63
|
+
[Testing_v3]
|
|
64
|
+
default_user = ""
|
|
65
|
+
default_polarities = ["pos"]
|
|
66
|
+
bfabric_areas = []
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**2. Define the sample types** — append to `qg_configs/core/structure/samples.csv`
|
|
70
|
+
(header: `tech_area,sample_id,sample_name,sample_type,level,description,inj_vol,file_name_template`):
|
|
71
|
+
|
|
72
|
+
```csv
|
|
73
|
+
Testing_v3,default,"",Unknown,,Default Testing_v3 user sample,2.0,{date}_{run}_C{container}_S{sample_id}_{sample_name}
|
|
74
|
+
Testing_v3,QCv3,QCv3,QC,,Testing_v3 QC sample,2.0,{date}_{run}_C{container}_{sample_name}
|
|
75
|
+
Testing_v3,cleanv3,cleanv3,Blank,,Testing_v3 column cleanup blank,2.0,{date}_{run}_C{container}_{sample_name}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Every technology needs a `default` row (the user-sample template). QC and blank rows
|
|
79
|
+
are the injections a pattern can reference.
|
|
80
|
+
|
|
81
|
+
**3. Add the instrument + its methods file.** Append to
|
|
82
|
+
`qg_configs/core/formatting/instruments.csv`
|
|
83
|
+
(header: `tech_area,instrument,methods_file,path_template`):
|
|
84
|
+
|
|
85
|
+
```csv
|
|
86
|
+
Testing_v3,TESTING_V3_1,methods/Testing_v3/TESTING_V3_1_methods.csv,D:\Data2San\p{container}\Testing_v3\TESTING_V3_1\{user}_{date}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Create the file the `methods_file` column points at,
|
|
90
|
+
`qg_configs/core/methods/Testing_v3/TESTING_V3_1_methods.csv`
|
|
91
|
+
(header: `sample_type,polarity,method_name,method_path`):
|
|
92
|
+
|
|
93
|
+
```csv
|
|
94
|
+
default,pos,DDA,C:\Xcalibur\methods\
|
|
95
|
+
QCv3,pos,DDA,C:\Xcalibur\methods\__autoQC\QCv3
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
A single-polarity technology (`pos` only) needs just `pos` rows; metabolomics /
|
|
99
|
+
lipidomics need both `pos` and `neg` because of polarity expansion. The `default` row
|
|
100
|
+
covers user samples; add a per-QC row for any QC sample placed in the queue.
|
|
101
|
+
|
|
102
|
+
**4. Add the plate layout + sampler mapping.** Append to
|
|
103
|
+
`qg_configs/core/position/plate_layouts.toml`:
|
|
104
|
+
|
|
105
|
+
```toml
|
|
106
|
+
[TestingV3_12]
|
|
107
|
+
rows = ["A", "B"]
|
|
108
|
+
cols = [1, 2, 3, 4, 5, 6]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Map it to a sampler + queue type in
|
|
112
|
+
`qg_configs/core/position/sampler_plate_layouts.csv`
|
|
113
|
+
(header: `sampler,plate_layout,queue_type`):
|
|
114
|
+
|
|
115
|
+
```csv
|
|
116
|
+
Vanquish,TestingV3_12,Vial
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
(Reusing the existing `Vanquish` sampler — no `sampler.toml` edit needed. Adding a new
|
|
120
|
+
sampler is a separate, larger change.)
|
|
121
|
+
|
|
122
|
+
**5. Reserve QC positions** for the layout — append to
|
|
123
|
+
`qg_configs/core/position/qc_layouts_well.csv`
|
|
124
|
+
(header: `tech_area,qc_layout_name,plate_layout,sample_id,tray,row,col`):
|
|
125
|
+
|
|
126
|
+
```csv
|
|
127
|
+
Testing_v3,v3std,TestingV3_12,QCv3,B,B,6
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`row` must be one of the layout's `rows` and `col` one of its `cols`. This is the
|
|
131
|
+
reserved-position half of the cascade rule.
|
|
132
|
+
|
|
133
|
+
**6. Create the pattern** that uses those QC samples — append to
|
|
134
|
+
`qg_configs/core/structure/queue_patterns.toml`:
|
|
135
|
+
|
|
136
|
+
```toml
|
|
137
|
+
[Testing_v3.simple]
|
|
138
|
+
description = "Testing_v3 simple pattern: QC at start and end"
|
|
139
|
+
run_QC_after_n_samples = 4
|
|
140
|
+
start = ["QCv3"]
|
|
141
|
+
middle = []
|
|
142
|
+
end = ["QCv3"]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Reference **only** sample ids defined for `Testing_v3` in `samples.csv`, and make sure
|
|
146
|
+
each appears in the QC layout from step 5.
|
|
147
|
+
|
|
148
|
+
**7. Expose it in the UI** — append to `qg_configs/ui/instrument_config.csv`
|
|
149
|
+
(header: `tech_area,instrument,sampler,output_format,default_pattern`):
|
|
150
|
+
|
|
151
|
+
```csv
|
|
152
|
+
Testing_v3,TESTING_V3_1,Vanquish,xcalibur,simple
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`sampler`, `output_format`, and `default_pattern` must all exist (in `sampler.toml`,
|
|
156
|
+
`output_formats.toml`, and `queue_patterns.toml` respectively). This file is **not**
|
|
157
|
+
cross-validated at load — double-check it manually.
|
|
158
|
+
|
|
159
|
+
Then validate: `uv run qg-validate`. A clean exit means the technology is consistent
|
|
160
|
+
and queue generation will resolve it.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Story 2 — Add an instrument to an existing technology
|
|
165
|
+
|
|
166
|
+
Three files, and they must agree (this is step 3 of Story 1, plus the UI row):
|
|
167
|
+
|
|
168
|
+
- `qg_configs/core/formatting/instruments.csv` — a row whose `methods_file` points at…
|
|
169
|
+
- `qg_configs/core/methods/<Technology>/<Instrument>_methods.csv` — the methods file you create.
|
|
170
|
+
- `qg_configs/ui/instrument_config.csv` — one row per (instrument, sampler) pairing so
|
|
171
|
+
the instrument appears in the app menu.
|
|
172
|
+
|
|
173
|
+
The `instruments.csv` `methods_file` path and the actual file must match exactly;
|
|
174
|
+
`MethodsConfig.load()` discovers methods files from these rows. See "Adding a New
|
|
175
|
+
Instrument" in [AGENTS.md](../../../AGENTS.md) for the full checklist.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Story 3 — Add a plate layout, its QC samples, and a pattern
|
|
180
|
+
|
|
181
|
+
This is the cascade in concrete form. Touches:
|
|
182
|
+
|
|
183
|
+
- `qg_configs/core/position/plate_layouts.toml` — the geometry (`rows`, `cols`).
|
|
184
|
+
- `qg_configs/core/position/sampler_plate_layouts.csv` — map the layout to a sampler + `queue_type`.
|
|
185
|
+
- `qg_configs/core/structure/samples.csv` — any **new** QC / blank sample types.
|
|
186
|
+
- `qg_configs/core/position/qc_layouts_well.csv` (well samplers) **or** `qc_layouts_tip.csv`
|
|
187
|
+
(tip samplers, e.g. Evosep) — a reserved position for **every** QC sample the pattern
|
|
188
|
+
will reference, for this `(tech_area, qc_layout_name, plate_layout)`.
|
|
189
|
+
- `qg_configs/core/structure/queue_patterns.toml` — the pattern, referencing only those
|
|
190
|
+
QC sample ids.
|
|
191
|
+
|
|
192
|
+
If you add a QC sample to a pattern but forget its reserved position,
|
|
193
|
+
`_validate_pattern_layout_compatibility` fails at load with a message naming the
|
|
194
|
+
required sample ids. Add the position; don't remove the reference.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Story 4 — Add or adjust an output format
|
|
199
|
+
|
|
200
|
+
One file: `qg_configs/core/formatting/output_formats.toml`. Each format is a base table
|
|
201
|
+
plus a `columns` sub-table mapping output column name → internal field:
|
|
202
|
+
|
|
203
|
+
```toml
|
|
204
|
+
[xcalibur]
|
|
205
|
+
description = "Thermo XCalibur format"
|
|
206
|
+
file_extension = ".csv"
|
|
207
|
+
writer = "xcalibur_csv"
|
|
208
|
+
position_format = "{tray}:{grid_position}"
|
|
209
|
+
grid_position_format = "{row},{col}"
|
|
210
|
+
grid_position_conversion = "identity"
|
|
211
|
+
|
|
212
|
+
[xcalibur.columns]
|
|
213
|
+
"File Name" = "file_name"
|
|
214
|
+
Path = "data_path"
|
|
215
|
+
Position = "position"
|
|
216
|
+
"Inj Vol" = "inj_vol"
|
|
217
|
+
"Sample ID" = "sample_id"
|
|
218
|
+
"Sample Name" = "sample_name"
|
|
219
|
+
"Instrument Method" = "method"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`writer` must be a known writer (`xcalibur_csv`, `chronos_csv`, `hystar_xml`). To add a
|
|
223
|
+
column for **one technology only**, use a per-tech overlay sub-table — it merges over
|
|
224
|
+
the base `columns`:
|
|
225
|
+
|
|
226
|
+
```toml
|
|
227
|
+
# Metabolomics-only extra columns, inherited base columns otherwise
|
|
228
|
+
[xcalibur_sii.columns.Metabolomics]
|
|
229
|
+
"Sample Type" = "sample_type"
|
|
230
|
+
"Level" = "level"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
A literal value uses the `literal:` prefix (e.g. `"L3 Laboratory" = "literal:FGCZ"`).
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Verify your change
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
uv run qg-validate # loads + cross-validates the committed config
|
|
241
|
+
uv run qg-validate -c <config_dir> # …or a custom config directory
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Then generate a sample queue to confirm it is usable end-to-end — either through the
|
|
245
|
+
app, or programmatically as `tests/test_config_authoring.py` does
|
|
246
|
+
(`qg_configuration(<dir>)` → `QueueGenerator(cfg, queue_input).generate()`), or via the
|
|
247
|
+
CLI: `uv run qg <input.json> -o <out.csv> -c <config_dir>`.
|
|
248
|
+
|
|
249
|
+
If you add a real (committed) technology or instrument, also follow the Release Process
|
|
250
|
+
in [AGENTS.md](../../../AGENTS.md): a `CHANGELOG.md` bullet and a version bump.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Config file
|
|
10
|
+
.env
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv
|
|
14
|
+
__marimo__/
|
|
15
|
+
.claude/
|
|
16
|
+
queue_files/
|
|
17
|
+
*.pdf
|
|
18
|
+
*.svg
|
|
19
|
+
.snakemake/
|
|
20
|
+
*.html
|
|
21
|
+
*_files/
|
|
22
|
+
*.png
|
|
23
|
+
plans/
|
|
24
|
+
|
|
25
|
+
# actual data
|
|
26
|
+
bfabric_cache
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
VALIDATION_BFABRIC_INSTANCE=https://fgcz-bfabric.uzh.ch/bfabric/
|
|
2
|
+
SUPPORTED_BFABRIC_INSTANCES=["https://fgcz-bfabric.uzh.ch/bfabric/","https://fgcz-bfabric-test.uzh.ch/bfabric/"]
|
|
3
|
+
# It's recommended to define a relatively long and safely random secret key
|
|
4
|
+
#SECRET_KEY=
|
|
5
|
+
_FEEDER_PWD="TODO"
|
|
6
|
+
FEEDER_USER_CREDENTIALS={"https://fgcz-bfabric.uzh.ch/bfabric/":{"login":"pfeeder","password":"${_FEEDER_PWD}"},"https://fgcz-bfabric-test.uzh.ch/bfabric/":{"login":"pfeeder","password":"${_FEEDER_PWD}"}}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
# Callable so publish.yml can run this whole suite as a release gate before
|
|
9
|
+
# building the image (restores GitLab's test -> build stage ordering).
|
|
10
|
+
workflow_call:
|
|
11
|
+
inputs:
|
|
12
|
+
# publish.yml passes a unique value here so the release-gate run gets its
|
|
13
|
+
# own concurrency group and can't be cancelled by a concurrent push to main
|
|
14
|
+
# (a tag release is already safe — its ref makes the group unique).
|
|
15
|
+
concurrency-group:
|
|
16
|
+
type: string
|
|
17
|
+
required: false
|
|
18
|
+
default: ""
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
# Mirror GitLab's workflow.auto_cancel.on_new_commit: interruptible — a new push
|
|
24
|
+
# to the same ref cancels the in-flight run. When called as a release gate the
|
|
25
|
+
# caller supplies a unique group (above), so the gate itself is never cancelled.
|
|
26
|
+
concurrency:
|
|
27
|
+
group: ${{ inputs.concurrency-group || format('ci-{0}', github.ref) }}
|
|
28
|
+
cancel-in-progress: true
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
# Fast feedback: every non-browser test. No playwright, no apt — fails fast.
|
|
32
|
+
unit:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- name: Checkout
|
|
36
|
+
uses: actions/checkout@v7
|
|
37
|
+
with:
|
|
38
|
+
fetch-depth: 0
|
|
39
|
+
- name: Install uv
|
|
40
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
41
|
+
with:
|
|
42
|
+
enable-cache: true
|
|
43
|
+
- name: Install Python
|
|
44
|
+
run: uv python install
|
|
45
|
+
- name: Sync (dev + portal)
|
|
46
|
+
run: uv sync --frozen
|
|
47
|
+
- name: Run unit tests
|
|
48
|
+
run: >-
|
|
49
|
+
uv run --no-sync pytest
|
|
50
|
+
--ignore=tests/gui
|
|
51
|
+
--ignore=tests/gui_local
|
|
52
|
+
--cov=qg
|
|
53
|
+
--cov-branch
|
|
54
|
+
--cov-report=xml:coverage.xml
|
|
55
|
+
--cov-report=term-missing:skip-covered
|
|
56
|
+
- name: Check changed-line coverage
|
|
57
|
+
if: github.event_name == 'pull_request'
|
|
58
|
+
run: >-
|
|
59
|
+
uv run --no-sync diff-cover coverage.xml
|
|
60
|
+
--compare-branch="origin/${{ github.base_ref }}"
|
|
61
|
+
--fail-under=60
|
|
62
|
+
|
|
63
|
+
quality:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- name: Checkout
|
|
67
|
+
uses: actions/checkout@v7
|
|
68
|
+
- name: Install uv
|
|
69
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
70
|
+
with:
|
|
71
|
+
enable-cache: true
|
|
72
|
+
- name: Install Python
|
|
73
|
+
run: uv python install
|
|
74
|
+
- name: Sync (dev + portal)
|
|
75
|
+
run: uv sync --frozen
|
|
76
|
+
- name: Ruff lint
|
|
77
|
+
run: uv run --no-sync ruff check . --output-format=github
|
|
78
|
+
- name: Ruff format
|
|
79
|
+
run: uv run --no-sync ruff format --check .
|
|
80
|
+
- name: Dependency declarations
|
|
81
|
+
run: uv run --no-sync deptry .
|
|
82
|
+
|
|
83
|
+
package-smoke:
|
|
84
|
+
runs-on: ubuntu-latest
|
|
85
|
+
steps:
|
|
86
|
+
- name: Checkout
|
|
87
|
+
uses: actions/checkout@v7
|
|
88
|
+
- name: Install uv
|
|
89
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
90
|
+
with:
|
|
91
|
+
enable-cache: true
|
|
92
|
+
- name: Install Python
|
|
93
|
+
run: uv python install
|
|
94
|
+
- name: Sync core development environment
|
|
95
|
+
run: uv sync --frozen --no-group portal
|
|
96
|
+
- name: Build and inspect distributions
|
|
97
|
+
run: uv run --no-sync python scripts/package_smoke.py
|
|
98
|
+
|
|
99
|
+
# Floor gate: `pip install qg` on the oldest supported interpreter must import,
|
|
100
|
+
# expose its CLIs, resolve its bundled configs, and pass the non-browser suite.
|
|
101
|
+
# Installs the built wheel with pip (not uv sync) so this mirrors what a reader
|
|
102
|
+
# of the README actually runs, and guards the requires-python floor from drifting
|
|
103
|
+
# up to the development interpreter unnoticed.
|
|
104
|
+
install-floor:
|
|
105
|
+
runs-on: ubuntu-latest
|
|
106
|
+
steps:
|
|
107
|
+
- name: Checkout
|
|
108
|
+
uses: actions/checkout@v7
|
|
109
|
+
- name: Install uv
|
|
110
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
111
|
+
with:
|
|
112
|
+
enable-cache: true
|
|
113
|
+
cache-suffix: floor
|
|
114
|
+
- name: Build wheel
|
|
115
|
+
run: uv build --wheel --out-dir dist
|
|
116
|
+
- name: Install the wheel on the floor interpreter
|
|
117
|
+
run: |
|
|
118
|
+
uv venv --python 3.13 --seed .venv-floor
|
|
119
|
+
.venv-floor/bin/pip install dist/ms_qg-*.whl pytest pytest-mock pytest-bdd xlsxwriter
|
|
120
|
+
- name: Import, CLI and bundled-config checks
|
|
121
|
+
run: |
|
|
122
|
+
.venv-floor/bin/python -c "import qg"
|
|
123
|
+
.venv-floor/bin/qg --help > /dev/null
|
|
124
|
+
.venv-floor/bin/python -c "from qg.config_models.loader import _DEFAULT_CONFIG_DIR as d; assert d.is_dir(), d"
|
|
125
|
+
- name: Run the non-browser suite against the installed wheel
|
|
126
|
+
run: >-
|
|
127
|
+
.venv-floor/bin/python -m pytest
|
|
128
|
+
--ignore=tests/gui
|
|
129
|
+
--ignore=tests/gui_local
|
|
130
|
+
-m "not bfabric and not bfabric_test_instance and not local_gui"
|
|
131
|
+
|
|
132
|
+
docs:
|
|
133
|
+
runs-on: ubuntu-latest
|
|
134
|
+
steps:
|
|
135
|
+
- name: Checkout
|
|
136
|
+
uses: actions/checkout@v7
|
|
137
|
+
- name: Install uv
|
|
138
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
139
|
+
with:
|
|
140
|
+
enable-cache: true
|
|
141
|
+
cache-suffix: docs
|
|
142
|
+
- name: Install Python
|
|
143
|
+
run: uv python install
|
|
144
|
+
- name: Sync docs environment
|
|
145
|
+
run: uv sync --frozen --only-group docs
|
|
146
|
+
- name: Build documentation (strict)
|
|
147
|
+
run: uv run --no-sync mkdocs build --strict
|
|
148
|
+
|
|
149
|
+
# Keep the checked-in Python sources aligned with the repository's Pyright
|
|
150
|
+
# configuration. This mirrors the local pre-commit hook.
|
|
151
|
+
pyright:
|
|
152
|
+
runs-on: ubuntu-latest
|
|
153
|
+
steps:
|
|
154
|
+
- name: Checkout
|
|
155
|
+
uses: actions/checkout@v7
|
|
156
|
+
- name: Install uv
|
|
157
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
158
|
+
with:
|
|
159
|
+
enable-cache: true
|
|
160
|
+
- name: Install Python
|
|
161
|
+
run: uv python install
|
|
162
|
+
- name: Sync (dev + portal)
|
|
163
|
+
run: uv sync --frozen
|
|
164
|
+
- name: Run Pyright
|
|
165
|
+
run: uv run --no-sync python -m pyright
|
|
166
|
+
|
|
167
|
+
# Core-install gate: prove `qg` installs, imports, and its non-B-Fabric tests
|
|
168
|
+
# pass WITHOUT the portal extra (no bfabric/fastapi/starlette/python-gitlab).
|
|
169
|
+
# Syncs only the `dev` group, omitting the `portal` default-group; asserts
|
|
170
|
+
# bfabric is absent, then runs the B-Fabric-free suite. Guards the publication
|
|
171
|
+
# path (a plain `pip install qg` stays B-Fabric-free).
|
|
172
|
+
core-no-bfabric:
|
|
173
|
+
runs-on: ubuntu-latest
|
|
174
|
+
steps:
|
|
175
|
+
- name: Checkout
|
|
176
|
+
uses: actions/checkout@v7
|
|
177
|
+
- name: Install uv
|
|
178
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
179
|
+
with:
|
|
180
|
+
enable-cache: true
|
|
181
|
+
# Own cache namespace: this job installs a portal-less subset, so it
|
|
182
|
+
# must not share the uv.lock-keyed cache with the full-install jobs
|
|
183
|
+
# (otherwise whichever saves first freezes the wrong dependency set).
|
|
184
|
+
cache-suffix: core
|
|
185
|
+
- name: Install Python
|
|
186
|
+
run: uv python install
|
|
187
|
+
- name: Sync core only (no portal group)
|
|
188
|
+
run: uv sync --frozen --no-group portal
|
|
189
|
+
- name: Run core profile checks
|
|
190
|
+
run: uv run --no-sync --no-group portal python scripts/core_profile_check.py
|
|
191
|
+
|
|
192
|
+
# Browser suite: tests/gui (portal app) + tests/gui_local (standalone app),
|
|
193
|
+
# driven in headless Chromium via pytest-playwright. Runs on Microsoft's
|
|
194
|
+
# official Playwright image, which ships the browsers AND their ~67 apt system
|
|
195
|
+
# libs pre-baked — so this job needs no `playwright install` and no apt step.
|
|
196
|
+
# The image tag MUST track the `playwright` pin in uv.lock (currently 1.62.0);
|
|
197
|
+
# a mismatch surfaces as a clear "browser not found / version" error.
|
|
198
|
+
# Do NOT set PLAYWRIGHT_BROWSERS_PATH here, or the baked browsers hide.
|
|
199
|
+
gui:
|
|
200
|
+
runs-on: ubuntu-latest
|
|
201
|
+
container: mcr.microsoft.com/playwright:v1.62.0-noble
|
|
202
|
+
steps:
|
|
203
|
+
- name: Checkout
|
|
204
|
+
uses: actions/checkout@v7
|
|
205
|
+
# setup-uv supports container jobs; if it ever fails to fetch/cache uv
|
|
206
|
+
# inside this image, the known-good fallback (used by the old GitLab job)
|
|
207
|
+
# is the static installer:
|
|
208
|
+
# run: curl -LsSf https://astral.sh/uv/install.sh | sh && echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
209
|
+
- name: Install uv
|
|
210
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
211
|
+
with:
|
|
212
|
+
enable-cache: true
|
|
213
|
+
- name: Install Python
|
|
214
|
+
run: uv python install
|
|
215
|
+
- name: Sync (dev + portal)
|
|
216
|
+
run: uv sync --frozen
|
|
217
|
+
- name: Run GUI tests
|
|
218
|
+
run: uv run --no-sync pytest tests/gui tests/gui_local
|
|
219
|
+
|
|
220
|
+
# Static lint of the marimo apps. --strict makes warnings (e.g. the
|
|
221
|
+
# branch-expression rule, where a bare mo.md(...) inside if/elif/else is
|
|
222
|
+
# computed then discarded so the output never renders) fail the pipeline.
|
|
223
|
+
marimo-check:
|
|
224
|
+
runs-on: ubuntu-latest
|
|
225
|
+
steps:
|
|
226
|
+
- name: Checkout
|
|
227
|
+
uses: actions/checkout@v7
|
|
228
|
+
- name: Install uv
|
|
229
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
230
|
+
with:
|
|
231
|
+
enable-cache: true
|
|
232
|
+
- name: Install Python
|
|
233
|
+
run: uv python install
|
|
234
|
+
- name: Sync (dev + portal)
|
|
235
|
+
run: uv sync --frozen
|
|
236
|
+
- name: marimo check (strict)
|
|
237
|
+
run: uv run --no-sync marimo check --strict src/qg/apps/queue_app.py src/qg/apps/queue_app_local.py src/qg/apps/config_editor.py
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: pages
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@v7
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
|
|
29
|
+
- name: Install Python
|
|
30
|
+
run: uv python install
|
|
31
|
+
|
|
32
|
+
# Docs-only: mkdocs-material lives in the `docs` group and the site needs
|
|
33
|
+
# no plugins that import the package, so we install just that group.
|
|
34
|
+
- name: Install docs dependencies
|
|
35
|
+
run: uv sync --frozen --only-group docs
|
|
36
|
+
|
|
37
|
+
- name: Configure Pages
|
|
38
|
+
uses: actions/configure-pages@v6
|
|
39
|
+
|
|
40
|
+
- name: Build docs
|
|
41
|
+
run: uv run --no-sync mkdocs build --strict
|
|
42
|
+
|
|
43
|
+
- name: Upload Pages artifact
|
|
44
|
+
uses: actions/upload-pages-artifact@v5
|
|
45
|
+
with:
|
|
46
|
+
path: public
|
|
47
|
+
|
|
48
|
+
deploy:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
needs: build
|
|
51
|
+
environment:
|
|
52
|
+
name: github-pages
|
|
53
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
54
|
+
steps:
|
|
55
|
+
- name: Deploy to GitHub Pages
|
|
56
|
+
id: deployment
|
|
57
|
+
uses: actions/deploy-pages@v5
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# CD for the GitHub remote: build the container image and push it to the GitHub
|
|
4
|
+
# Container Registry (ghcr.io/<owner>/<repo>). GitLab separately retains the
|
|
5
|
+
# production arm64 OCI-to-NFS path. Tagging a release (vX.Y.Z) publishes this
|
|
6
|
+
# multi-platform image; workflow_dispatch supports an explicit GitHub build.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
packages: write
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: publish-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: false
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
IMAGE: ghcr.io/${{ github.repository }}
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
# Release gate: run the full CI suite against the tagged commit first. Mirrors
|
|
25
|
+
# GitLab's stages: [test, build] ordering — a red test stage blocks the build.
|
|
26
|
+
test:
|
|
27
|
+
uses: ./.github/workflows/ci.yml
|
|
28
|
+
with:
|
|
29
|
+
# Unique group so this gate run isn't cancelled by a concurrent push to main.
|
|
30
|
+
concurrency-group: release-gate-${{ github.run_id }}
|
|
31
|
+
|
|
32
|
+
build-push:
|
|
33
|
+
needs: test
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout
|
|
37
|
+
uses: actions/checkout@v7
|
|
38
|
+
|
|
39
|
+
# arm64 emulation so a single amd64 runner can build the arm64 leg.
|
|
40
|
+
- name: Set up QEMU
|
|
41
|
+
uses: docker/setup-qemu-action@v3
|
|
42
|
+
|
|
43
|
+
- name: Set up Buildx
|
|
44
|
+
uses: docker/setup-buildx-action@v3
|
|
45
|
+
|
|
46
|
+
- name: Log in to ghcr.io
|
|
47
|
+
uses: docker/login-action@v3
|
|
48
|
+
with:
|
|
49
|
+
registry: ghcr.io
|
|
50
|
+
username: ${{ github.actor }}
|
|
51
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
52
|
+
|
|
53
|
+
- name: Derive image tags and labels
|
|
54
|
+
id: meta
|
|
55
|
+
uses: docker/metadata-action@v5
|
|
56
|
+
with:
|
|
57
|
+
images: ${{ env.IMAGE }}
|
|
58
|
+
# tag push -> the version tag (vX.Y.Z); manual dispatch -> the branch
|
|
59
|
+
# name (e.g. main), so manual builds get a readable tag, not sha-only.
|
|
60
|
+
tags: |
|
|
61
|
+
type=ref,event=tag
|
|
62
|
+
type=ref,event=branch
|
|
63
|
+
type=sha
|
|
64
|
+
|
|
65
|
+
# Builds the existing Dockerfile (uv base, portal extra by default, uvicorn
|
|
66
|
+
# entrypoint on :8000) for both platforms. The arm64 image is what the
|
|
67
|
+
# deploy host runs; amd64 lets the image run on amd64 dev machines too.
|
|
68
|
+
- name: Build and push
|
|
69
|
+
uses: docker/build-push-action@v6
|
|
70
|
+
with:
|
|
71
|
+
context: .
|
|
72
|
+
platforms: linux/amd64,linux/arm64
|
|
73
|
+
push: true
|
|
74
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
75
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
76
|
+
cache-from: type=gha
|
|
77
|
+
cache-to: type=gha,mode=max
|
|
78
|
+
|
|
79
|
+
# Publishes the sdist and wheel to PyPI on a version tag, gated by the same CI
|
|
80
|
+
# suite. Uses PyPI Trusted Publishing (OIDC), so no API token is stored here;
|
|
81
|
+
# the PyPI project must list this repository and the `pypi` environment as a
|
|
82
|
+
# trusted publisher. Skipped on workflow_dispatch, where the version is usually
|
|
83
|
+
# already published and PyPI rejects a re-upload.
|
|
84
|
+
publish-pypi:
|
|
85
|
+
needs: test
|
|
86
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
environment: pypi
|
|
89
|
+
permissions:
|
|
90
|
+
id-token: write
|
|
91
|
+
steps:
|
|
92
|
+
- name: Checkout
|
|
93
|
+
uses: actions/checkout@v7
|
|
94
|
+
- name: Install uv
|
|
95
|
+
uses: astral-sh/setup-uv@v8.2.0
|
|
96
|
+
with:
|
|
97
|
+
enable-cache: true
|
|
98
|
+
- name: Build sdist and wheel
|
|
99
|
+
run: uv build --out-dir dist
|
|
100
|
+
- name: Publish to PyPI
|
|
101
|
+
run: uv publish --trusted-publishing always
|