microlens-submit 0.12.1__tar.gz → 0.16.0__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.
Files changed (54) hide show
  1. microlens_submit-0.16.0/MANIFEST.in +2 -0
  2. {microlens_submit-0.12.1/microlens_submit.egg-info → microlens_submit-0.16.0}/PKG-INFO +44 -27
  3. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/README.md +21 -15
  4. microlens_submit-0.16.0/microlens_submit/__init__.py +13 -0
  5. microlens_submit-0.16.0/microlens_submit/cli/__init__.py +5 -0
  6. microlens_submit-0.16.0/microlens_submit/cli/__main__.py +6 -0
  7. microlens_submit-0.16.0/microlens_submit/cli/commands/__init__.py +1 -0
  8. microlens_submit-0.16.0/microlens_submit/cli/commands/dossier.py +139 -0
  9. microlens_submit-0.16.0/microlens_submit/cli/commands/export.py +177 -0
  10. microlens_submit-0.16.0/microlens_submit/cli/commands/init.py +172 -0
  11. microlens_submit-0.16.0/microlens_submit/cli/commands/solutions.py +722 -0
  12. microlens_submit-0.16.0/microlens_submit/cli/commands/validation.py +241 -0
  13. microlens_submit-0.16.0/microlens_submit/cli/main.py +120 -0
  14. microlens_submit-0.16.0/microlens_submit/dossier/__init__.py +51 -0
  15. microlens_submit-0.16.0/microlens_submit/dossier/dashboard.py +499 -0
  16. microlens_submit-0.16.0/microlens_submit/dossier/event_page.py +369 -0
  17. microlens_submit-0.16.0/microlens_submit/dossier/full_report.py +330 -0
  18. microlens_submit-0.16.0/microlens_submit/dossier/solution_page.py +533 -0
  19. microlens_submit-0.16.0/microlens_submit/dossier/utils.py +111 -0
  20. microlens_submit-0.16.0/microlens_submit/error_messages.py +283 -0
  21. microlens_submit-0.16.0/microlens_submit/models/__init__.py +28 -0
  22. microlens_submit-0.16.0/microlens_submit/models/event.py +406 -0
  23. microlens_submit-0.16.0/microlens_submit/models/solution.py +569 -0
  24. microlens_submit-0.16.0/microlens_submit/models/submission.py +569 -0
  25. microlens_submit-0.16.0/microlens_submit/tier_validation.py +208 -0
  26. microlens_submit-0.16.0/microlens_submit/utils.py +373 -0
  27. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/microlens_submit/validate_parameters.py +478 -180
  28. {microlens_submit-0.12.1 → microlens_submit-0.16.0/microlens_submit.egg-info}/PKG-INFO +44 -27
  29. microlens_submit-0.16.0/microlens_submit.egg-info/SOURCES.txt +42 -0
  30. microlens_submit-0.16.0/microlens_submit.egg-info/requires.txt +20 -0
  31. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/pyproject.toml +33 -12
  32. microlens_submit-0.16.0/setup.py +65 -0
  33. microlens_submit-0.16.0/tests/test_api.py +1048 -0
  34. microlens_submit-0.16.0/tests/test_cli.py +1730 -0
  35. microlens_submit-0.16.0/tests/test_dossier_generation.py +443 -0
  36. microlens_submit-0.16.0/tests/test_dossier_pages.py +49 -0
  37. microlens_submit-0.16.0/tests/test_tier_validation.py +518 -0
  38. microlens_submit-0.12.1/MANIFEST.in +0 -2
  39. microlens_submit-0.12.1/microlens_submit/__init__.py +0 -163
  40. microlens_submit-0.12.1/microlens_submit/api.py +0 -1274
  41. microlens_submit-0.12.1/microlens_submit/cli.py +0 -1803
  42. microlens_submit-0.12.1/microlens_submit/dossier.py +0 -1443
  43. microlens_submit-0.12.1/microlens_submit.egg-info/SOURCES.txt +0 -20
  44. microlens_submit-0.12.1/microlens_submit.egg-info/requires.txt +0 -16
  45. microlens_submit-0.12.1/tests/test_api.py +0 -496
  46. microlens_submit-0.12.1/tests/test_cli.py +0 -1171
  47. microlens_submit-0.12.1/tests/test_dossier_generation.py +0 -212
  48. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/LICENSE +0 -0
  49. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/microlens_submit/assets/github-desktop_logo.png +0 -0
  50. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/microlens_submit/assets/rges-pit_logo.png +0 -0
  51. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/microlens_submit.egg-info/dependency_links.txt +0 -0
  52. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/microlens_submit.egg-info/entry_points.txt +0 -0
  53. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/microlens_submit.egg-info/top_level.txt +0 -0
  54. {microlens_submit-0.12.1 → microlens_submit-0.16.0}/setup.cfg +0 -0
@@ -0,0 +1,2 @@
1
+ include microlens_submit/assets/rges-pit_logo.png
2
+ include microlens_submit/assets/github-desktop_logo.png
@@ -1,24 +1,35 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: microlens-submit
3
- Version: 0.12.1
4
- Summary: A stateful submission toolkit for the Microlensing Data Challenge.
5
- Author-email: Amber Malpas <malpas.1@osu.edu>
3
+ Version: 0.16.0
4
+ Summary: A tool for managing and submitting microlensing solutions
5
+ Home-page: https://github.com/AmberLee2427/microlens-submit
6
+ Author: Amber Malpas
7
+ Author-email: Amber Malpas <malpas.1@osu.edu>, Roman Science Platform Team <roman-science-platform@stsci.edu>
6
8
  License: MIT
7
9
  Project-URL: Homepage, https://github.com/AmberLee2427/microlens-submit
8
10
  Project-URL: Repository, https://github.com/AmberLee2427/microlens-submit
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Development Status :: 3 - Alpha
11
+ Keywords: astronomy,microlensing,data-challenge,submission,roman
12
+ Classifier: Development Status :: 4 - Beta
12
13
  Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
13
22
  Classifier: Topic :: Scientific/Engineering :: Astronomy
23
+ Classifier: Topic :: Scientific/Engineering :: Physics
14
24
  Requires-Python: >=3.8
15
25
  Description-Content-Type: text/markdown
16
26
  License-File: LICENSE
27
+ Requires-Dist: pydantic>=2.0.0
17
28
  Requires-Dist: typer[all]>=0.9.0
18
- Requires-Dist: pydantic>=2.0
19
- Requires-Dist: rich>=13.0
20
- Requires-Dist: PyYAML>=6.0
21
- Requires-Dist: markdown>=3.0
29
+ Requires-Dist: rich>=13.0.0
30
+ Requires-Dist: pyyaml>=6.0
31
+ Requires-Dist: markdown>=3.4.0
32
+ Requires-Dist: importlib_resources>=1.0.0; python_version < "3.9"
22
33
  Provides-Extra: dev
23
34
  Requires-Dist: pytest; extra == "dev"
24
35
  Requires-Dist: pytest-cov; extra == "dev"
@@ -29,7 +40,7 @@ Requires-Dist: black; extra == "dev"
29
40
  Requires-Dist: isort; extra == "dev"
30
41
  Requires-Dist: sphinx; extra == "dev"
31
42
  Requires-Dist: sphinx_rtd_theme; extra == "dev"
32
- Dynamic: license-file
43
+ Requires-Dist: importlib_resources; extra == "dev"
33
44
 
34
45
  <p align="center">
35
46
  <a href="https://github.com/AmberLee2427/microlens-submit">
@@ -41,7 +52,9 @@ Dynamic: license-file
41
52
 
42
53
  *A stateful submission toolkit for the RGES-PIT Microlensing Data Challenge.*
43
54
 
44
- [![PyPI - v0.12.1](https://img.shields.io/pypi/v/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
55
+ [![PyPI - v0.16.0](https://img.shields.io/pypi/v/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
56
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
57
+ [![PyPI - License](https://img.shields.io/pypi/l/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
45
58
  [![CI](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml/badge.svg)](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)
46
59
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
47
60
 
@@ -49,7 +62,7 @@ Dynamic: license-file
49
62
 
50
63
  `microlens-submit` provides a robust, version-controlled workflow for managing, validating, and packaging your challenge submission over a long period. It supports both a programmatic Python API and a full-featured Command Line Interface (CLI) for language-agnostic use.
51
64
 
52
- Full documentation is hosted on [Read the Docs](https://microlens-submit.readthedocs.io/en/latest/). A comprehensive tutorial notebook is available at `docs/Submission_Tool_Tutorial.ipynb`. Challenge participants who prefer not to use this tool can consult [SUBMISSION_MANUAL.md](SUBMISSION_MANUAL.md) for the manual submission format.
65
+ Full documentation is hosted on [Read the Docs](https://microlens-submit.readthedocs.io/en/latest/). A comprehensive tutorial notebook is available at `docs/Submission_Tool_Tutorial.ipynb`. Challenge participants who prefer not to use this tool can consult the [Submission Manual](https://microlens-submit.readthedocs.io/en/latest/submission_manual.html) for the manual submission format.
53
66
 
54
67
  ## Key Features
55
68
 
@@ -64,16 +77,11 @@ Full documentation is hosted on [Read the Docs](https://microlens-submit.readthe
64
77
  * **Environment Capture:** Automatically records your Python dependencies for each specific model fit, ensuring reproducibility.
65
78
  * **Optional Posterior Storage:** Record the path to posterior samples for any solution.
66
79
  * **Simple Export:** Packages all your active solutions into a clean, standardized `.zip` archive for final submission.
80
+ * **Bulk Import:** Import multiple solutions at once from a CSV file using the `import-solutions` CLI command. Supports column mapping, alias handling, duplicate handling, notes, dry-run, and validation options.
67
81
 
68
82
  ## Installation
69
83
 
70
- This package is pre-release. It is currently available on TestPyPI:
71
-
72
- ```bash
73
- pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple microlens-submit==0.12.0-dev
74
- ```
75
-
76
- The package will be available on PyPI:
84
+ The package is available on PyPI:
77
85
 
78
86
  ```bash
79
87
  pip install microlens-submit
@@ -98,10 +106,15 @@ You can pass ``--no-color`` to any command if your terminal does not support ANS
98
106
  This will create a new solution and print its unique `solution_id`.
99
107
  You can run the same command with `--dry-run` first to verify the
100
108
  parsed input without saving anything.
101
- 3. Deactivate a solution that didn't work out: `microlens-submit deactivate <solution_id>`
102
- 4. List all solutions for an event: `microlens-submit list-solutions ogle-2025-blg-0042`
103
- 5. Validate solutions and check for issues: `microlens-submit validate-solution <solution_id>`
104
- 6. Export your final submission: `microlens-submit export final_submission.zip`
109
+ 3. **Bulk import multiple solutions from a CSV file:**
110
+ ```bash
111
+ microlens-submit import-solutions tests/data/test_import.csv --dry-run
112
+ ```
113
+ See the file `tests/data/test_import.csv` for a comprehensive example covering all features and edge cases. You can use this file as a template for your own imports.
114
+ 4. Deactivate a solution that didn't work out: `microlens-submit deactivate <solution_id>`
115
+ 5. List all solutions for an event: `microlens-submit list-solutions ogle-2025-blg-0042`
116
+ 6. Validate solutions and check for issues: `microlens-submit validate-solution <solution_id>`
117
+ 7. Export your final submission: `microlens-submit export final_submission.zip`
105
118
 
106
119
  **Note:** When you add a solution, it's automatically validated and any warnings are displayed. Use `--dry-run` to check validation without saving.
107
120
 
@@ -137,7 +150,8 @@ sub.export("final_submission.zip")
137
150
 
138
151
  The full development plan can be found in agents.md. Contributions are welcome!
139
152
 
140
- To build and test this project, the development environment needs the following Python libraries. You can provide these to Codex or set up a `requirements-dev.txt` file.
153
+ To build and test this project, install the development dependencies using either `pip install -e .[dev]` or `pip install -r requirements-dev.txt`. These packages are required to run the test suite and are listed in `requirements-dev.txt`.
154
+ After installing the dependencies, run `pre-commit install` to set up the Git hooks for automatic formatting and linting. The development environment needs the following Python libraries.
141
155
 
142
156
  ### Core Dependencies:
143
157
  * **`typer[all]`**: For building the powerful command-line interface. The `[all]` extra ensures shell completion support is included.
@@ -151,9 +165,12 @@ To build and test this project, the development environment needs the following
151
165
  * **`build`**: For building the package from the `pyproject.toml` file.
152
166
  * **`twine`**: For uploading the final package to PyPI.
153
167
 
168
+ ### Test Data
169
+
170
+ A comprehensive test CSV file is provided at `tests/data/test_import.csv`. This file is used in the test suite and can be copied or adapted for your own bulk imports or for development/testing purposes.
171
+
154
172
  ## Citation
155
173
 
156
174
  If you use **microlens-submit** in your research, please cite the project using
157
175
  the metadata provided in the `CITATION.cff` file. Most reference managers can
158
176
  import this file directly.
159
-
@@ -8,7 +8,9 @@
8
8
 
9
9
  *A stateful submission toolkit for the RGES-PIT Microlensing Data Challenge.*
10
10
 
11
- [![PyPI - v0.12.1](https://img.shields.io/pypi/v/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
11
+ [![PyPI - v0.16.0](https://img.shields.io/pypi/v/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
12
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
13
+ [![PyPI - License](https://img.shields.io/pypi/l/microlens-submit.svg)](https://pypi.org/project/microlens-submit/)
12
14
  [![CI](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml/badge.svg)](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)
13
15
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
16
 
@@ -16,7 +18,7 @@
16
18
 
17
19
  `microlens-submit` provides a robust, version-controlled workflow for managing, validating, and packaging your challenge submission over a long period. It supports both a programmatic Python API and a full-featured Command Line Interface (CLI) for language-agnostic use.
18
20
 
19
- Full documentation is hosted on [Read the Docs](https://microlens-submit.readthedocs.io/en/latest/). A comprehensive tutorial notebook is available at `docs/Submission_Tool_Tutorial.ipynb`. Challenge participants who prefer not to use this tool can consult [SUBMISSION_MANUAL.md](SUBMISSION_MANUAL.md) for the manual submission format.
21
+ Full documentation is hosted on [Read the Docs](https://microlens-submit.readthedocs.io/en/latest/). A comprehensive tutorial notebook is available at `docs/Submission_Tool_Tutorial.ipynb`. Challenge participants who prefer not to use this tool can consult the [Submission Manual](https://microlens-submit.readthedocs.io/en/latest/submission_manual.html) for the manual submission format.
20
22
 
21
23
  ## Key Features
22
24
 
@@ -31,16 +33,11 @@ Full documentation is hosted on [Read the Docs](https://microlens-submit.readthe
31
33
  * **Environment Capture:** Automatically records your Python dependencies for each specific model fit, ensuring reproducibility.
32
34
  * **Optional Posterior Storage:** Record the path to posterior samples for any solution.
33
35
  * **Simple Export:** Packages all your active solutions into a clean, standardized `.zip` archive for final submission.
36
+ * **Bulk Import:** Import multiple solutions at once from a CSV file using the `import-solutions` CLI command. Supports column mapping, alias handling, duplicate handling, notes, dry-run, and validation options.
34
37
 
35
38
  ## Installation
36
39
 
37
- This package is pre-release. It is currently available on TestPyPI:
38
-
39
- ```bash
40
- pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple microlens-submit==0.12.0-dev
41
- ```
42
-
43
- The package will be available on PyPI:
40
+ The package is available on PyPI:
44
41
 
45
42
  ```bash
46
43
  pip install microlens-submit
@@ -65,10 +62,15 @@ You can pass ``--no-color`` to any command if your terminal does not support ANS
65
62
  This will create a new solution and print its unique `solution_id`.
66
63
  You can run the same command with `--dry-run` first to verify the
67
64
  parsed input without saving anything.
68
- 3. Deactivate a solution that didn't work out: `microlens-submit deactivate <solution_id>`
69
- 4. List all solutions for an event: `microlens-submit list-solutions ogle-2025-blg-0042`
70
- 5. Validate solutions and check for issues: `microlens-submit validate-solution <solution_id>`
71
- 6. Export your final submission: `microlens-submit export final_submission.zip`
65
+ 3. **Bulk import multiple solutions from a CSV file:**
66
+ ```bash
67
+ microlens-submit import-solutions tests/data/test_import.csv --dry-run
68
+ ```
69
+ See the file `tests/data/test_import.csv` for a comprehensive example covering all features and edge cases. You can use this file as a template for your own imports.
70
+ 4. Deactivate a solution that didn't work out: `microlens-submit deactivate <solution_id>`
71
+ 5. List all solutions for an event: `microlens-submit list-solutions ogle-2025-blg-0042`
72
+ 6. Validate solutions and check for issues: `microlens-submit validate-solution <solution_id>`
73
+ 7. Export your final submission: `microlens-submit export final_submission.zip`
72
74
 
73
75
  **Note:** When you add a solution, it's automatically validated and any warnings are displayed. Use `--dry-run` to check validation without saving.
74
76
 
@@ -104,7 +106,8 @@ sub.export("final_submission.zip")
104
106
 
105
107
  The full development plan can be found in agents.md. Contributions are welcome!
106
108
 
107
- To build and test this project, the development environment needs the following Python libraries. You can provide these to Codex or set up a `requirements-dev.txt` file.
109
+ To build and test this project, install the development dependencies using either `pip install -e .[dev]` or `pip install -r requirements-dev.txt`. These packages are required to run the test suite and are listed in `requirements-dev.txt`.
110
+ After installing the dependencies, run `pre-commit install` to set up the Git hooks for automatic formatting and linting. The development environment needs the following Python libraries.
108
111
 
109
112
  ### Core Dependencies:
110
113
  * **`typer[all]`**: For building the powerful command-line interface. The `[all]` extra ensures shell completion support is included.
@@ -118,9 +121,12 @@ To build and test this project, the development environment needs the following
118
121
  * **`build`**: For building the package from the `pyproject.toml` file.
119
122
  * **`twine`**: For uploading the final package to PyPI.
120
123
 
124
+ ### Test Data
125
+
126
+ A comprehensive test CSV file is provided at `tests/data/test_import.csv`. This file is used in the test suite and can be copied or adapted for your own bulk imports or for development/testing purposes.
127
+
121
128
  ## Citation
122
129
 
123
130
  If you use **microlens-submit** in your research, please cite the project using
124
131
  the metadata provided in the `CITATION.cff` file. Most reference managers can
125
132
  import this file directly.
126
-
@@ -0,0 +1,13 @@
1
+ """Stateful tools for Microlensing Data Challenge submissions.
2
+
3
+ ``microlens-submit`` manages events and solutions on disk so you can build,
4
+ validate, and export a challenge submission using either the Python API or
5
+ the command line interface.
6
+ """
7
+
8
+ __version__ = "0.16.0"
9
+
10
+ from .models import Event, Solution, Submission
11
+ from .utils import load
12
+
13
+ __all__ = ["Event", "Solution", "Submission", "load"]
@@ -0,0 +1,5 @@
1
+ """CLI package for microlens-submit."""
2
+
3
+ from .main import app
4
+
5
+ __all__ = ["app"]
@@ -0,0 +1,6 @@
1
+ """Entry point for running the CLI as a module."""
2
+
3
+ from .main import app
4
+
5
+ if __name__ == "__main__":
6
+ app()
@@ -0,0 +1 @@
1
+ """CLI commands package for microlens-submit."""
@@ -0,0 +1,139 @@
1
+ """Dossier generation commands for microlens-submit CLI."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ import typer
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+
10
+ from microlens_submit.dossier import generate_dashboard_html, generate_event_page, generate_solution_page
11
+ from microlens_submit.dossier.full_report import generate_full_dossier_report_html
12
+ from microlens_submit.utils import load
13
+
14
+ console = Console()
15
+
16
+
17
+ def generate_dossier(
18
+ project_path: Path = typer.Argument(Path("."), help="Project directory"),
19
+ event_id: Optional[str] = typer.Option(
20
+ None,
21
+ "--event-id",
22
+ help="Generate dossier for a specific event only (omit for full dossier)",
23
+ ),
24
+ solution_id: Optional[str] = typer.Option(
25
+ None,
26
+ "--solution-id",
27
+ help="Generate dossier for a specific solution only (omit for full dossier)",
28
+ ),
29
+ open: bool = typer.Option(
30
+ False,
31
+ "--open",
32
+ help="Open the generated dossier in your web browser after generation.",
33
+ ),
34
+ ) -> None:
35
+ """Generate an HTML dossier for the submission.
36
+
37
+ Use --open to automatically open the main dossier page in your browser after generation.
38
+ """
39
+ sub = load(str(project_path))
40
+ output_dir = Path(project_path) / "dossier"
41
+
42
+ if solution_id:
43
+ # Find the solution across all events (same pattern as other CLI commands)
44
+ solution = None
45
+ containing_event_id = None
46
+ for eid, event in sub.events.items():
47
+ if solution_id in event.solutions:
48
+ solution = event.solutions[solution_id]
49
+ containing_event_id = eid
50
+ break
51
+
52
+ if solution is None:
53
+ console.print(f"Solution {solution_id} not found", style="bold red")
54
+ raise typer.Exit(1)
55
+
56
+ # Create output directory and assets subdirectory
57
+ output_dir.mkdir(parents=True, exist_ok=True)
58
+ (output_dir / "assets").mkdir(exist_ok=True)
59
+
60
+ # Generate only the specific solution page
61
+ event = sub.events[containing_event_id]
62
+ console.print(
63
+ Panel(
64
+ f"Generating dossier for solution {solution_id} in event {containing_event_id}...",
65
+ style="cyan",
66
+ )
67
+ )
68
+ generate_solution_page(solution, event, sub, output_dir)
69
+ if open:
70
+ import webbrowser
71
+
72
+ solution_path = output_dir / f"{solution_id}.html"
73
+ if solution_path.exists():
74
+ webbrowser.open(solution_path.resolve().as_uri())
75
+
76
+ elif event_id:
77
+ # Generate only the specific event page
78
+ if event_id not in sub.events:
79
+ console.print(f"Event {event_id} not found", style="bold red")
80
+ raise typer.Exit(1)
81
+
82
+ # Create output directory and assets subdirectory
83
+ output_dir.mkdir(parents=True, exist_ok=True)
84
+ (output_dir / "assets").mkdir(exist_ok=True)
85
+
86
+ event = sub.events[event_id]
87
+ console.print(Panel(f"Generating dossier for event {event_id}...", style="cyan"))
88
+ generate_event_page(event, sub, output_dir)
89
+ if open:
90
+ import webbrowser
91
+
92
+ event_path = output_dir / f"{event_id}.html"
93
+ if event_path.exists():
94
+ webbrowser.open(event_path.resolve().as_uri())
95
+
96
+ else:
97
+ # Generate full dossier (all events and solutions)
98
+ console.print(
99
+ Panel(
100
+ "Generating comprehensive dossier for all events and solutions...",
101
+ style="cyan",
102
+ )
103
+ )
104
+ generate_dashboard_html(sub, output_dir)
105
+
106
+ # Generate comprehensive printable dossier
107
+ console.print(Panel("Generating comprehensive printable dossier...", style="cyan"))
108
+ generate_full_dossier_report_html(sub, output_dir)
109
+
110
+ # Replace placeholder in index.html with the real link
111
+ dashboard_path = output_dir / "index.html"
112
+ if dashboard_path.exists():
113
+ with dashboard_path.open("r", encoding="utf-8") as f:
114
+ dashboard_html = f.read()
115
+ dashboard_html = dashboard_html.replace(
116
+ "<!--FULL_DOSSIER_LINK_PLACEHOLDER-->",
117
+ '<div class="text-center">'
118
+ '<a href="./full_dossier_report.html" '
119
+ 'class="inline-block bg-rtd-accent text-white py-3 px-6 '
120
+ "rounded-lg shadow-md hover:bg-rtd-secondary "
121
+ 'transition-colors duration-200 text-lg font-semibold mt-8">'
122
+ "View Full Comprehensive Dossier (Printable)</a></div>",
123
+ )
124
+ with dashboard_path.open("w", encoding="utf-8") as f:
125
+ f.write(dashboard_html)
126
+ console.print(Panel("Comprehensive dossier generated!", style="bold green"))
127
+
128
+ # Open the main dashboard if requested
129
+ if open:
130
+ import webbrowser
131
+
132
+ webbrowser.open(dashboard_path.resolve().as_uri())
133
+
134
+ console.print(
135
+ Panel(
136
+ f"Dossier generated successfully at {output_dir / 'index.html'}",
137
+ style="bold green",
138
+ )
139
+ )
@@ -0,0 +1,177 @@
1
+ """Export commands for microlens-submit CLI."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
6
+ import typer
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+
10
+ from microlens_submit.utils import load
11
+
12
+ console = Console()
13
+
14
+
15
+ def export(
16
+ output_path: Path,
17
+ project_path: Path = typer.Argument(Path("."), help="Project directory"),
18
+ ) -> None:
19
+ """Generate a zip archive containing all active solutions.
20
+
21
+ This command creates a submission-ready archive. Unlike save operations,
22
+ export requires all validation checks to pass (complete team info,
23
+ hardware info, valid parameters, etc.) since this is for actual submission.
24
+
25
+ Args:
26
+ output_path: Path for the output zip file
27
+ project_path: Directory containing the submission project
28
+
29
+ Example:
30
+ # Export for submission (requires complete submission)
31
+ microlens-submit export submission.zip ./my_project
32
+
33
+ Note:
34
+ Export is strict and requires complete submissions. Use save operations
35
+ for saving incomplete work during development.
36
+ """
37
+ sub = load(str(project_path))
38
+ sub.export(str(output_path))
39
+ console.print(Panel(f"Exported submission to {output_path}", style="bold green"))
40
+
41
+
42
+ def remove_event(
43
+ event_id: str,
44
+ force: bool = typer.Option(False, "--force", help="Force removal even if event has saved solutions"),
45
+ project_path: Path = typer.Argument(Path("."), help="Project directory"),
46
+ ) -> None:
47
+ """Remove an entire event and all its solutions from the submission."""
48
+ submission = load(str(project_path))
49
+
50
+ if event_id not in submission.events:
51
+ typer.echo(f"❌ Event '{event_id}' not found in submission")
52
+ raise typer.Exit(1)
53
+
54
+ event = submission.events[event_id]
55
+ solution_count = len(event.solutions)
56
+
57
+ if not force:
58
+ typer.echo(f"⚠️ This will permanently remove event '{event_id}' " f"and all {solution_count} solutions.")
59
+ typer.echo(" This action cannot be undone.")
60
+ confirm = typer.confirm("Are you sure you want to continue?")
61
+ if not confirm:
62
+ typer.echo("❌ Operation cancelled")
63
+ raise typer.Exit(0)
64
+
65
+ try:
66
+ removed = submission.remove_event(event_id, force=force)
67
+ if removed:
68
+ typer.echo(f"✅ Removed event '{event_id}' and all {solution_count} solutions")
69
+ submission.save()
70
+ else:
71
+ typer.echo(f"❌ Failed to remove event '{event_id}'")
72
+ raise typer.Exit(1)
73
+ except ValueError as e:
74
+ typer.echo(f"❌ Cannot remove event: {e}")
75
+ raise typer.Exit(1)
76
+
77
+
78
+ def set_repo_url(
79
+ repo_url: str = typer.Argument(..., help="GitHub repository URL (e.g. https://github.com/owner/repo)"),
80
+ project_path: Path = typer.Argument(Path("."), help="Project directory"),
81
+ ) -> None:
82
+ """Set or update the GitHub repository URL in the submission metadata."""
83
+ sub = load(str(project_path))
84
+ sub.repo_url = repo_url
85
+ sub.save()
86
+ console.print(
87
+ Panel(
88
+ f"Set repo_url to {repo_url} in {project_path}/submission.json",
89
+ style="bold green",
90
+ )
91
+ )
92
+
93
+
94
+ def set_hardware_info(
95
+ cpu: Optional[str] = typer.Option(None, "--cpu", help="CPU model/description"),
96
+ cpu_details: Optional[str] = typer.Option(None, "--cpu-details", help="Detailed CPU information"),
97
+ memory_gb: Optional[float] = typer.Option(None, "--memory-gb", help="Memory in GB"),
98
+ ram_gb: Optional[float] = typer.Option(None, "--ram-gb", help="RAM in GB (alternative to --memory-gb)"),
99
+ platform: Optional[str] = typer.Option(
100
+ None,
101
+ "--platform",
102
+ help="Platform description (e.g., 'Local Analysis', 'Roman Nexus')",
103
+ ),
104
+ nexus_image: Optional[str] = typer.Option(None, "--nexus-image", help="Roman Nexus image identifier"),
105
+ clear: bool = typer.Option(False, "--clear", help="Clear all existing hardware info"),
106
+ dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be changed without saving"),
107
+ project_path: Path = typer.Argument(Path("."), help="Project directory"),
108
+ ) -> None:
109
+ """Set or update hardware information in the submission metadata."""
110
+ sub = load(str(project_path))
111
+
112
+ # Initialize hardware_info if it doesn't exist
113
+ if sub.hardware_info is None:
114
+ sub.hardware_info = {}
115
+
116
+ changes = []
117
+
118
+ # Clear existing info if requested
119
+ if clear:
120
+ if sub.hardware_info:
121
+ changes.append("Clear all existing hardware info")
122
+ sub.hardware_info = {}
123
+
124
+ # Set new values
125
+ if cpu_details is not None:
126
+ if sub.hardware_info.get("cpu_details") != cpu_details:
127
+ changes.append(f"Set cpu_details: {cpu_details}")
128
+ sub.hardware_info["cpu_details"] = cpu_details
129
+ elif cpu is not None:
130
+ if sub.hardware_info.get("cpu") != cpu:
131
+ changes.append(f"Set cpu: {cpu}")
132
+ sub.hardware_info["cpu"] = cpu
133
+
134
+ if memory_gb is not None:
135
+ if sub.hardware_info.get("memory_gb") != memory_gb:
136
+ changes.append(f"Set memory_gb: {memory_gb}")
137
+ sub.hardware_info["memory_gb"] = memory_gb
138
+ elif ram_gb is not None:
139
+ if sub.hardware_info.get("ram_gb") != ram_gb:
140
+ changes.append(f"Set ram_gb: {ram_gb}")
141
+ sub.hardware_info["ram_gb"] = ram_gb
142
+
143
+ if platform is not None:
144
+ if sub.hardware_info.get("platform") != platform:
145
+ changes.append(f"Set platform: {platform}")
146
+ sub.hardware_info["platform"] = platform
147
+
148
+ if nexus_image is not None:
149
+ if sub.hardware_info.get("nexus_image") != nexus_image:
150
+ changes.append(f"Set nexus_image: {nexus_image}")
151
+ sub.hardware_info["nexus_image"] = nexus_image
152
+
153
+ # Show dry run results
154
+ if dry_run:
155
+ if changes:
156
+ console.print(Panel("Hardware info changes (dry run):", style="cyan"))
157
+ for change in changes:
158
+ console.print(f" • {change}")
159
+ console.print(f"\nNew hardware_info: {sub.hardware_info}")
160
+ else:
161
+ console.print(Panel("No changes would be made", style="yellow"))
162
+ return
163
+
164
+ # Apply changes
165
+ if changes:
166
+ sub.save()
167
+ console.print(
168
+ Panel(
169
+ f"Updated hardware info in {project_path}/submission.json",
170
+ style="bold green",
171
+ )
172
+ )
173
+ for change in changes:
174
+ console.print(f" • {change}")
175
+ console.print(f"\nCurrent hardware_info: {sub.hardware_info}")
176
+ else:
177
+ console.print(Panel("No changes made", style="yellow"))