microlens-submit 0.16.0__tar.gz → 0.16.2__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.
- {microlens_submit-0.16.0/microlens_submit.egg-info → microlens_submit-0.16.2}/PKG-INFO +53 -9
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/README.md +46 -7
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/__init__.py +1 -1
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/export.py +9 -6
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/init.py +13 -8
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/solutions.py +26 -21
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/validation.py +11 -7
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/dossier/dashboard.py +6 -2
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/dossier/event_page.py +2 -1
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/dossier/solution_page.py +2 -1
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/error_messages.py +6 -4
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/models/event.py +10 -7
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/models/submission.py +23 -22
- microlens_submit-0.16.2/microlens_submit/text_symbols.py +75 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2/microlens_submit.egg-info}/PKG-INFO +53 -9
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit.egg-info/SOURCES.txt +1 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit.egg-info/requires.txt +1 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/pyproject.toml +4 -3
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/setup.py +2 -2
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/tests/test_cli.py +20 -15
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/LICENSE +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/MANIFEST.in +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/assets/github-desktop_logo.png +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/assets/rges-pit_logo.png +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/__init__.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/__main__.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/__init__.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/dossier.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/main.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/dossier/__init__.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/dossier/full_report.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/dossier/utils.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/models/__init__.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/models/solution.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/tier_validation.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/utils.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/validate_parameters.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit.egg-info/dependency_links.txt +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit.egg-info/entry_points.txt +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit.egg-info/top_level.txt +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/setup.cfg +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/tests/test_api.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/tests/test_dossier_generation.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/tests/test_dossier_pages.py +0 -0
- {microlens_submit-0.16.0 → microlens_submit-0.16.2}/tests/test_tier_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: microlens-submit
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.2
|
|
4
4
|
Summary: A tool for managing and submitting microlensing solutions
|
|
5
5
|
Home-page: https://github.com/AmberLee2427/microlens-submit
|
|
6
6
|
Author: Amber Malpas
|
|
@@ -41,6 +41,11 @@ Requires-Dist: isort; extra == "dev"
|
|
|
41
41
|
Requires-Dist: sphinx; extra == "dev"
|
|
42
42
|
Requires-Dist: sphinx_rtd_theme; extra == "dev"
|
|
43
43
|
Requires-Dist: importlib_resources; extra == "dev"
|
|
44
|
+
Requires-Dist: python-dotenv; extra == "dev"
|
|
45
|
+
Dynamic: author
|
|
46
|
+
Dynamic: home-page
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
Dynamic: requires-python
|
|
44
49
|
|
|
45
50
|
<p align="center">
|
|
46
51
|
<a href="https://github.com/AmberLee2427/microlens-submit">
|
|
@@ -52,11 +57,7 @@ Requires-Dist: importlib_resources; extra == "dev"
|
|
|
52
57
|
|
|
53
58
|
*A stateful submission toolkit for the RGES-PIT Microlensing Data Challenge.*
|
|
54
59
|
|
|
55
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
56
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
57
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
58
|
-
[](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)
|
|
59
|
-
[](https://opensource.org/licenses/MIT)
|
|
60
|
+
[](https://pypi.org/project/microlens-submit/)[](https://microlens-submit.readthedocs.io/en/latest/?badge=latest)[](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)[](https://pypi.org/project/microlens-submit/)[](https://opensource.org/licenses/MIT)
|
|
60
61
|
|
|
61
62
|
<br>
|
|
62
63
|
|
|
@@ -93,8 +94,21 @@ The CLI is the recommended way to interact with your submission project.
|
|
|
93
94
|
|
|
94
95
|
You can pass ``--no-color`` to any command if your terminal does not support ANSI colors.
|
|
95
96
|
|
|
96
|
-
1. Initialize your project:
|
|
97
|
+
1. Initialize your project:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
microlens-submit init --team-name "Planet Pounders" --tier "advanced"
|
|
101
|
+
# if a project directory was provided to `init`, you should now `cd` into that project
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
To pass validation, you need to have provided a `repo_url` and `hardware_info` to the project and have a git project initialized in your sumission-project directory.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
microlens-submit set-repo-url <url> ./
|
|
108
|
+
microlens-submit set-hardware-info --cpu-details "intel i7 xxx" --ram-gb 32 ./
|
|
109
|
+
```
|
|
97
110
|
2. Add a new solution to an event:
|
|
111
|
+
|
|
98
112
|
```bash
|
|
99
113
|
microlens-submit add-solution ogle-2025-blg-0042 1S2L \
|
|
100
114
|
--param t0=555.5 \
|
|
@@ -102,18 +116,28 @@ You can pass ``--no-color`` to any command if your terminal does not support ANS
|
|
|
102
116
|
--param tE=25.0 \
|
|
103
117
|
--notes "This is a great fit!"
|
|
104
118
|
```
|
|
119
|
+
|
|
105
120
|
Model types must be one of `1S1L`, `1S2L`, `2S1L`, `2S2L`, `1S3L`, `2S3L`, or `other`.
|
|
106
121
|
This will create a new solution and print its unique `solution_id`.
|
|
122
|
+
|
|
107
123
|
You can run the same command with `--dry-run` first to verify the
|
|
108
124
|
parsed input without saving anything.
|
|
125
|
+
|
|
109
126
|
3. **Bulk import multiple solutions from a CSV file:**
|
|
127
|
+
|
|
110
128
|
```bash
|
|
111
129
|
microlens-submit import-solutions tests/data/test_import.csv --dry-run
|
|
112
130
|
```
|
|
113
|
-
|
|
131
|
+
|
|
132
|
+
See the file `tests/data/test_import.csv` for a comprehensive example covering all features and edge cases.
|
|
133
|
+
You can use this file as a template for your own imports.
|
|
134
|
+
|
|
114
135
|
4. Deactivate a solution that didn't work out: `microlens-submit deactivate <solution_id>`
|
|
136
|
+
|
|
115
137
|
5. List all solutions for an event: `microlens-submit list-solutions ogle-2025-blg-0042`
|
|
138
|
+
|
|
116
139
|
6. Validate solutions and check for issues: `microlens-submit validate-solution <solution_id>`
|
|
140
|
+
|
|
117
141
|
7. Export your final submission: `microlens-submit export final_submission.zip`
|
|
118
142
|
|
|
119
143
|
**Note:** When you add a solution, it's automatically validated and any warnings are displayed. Use `--dry-run` to check validation without saving.
|
|
@@ -174,3 +198,23 @@ A comprehensive test CSV file is provided at `tests/data/test_import.csv`. This
|
|
|
174
198
|
If you use **microlens-submit** in your research, please cite the project using
|
|
175
199
|
the metadata provided in the `CITATION.cff` file. Most reference managers can
|
|
176
200
|
import this file directly.
|
|
201
|
+
|
|
202
|
+
Bibtex:
|
|
203
|
+
```
|
|
204
|
+
@software{malpas_2025_17460500,
|
|
205
|
+
author = {Malpas, Amber},
|
|
206
|
+
title = {microlens-submit},
|
|
207
|
+
month = oct,
|
|
208
|
+
year = 2025,
|
|
209
|
+
publisher = {Zenodo},
|
|
210
|
+
version = {v0.16.1},
|
|
211
|
+
doi = {10.5281/zenodo.17460500},
|
|
212
|
+
url = {https://doi.org/10.5281/zenodo.17460500},
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Cite without version:
|
|
217
|
+
Malpas, A. (2025). microlens-submit. Zenodo. https://doi.org/10.5281/zenodo.17459752
|
|
218
|
+
|
|
219
|
+
Cite current version:
|
|
220
|
+
Malpas, A. (2025). microlens-submit (v0.16.1). Zenodo. https://doi.org/10.5281/zenodo.17459753
|
|
@@ -8,11 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
*A stateful submission toolkit for the RGES-PIT Microlensing Data Challenge.*
|
|
10
10
|
|
|
11
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
12
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
13
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
14
|
-
[](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)
|
|
15
|
-
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://pypi.org/project/microlens-submit/)[](https://microlens-submit.readthedocs.io/en/latest/?badge=latest)[](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)[](https://pypi.org/project/microlens-submit/)[](https://opensource.org/licenses/MIT)
|
|
16
12
|
|
|
17
13
|
<br>
|
|
18
14
|
|
|
@@ -49,8 +45,21 @@ The CLI is the recommended way to interact with your submission project.
|
|
|
49
45
|
|
|
50
46
|
You can pass ``--no-color`` to any command if your terminal does not support ANSI colors.
|
|
51
47
|
|
|
52
|
-
1. Initialize your project:
|
|
48
|
+
1. Initialize your project:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
microlens-submit init --team-name "Planet Pounders" --tier "advanced"
|
|
52
|
+
# if a project directory was provided to `init`, you should now `cd` into that project
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To pass validation, you need to have provided a `repo_url` and `hardware_info` to the project and have a git project initialized in your sumission-project directory.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
microlens-submit set-repo-url <url> ./
|
|
59
|
+
microlens-submit set-hardware-info --cpu-details "intel i7 xxx" --ram-gb 32 ./
|
|
60
|
+
```
|
|
53
61
|
2. Add a new solution to an event:
|
|
62
|
+
|
|
54
63
|
```bash
|
|
55
64
|
microlens-submit add-solution ogle-2025-blg-0042 1S2L \
|
|
56
65
|
--param t0=555.5 \
|
|
@@ -58,18 +67,28 @@ You can pass ``--no-color`` to any command if your terminal does not support ANS
|
|
|
58
67
|
--param tE=25.0 \
|
|
59
68
|
--notes "This is a great fit!"
|
|
60
69
|
```
|
|
70
|
+
|
|
61
71
|
Model types must be one of `1S1L`, `1S2L`, `2S1L`, `2S2L`, `1S3L`, `2S3L`, or `other`.
|
|
62
72
|
This will create a new solution and print its unique `solution_id`.
|
|
73
|
+
|
|
63
74
|
You can run the same command with `--dry-run` first to verify the
|
|
64
75
|
parsed input without saving anything.
|
|
76
|
+
|
|
65
77
|
3. **Bulk import multiple solutions from a CSV file:**
|
|
78
|
+
|
|
66
79
|
```bash
|
|
67
80
|
microlens-submit import-solutions tests/data/test_import.csv --dry-run
|
|
68
81
|
```
|
|
69
|
-
|
|
82
|
+
|
|
83
|
+
See the file `tests/data/test_import.csv` for a comprehensive example covering all features and edge cases.
|
|
84
|
+
You can use this file as a template for your own imports.
|
|
85
|
+
|
|
70
86
|
4. Deactivate a solution that didn't work out: `microlens-submit deactivate <solution_id>`
|
|
87
|
+
|
|
71
88
|
5. List all solutions for an event: `microlens-submit list-solutions ogle-2025-blg-0042`
|
|
89
|
+
|
|
72
90
|
6. Validate solutions and check for issues: `microlens-submit validate-solution <solution_id>`
|
|
91
|
+
|
|
73
92
|
7. Export your final submission: `microlens-submit export final_submission.zip`
|
|
74
93
|
|
|
75
94
|
**Note:** When you add a solution, it's automatically validated and any warnings are displayed. Use `--dry-run` to check validation without saving.
|
|
@@ -130,3 +149,23 @@ A comprehensive test CSV file is provided at `tests/data/test_import.csv`. This
|
|
|
130
149
|
If you use **microlens-submit** in your research, please cite the project using
|
|
131
150
|
the metadata provided in the `CITATION.cff` file. Most reference managers can
|
|
132
151
|
import this file directly.
|
|
152
|
+
|
|
153
|
+
Bibtex:
|
|
154
|
+
```
|
|
155
|
+
@software{malpas_2025_17460500,
|
|
156
|
+
author = {Malpas, Amber},
|
|
157
|
+
title = {microlens-submit},
|
|
158
|
+
month = oct,
|
|
159
|
+
year = 2025,
|
|
160
|
+
publisher = {Zenodo},
|
|
161
|
+
version = {v0.16.1},
|
|
162
|
+
doi = {10.5281/zenodo.17460500},
|
|
163
|
+
url = {https://doi.org/10.5281/zenodo.17460500},
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Cite without version:
|
|
168
|
+
Malpas, A. (2025). microlens-submit. Zenodo. https://doi.org/10.5281/zenodo.17459752
|
|
169
|
+
|
|
170
|
+
Cite current version:
|
|
171
|
+
Malpas, A. (2025). microlens-submit (v0.16.1). Zenodo. https://doi.org/10.5281/zenodo.17459753
|
|
@@ -7,6 +7,7 @@ import typer
|
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from rich.panel import Panel
|
|
9
9
|
|
|
10
|
+
from microlens_submit.text_symbols import symbol
|
|
10
11
|
from microlens_submit.utils import load
|
|
11
12
|
|
|
12
13
|
console = Console()
|
|
@@ -48,30 +49,32 @@ def remove_event(
|
|
|
48
49
|
submission = load(str(project_path))
|
|
49
50
|
|
|
50
51
|
if event_id not in submission.events:
|
|
51
|
-
typer.echo(f"
|
|
52
|
+
typer.echo(f"{symbol('error')} Event '{event_id}' not found in submission")
|
|
52
53
|
raise typer.Exit(1)
|
|
53
54
|
|
|
54
55
|
event = submission.events[event_id]
|
|
55
56
|
solution_count = len(event.solutions)
|
|
56
57
|
|
|
57
58
|
if not force:
|
|
58
|
-
typer.echo(
|
|
59
|
+
typer.echo(
|
|
60
|
+
f"{symbol('warning')} This will permanently remove event '{event_id}' and all {solution_count} solutions."
|
|
61
|
+
)
|
|
59
62
|
typer.echo(" This action cannot be undone.")
|
|
60
63
|
confirm = typer.confirm("Are you sure you want to continue?")
|
|
61
64
|
if not confirm:
|
|
62
|
-
typer.echo("
|
|
65
|
+
typer.echo(f"{symbol('error')} Operation cancelled")
|
|
63
66
|
raise typer.Exit(0)
|
|
64
67
|
|
|
65
68
|
try:
|
|
66
69
|
removed = submission.remove_event(event_id, force=force)
|
|
67
70
|
if removed:
|
|
68
|
-
typer.echo(f"
|
|
71
|
+
typer.echo(f"{symbol('check')} Removed event '{event_id}' and all {solution_count} solutions")
|
|
69
72
|
submission.save()
|
|
70
73
|
else:
|
|
71
|
-
typer.echo(f"
|
|
74
|
+
typer.echo(f"{symbol('error')} Failed to remove event '{event_id}'")
|
|
72
75
|
raise typer.Exit(1)
|
|
73
76
|
except ValueError as e:
|
|
74
|
-
typer.echo(f"
|
|
77
|
+
typer.echo(f"{symbol('error')} Cannot remove event: {e}")
|
|
75
78
|
raise typer.Exit(1)
|
|
76
79
|
|
|
77
80
|
|
|
@@ -7,6 +7,7 @@ import typer
|
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from rich.panel import Panel
|
|
9
9
|
|
|
10
|
+
from microlens_submit.text_symbols import symbol
|
|
10
11
|
from microlens_submit.utils import load
|
|
11
12
|
|
|
12
13
|
console = Console()
|
|
@@ -101,19 +102,21 @@ def init(
|
|
|
101
102
|
# Run warnings-only validation
|
|
102
103
|
warnings = sub.run_validation_warnings()
|
|
103
104
|
if warnings:
|
|
104
|
-
console.print("[yellow]
|
|
105
|
+
console.print(f"[yellow]{symbol('warning')} Project initialized with warnings:[/yellow]")
|
|
105
106
|
for warning in warnings:
|
|
106
107
|
console.print(f" [yellow]• {warning}[/yellow]")
|
|
107
|
-
console.print("[yellow]
|
|
108
|
+
console.print(f"[yellow]{symbol('hint')} These warnings will become errors when saving or exporting.[/yellow]")
|
|
108
109
|
|
|
109
110
|
# Try to save, but don't fail if there are validation errors
|
|
110
111
|
try:
|
|
111
112
|
sub.save()
|
|
112
113
|
console.print(Panel(f"Initialized project at {project_path}", style="bold green"))
|
|
113
114
|
except ValueError as e:
|
|
114
|
-
console.print(
|
|
115
|
+
console.print(
|
|
116
|
+
f"[yellow]{symbol('warning')} Project initialized but could not save due to validation errors:[/yellow]"
|
|
117
|
+
)
|
|
115
118
|
console.print(f"[yellow]{str(e)}[/yellow]")
|
|
116
|
-
console.print("[yellow]
|
|
119
|
+
console.print(f"[yellow]{symbol('hint')} Fix validation errors before saving or exporting.[/yellow]")
|
|
117
120
|
console.print(Panel(f"Initialized project at {project_path} (unsaved)", style="bold yellow"))
|
|
118
121
|
|
|
119
122
|
|
|
@@ -156,17 +159,19 @@ def nexus_init(
|
|
|
156
159
|
# Run warnings-only validation after adding hardware info
|
|
157
160
|
warnings = sub.run_validation_warnings()
|
|
158
161
|
if warnings:
|
|
159
|
-
console.print("[yellow]
|
|
162
|
+
console.print(f"[yellow]{symbol('warning')} Project updated with warnings:[/yellow]")
|
|
160
163
|
for warning in warnings:
|
|
161
164
|
console.print(f" [yellow]• {warning}[/yellow]")
|
|
162
|
-
console.print("[yellow]
|
|
165
|
+
console.print(f"[yellow]{symbol('hint')} These warnings will become errors when saving or exporting.[/yellow]")
|
|
163
166
|
|
|
164
167
|
# Try to save, but don't fail if there are validation errors
|
|
165
168
|
try:
|
|
166
169
|
sub.save()
|
|
167
170
|
console.print("Nexus platform info captured.", style="bold green")
|
|
168
171
|
except ValueError as e:
|
|
169
|
-
console.print(
|
|
172
|
+
console.print(
|
|
173
|
+
f"[yellow]{symbol('warning')} Project updated but could not save due to validation errors:[/yellow]"
|
|
174
|
+
)
|
|
170
175
|
console.print(f"[yellow]{str(e)}[/yellow]")
|
|
171
|
-
console.print("[yellow]
|
|
176
|
+
console.print(f"[yellow]{symbol('hint')} Fix validation errors before saving or exporting.[/yellow]")
|
|
172
177
|
console.print("Nexus platform info captured (unsaved).", style="bold yellow")
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/solutions.py
RENAMED
|
@@ -10,6 +10,7 @@ from rich.console import Console
|
|
|
10
10
|
from rich.panel import Panel
|
|
11
11
|
|
|
12
12
|
from microlens_submit.error_messages import enhance_validation_messages, format_cli_error_with_suggestions
|
|
13
|
+
from microlens_submit.text_symbols import symbol
|
|
13
14
|
from microlens_submit.utils import import_solutions_from_csv, load
|
|
14
15
|
|
|
15
16
|
console = Console()
|
|
@@ -319,7 +320,7 @@ def add_solution(
|
|
|
319
320
|
console.print(f" • {msg}")
|
|
320
321
|
else:
|
|
321
322
|
console.print(
|
|
322
|
-
f"
|
|
323
|
+
f"{symbol('check')} Solution {sol.solution_id} created successfully!",
|
|
323
324
|
style="green",
|
|
324
325
|
)
|
|
325
326
|
|
|
@@ -362,7 +363,7 @@ def activate(
|
|
|
362
363
|
|
|
363
364
|
solution.activate()
|
|
364
365
|
submission.save()
|
|
365
|
-
console.print(f"[green]
|
|
366
|
+
console.print(f"[green]{symbol('check')} Activated solution {solution_id[:8]}... in event {event_id}[/green]")
|
|
366
367
|
|
|
367
368
|
|
|
368
369
|
def remove_solution(
|
|
@@ -390,14 +391,17 @@ def remove_solution(
|
|
|
390
391
|
removed = submission.events[event_id].remove_solution(solution_id, force=force)
|
|
391
392
|
if removed:
|
|
392
393
|
submission.save()
|
|
393
|
-
console.print(
|
|
394
|
+
console.print(
|
|
395
|
+
f"[green]{symbol('check')} Solution {solution_id[:8]}... removed from event {event_id}[/green]"
|
|
396
|
+
)
|
|
394
397
|
else:
|
|
395
398
|
console.print(f"[red]Error: Failed to remove solution {solution_id}[/red]")
|
|
396
399
|
raise typer.Exit(1)
|
|
397
400
|
except ValueError as e:
|
|
398
401
|
console.print(f"[red]Error: {e}[/red]")
|
|
399
402
|
console.print(
|
|
400
|
-
"[yellow]
|
|
403
|
+
f"[yellow]{symbol('hint')} Use --force to override safety checks, "
|
|
404
|
+
"or use deactivate to keep the solution[/yellow]"
|
|
401
405
|
)
|
|
402
406
|
raise typer.Exit(1)
|
|
403
407
|
|
|
@@ -482,10 +486,11 @@ def edit_solution(
|
|
|
482
486
|
if target_solution is None:
|
|
483
487
|
console.print(f"Solution {solution_id} not found", style="bold red")
|
|
484
488
|
raise typer.Exit(code=1)
|
|
489
|
+
arrow = symbol("arrow")
|
|
485
490
|
changes = []
|
|
486
491
|
if alias is not None:
|
|
487
492
|
if target_solution.alias != alias:
|
|
488
|
-
changes.append(f"Update alias: {target_solution.alias}
|
|
493
|
+
changes.append(f"Update alias: {target_solution.alias} {arrow} {alias}")
|
|
489
494
|
target_solution.alias = alias
|
|
490
495
|
if clear_relative_probability:
|
|
491
496
|
if target_solution.relative_probability is not None:
|
|
@@ -494,7 +499,7 @@ def edit_solution(
|
|
|
494
499
|
elif relative_probability is not None:
|
|
495
500
|
if target_solution.relative_probability != relative_probability:
|
|
496
501
|
changes.append(
|
|
497
|
-
f"Update relative_probability:
|
|
502
|
+
f"Update relative_probability: {target_solution.relative_probability} {arrow} {relative_probability}"
|
|
498
503
|
)
|
|
499
504
|
target_solution.relative_probability = relative_probability
|
|
500
505
|
if clear_log_likelihood:
|
|
@@ -503,7 +508,7 @@ def edit_solution(
|
|
|
503
508
|
target_solution.log_likelihood = None
|
|
504
509
|
elif log_likelihood is not None:
|
|
505
510
|
if target_solution.log_likelihood != log_likelihood:
|
|
506
|
-
changes.append(f"Update log_likelihood: {target_solution.log_likelihood}
|
|
511
|
+
changes.append(f"Update log_likelihood: {target_solution.log_likelihood} {arrow} {log_likelihood}")
|
|
507
512
|
target_solution.log_likelihood = log_likelihood
|
|
508
513
|
if clear_n_data_points:
|
|
509
514
|
if target_solution.n_data_points is not None:
|
|
@@ -511,7 +516,7 @@ def edit_solution(
|
|
|
511
516
|
target_solution.n_data_points = None
|
|
512
517
|
elif n_data_points is not None:
|
|
513
518
|
if target_solution.n_data_points != n_data_points:
|
|
514
|
-
changes.append(f"Update n_data_points: {target_solution.n_data_points}
|
|
519
|
+
changes.append(f"Update n_data_points: {target_solution.n_data_points} {arrow} {n_data_points}")
|
|
515
520
|
target_solution.n_data_points = n_data_points
|
|
516
521
|
# Notes file logic
|
|
517
522
|
canonical_notes_path = (
|
|
@@ -550,9 +555,9 @@ def edit_solution(
|
|
|
550
555
|
old_cpu = target_solution.compute_info.get("cpu_hours")
|
|
551
556
|
old_wall = target_solution.compute_info.get("wall_time_hours")
|
|
552
557
|
if cpu_hours is not None and old_cpu != cpu_hours:
|
|
553
|
-
changes.append(f"Update cpu_hours: {old_cpu}
|
|
558
|
+
changes.append(f"Update cpu_hours: {old_cpu} {arrow} {cpu_hours}")
|
|
554
559
|
if wall_time_hours is not None and old_wall != wall_time_hours:
|
|
555
|
-
changes.append(f"Update wall_time_hours: {old_wall}
|
|
560
|
+
changes.append(f"Update wall_time_hours: {old_wall} {arrow} {wall_time_hours}")
|
|
556
561
|
target_solution.set_compute_info(
|
|
557
562
|
cpu_hours=cpu_hours if cpu_hours is not None else old_cpu,
|
|
558
563
|
wall_time_hours=(wall_time_hours if wall_time_hours is not None else old_wall),
|
|
@@ -568,7 +573,7 @@ def edit_solution(
|
|
|
568
573
|
new_value = value
|
|
569
574
|
old_value = target_solution.parameters.get(key)
|
|
570
575
|
if old_value != new_value:
|
|
571
|
-
changes.append(f"Update parameter {key}: {old_value}
|
|
576
|
+
changes.append(f"Update parameter {key}: {old_value} {arrow} {new_value}")
|
|
572
577
|
target_solution.parameters[key] = new_value
|
|
573
578
|
if param_uncertainty:
|
|
574
579
|
if target_solution.parameter_uncertainties is None:
|
|
@@ -583,7 +588,7 @@ def edit_solution(
|
|
|
583
588
|
new_value = value
|
|
584
589
|
old_value = target_solution.parameter_uncertainties.get(key)
|
|
585
590
|
if old_value != new_value:
|
|
586
|
-
changes.append(f"Update uncertainty {key}: {old_value}
|
|
591
|
+
changes.append(f"Update uncertainty {key}: {old_value} {arrow} {new_value}")
|
|
587
592
|
target_solution.parameter_uncertainties[key] = new_value
|
|
588
593
|
if clear_higher_order_effects:
|
|
589
594
|
if target_solution.higher_order_effects:
|
|
@@ -592,7 +597,7 @@ def edit_solution(
|
|
|
592
597
|
elif higher_order_effect:
|
|
593
598
|
if target_solution.higher_order_effects != higher_order_effect:
|
|
594
599
|
changes.append(
|
|
595
|
-
f"Update higher_order_effects:
|
|
600
|
+
f"Update higher_order_effects: {target_solution.higher_order_effects} {arrow} {higher_order_effect}"
|
|
596
601
|
)
|
|
597
602
|
target_solution.higher_order_effects = higher_order_effect
|
|
598
603
|
if dry_run:
|
|
@@ -670,14 +675,14 @@ def import_solutions(
|
|
|
670
675
|
) -> None:
|
|
671
676
|
"""Import solutions from a CSV file into the current project."""
|
|
672
677
|
if on_duplicate not in ["error", "override", "ignore"]:
|
|
673
|
-
typer.echo(f"
|
|
678
|
+
typer.echo(f"{symbol('error')} Invalid --on-duplicate option: {on_duplicate}")
|
|
674
679
|
typer.echo(" Valid options: error, override, ignore")
|
|
675
680
|
raise typer.Exit(1)
|
|
676
681
|
|
|
677
682
|
try:
|
|
678
683
|
submission = load(str(project_path))
|
|
679
684
|
except Exception as e: # pragma: no cover - unexpected I/O errors
|
|
680
|
-
typer.echo(f"
|
|
685
|
+
typer.echo(f"{symbol('error')} Failed to load submission: {e}")
|
|
681
686
|
raise typer.Exit(1)
|
|
682
687
|
|
|
683
688
|
try:
|
|
@@ -692,17 +697,17 @@ def import_solutions(
|
|
|
692
697
|
project_path=project_path,
|
|
693
698
|
)
|
|
694
699
|
except Exception as e: # pragma: no cover - unexpected parse errors
|
|
695
|
-
typer.echo(f"
|
|
700
|
+
typer.echo(f"{symbol('error')} Failed to import solutions: {e}")
|
|
696
701
|
raise typer.Exit(1)
|
|
697
702
|
|
|
698
703
|
if not dry_run and stats["successful_imports"] > 0:
|
|
699
704
|
try:
|
|
700
705
|
submission.save()
|
|
701
706
|
except Exception as e: # pragma: no cover - disk failures
|
|
702
|
-
typer.echo(f"
|
|
707
|
+
typer.echo(f"{symbol('error')} Failed to save submission: {e}")
|
|
703
708
|
raise typer.Exit(1)
|
|
704
709
|
|
|
705
|
-
typer.echo("\n
|
|
710
|
+
typer.echo(f"\n{symbol('progress')} Import Summary:")
|
|
706
711
|
typer.echo(f" Total rows processed: {stats['total_rows']}")
|
|
707
712
|
typer.echo(f" Successful imports: {stats['successful_imports']}")
|
|
708
713
|
typer.echo(f" Skipped rows: {stats['skipped_rows']}")
|
|
@@ -710,13 +715,13 @@ def import_solutions(
|
|
|
710
715
|
typer.echo(f" Duplicates handled: {stats['duplicate_handled']}")
|
|
711
716
|
|
|
712
717
|
if stats["errors"]:
|
|
713
|
-
typer.echo("\n
|
|
718
|
+
typer.echo(f"\n{symbol('warning')} Errors encountered:")
|
|
714
719
|
for error in stats["errors"][:10]:
|
|
715
720
|
typer.echo(f" {error}")
|
|
716
721
|
if len(stats["errors"]) > 10:
|
|
717
722
|
typer.echo(f" ... and {len(stats['errors']) - 10} more errors")
|
|
718
723
|
|
|
719
724
|
if dry_run:
|
|
720
|
-
typer.echo("\n
|
|
725
|
+
typer.echo(f"\n{symbol('search')} Dry run completed - no changes made")
|
|
721
726
|
else:
|
|
722
|
-
typer.echo("\n
|
|
727
|
+
typer.echo(f"\n{symbol('check')} Import completed successfully")
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/validation.py
RENAMED
|
@@ -10,6 +10,7 @@ from rich.panel import Panel
|
|
|
10
10
|
from rich.table import Table
|
|
11
11
|
|
|
12
12
|
from microlens_submit.error_messages import enhance_validation_messages
|
|
13
|
+
from microlens_submit.text_symbols import symbol
|
|
13
14
|
from microlens_submit.utils import load
|
|
14
15
|
|
|
15
16
|
console = Console()
|
|
@@ -42,7 +43,7 @@ def validate_solution(
|
|
|
42
43
|
if not enhanced_messages:
|
|
43
44
|
console.print(
|
|
44
45
|
Panel(
|
|
45
|
-
f"
|
|
46
|
+
f"{symbol('check')} All validations passed for {solution_id} (event {target_event_id})",
|
|
46
47
|
style="bold green",
|
|
47
48
|
)
|
|
48
49
|
)
|
|
@@ -65,7 +66,7 @@ def validate_submission(
|
|
|
65
66
|
warnings = sub.run_validation_warnings()
|
|
66
67
|
|
|
67
68
|
if not warnings:
|
|
68
|
-
console.print(Panel("
|
|
69
|
+
console.print(Panel(f"{symbol('check')} All validations passed!", style="bold green"))
|
|
69
70
|
else:
|
|
70
71
|
console.print(Panel("Validation Warnings", style="yellow"))
|
|
71
72
|
for warning in warnings:
|
|
@@ -78,21 +79,24 @@ def validate_submission(
|
|
|
78
79
|
|
|
79
80
|
if has_repo_issue:
|
|
80
81
|
console.print(
|
|
81
|
-
"\n[blue]
|
|
82
|
+
f"\n[blue]{symbol('hint')} To fix repository URL issues:[/blue]\n"
|
|
82
83
|
" microlens-submit set-repo-url <url> <project_dir>",
|
|
83
84
|
style="blue",
|
|
84
85
|
)
|
|
85
86
|
|
|
86
87
|
if has_hardware_issue:
|
|
87
88
|
console.print(
|
|
88
|
-
"\n[blue]
|
|
89
|
+
f"\n[blue]{symbol('hint')} To fix hardware info issues:[/blue]\n"
|
|
89
90
|
" microlens-submit nexus-init --team-name <name> --tier <tier> <project_dir>\n"
|
|
90
91
|
" (or manually set hardware_info in submission.json)",
|
|
91
92
|
style="blue",
|
|
92
93
|
)
|
|
93
94
|
|
|
94
95
|
console.print(
|
|
95
|
-
"\n[yellow]
|
|
96
|
+
"\n[yellow]"
|
|
97
|
+
f"{symbol('warning')} Note: These warnings will become errors when saving or exporting."
|
|
98
|
+
"[/yellow]",
|
|
99
|
+
style="yellow",
|
|
96
100
|
)
|
|
97
101
|
|
|
98
102
|
|
|
@@ -121,10 +125,10 @@ def validate_event(
|
|
|
121
125
|
console.print(f" • {msg}")
|
|
122
126
|
all_messages.append(f"{solution.solution_id}: {msg}")
|
|
123
127
|
else:
|
|
124
|
-
console.print(f"
|
|
128
|
+
console.print(f"{symbol('check')} Solution {solution.solution_id}: All validations passed")
|
|
125
129
|
|
|
126
130
|
if not all_messages:
|
|
127
|
-
console.print(Panel("
|
|
131
|
+
console.print(Panel(f"{symbol('check')} All solutions passed validation!", style="bold green"))
|
|
128
132
|
else:
|
|
129
133
|
console.print(
|
|
130
134
|
f"\nFound {len(all_messages)} validation issue(s) across all solutions",
|
|
@@ -11,8 +11,12 @@ import webbrowser
|
|
|
11
11
|
from datetime import datetime
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
try: # Prefer stdlib importlib.resources when available (Python >= 3.9)
|
|
15
|
+
import importlib.resources as importlib_resources
|
|
16
|
+
except ImportError: # pragma: no cover - fallback for Python < 3.9
|
|
17
|
+
import importlib_resources
|
|
15
18
|
|
|
19
|
+
from .. import __version__
|
|
16
20
|
from ..models.submission import Submission
|
|
17
21
|
from .utils import extract_github_repo_name, format_hardware_info
|
|
18
22
|
|
|
@@ -485,7 +489,7 @@ style="width: {progress_percentage}%"></div>
|
|
|
485
489
|
|
|
486
490
|
<!-- Footer -->
|
|
487
491
|
<div class="text-sm text-gray-500 text-center pt-8 pb-6">
|
|
488
|
-
Generated by microlens-submit
|
|
492
|
+
Generated by microlens-submit v{__version__} on
|
|
489
493
|
{datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
|
|
490
494
|
</div>
|
|
491
495
|
|
|
@@ -9,6 +9,7 @@ evaluator-only visualizations.
|
|
|
9
9
|
from datetime import datetime
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
+
from .. import __version__
|
|
12
13
|
from ..models import Event, Submission
|
|
13
14
|
from .solution_page import generate_solution_page
|
|
14
15
|
|
|
@@ -357,7 +358,7 @@ Event {event.event_id}. Points colored by Relative Probability (Evaluator View).
|
|
|
357
358
|
|
|
358
359
|
<!-- Footer -->
|
|
359
360
|
<div class='text-sm text-gray-500 text-center pt-8 border-t border-gray-200 mt-10'>
|
|
360
|
-
Generated by microlens-submit
|
|
361
|
+
Generated by microlens-submit v{__version__} on {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
|
|
361
362
|
</div>
|
|
362
363
|
|
|
363
364
|
<!-- Regex Finish -->
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/dossier/solution_page.py
RENAMED
|
@@ -11,6 +11,7 @@ from pathlib import Path
|
|
|
11
11
|
|
|
12
12
|
import markdown
|
|
13
13
|
|
|
14
|
+
from .. import __version__
|
|
14
15
|
from ..models.event import Event
|
|
15
16
|
from ..models.solution import Solution
|
|
16
17
|
from ..models.submission import Submission
|
|
@@ -521,7 +522,7 @@ def _generate_solution_page_content(solution: Solution, event: Event, submission
|
|
|
521
522
|
|
|
522
523
|
<!-- Footer -->
|
|
523
524
|
<div class='text-sm text-gray-500 text-center pt-8 border-t border-gray-200 mt-10'>
|
|
524
|
-
Generated by microlens-submit
|
|
525
|
+
Generated by microlens-submit v{__version__} on {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}
|
|
525
526
|
</div>
|
|
526
527
|
|
|
527
528
|
<!-- Regex Finish -->
|
|
@@ -8,6 +8,8 @@ to help users understand and fix issues more easily.
|
|
|
8
8
|
import re
|
|
9
9
|
from typing import Dict, List, Optional
|
|
10
10
|
|
|
11
|
+
from microlens_submit.text_symbols import symbol
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
def get_model_type_suggestions(invalid_type: str) -> List[str]:
|
|
13
15
|
"""Get suggestions for model type corrections."""
|
|
@@ -156,9 +158,9 @@ def format_validation_message(message: str, suggestions: Optional[List[str]] = N
|
|
|
156
158
|
|
|
157
159
|
formatted = message
|
|
158
160
|
if len(suggestions) == 1:
|
|
159
|
-
formatted += f"\n
|
|
161
|
+
formatted += f"\n{symbol('hint')} Suggestion: {suggestions[0]}"
|
|
160
162
|
else:
|
|
161
|
-
formatted += "\n
|
|
163
|
+
formatted += f"\n{symbol('hint')} Suggestions:"
|
|
162
164
|
for suggestion in suggestions:
|
|
163
165
|
formatted += f"\n • {suggestion}"
|
|
164
166
|
|
|
@@ -259,7 +261,7 @@ def format_cli_error_with_suggestions(error_message: str, context: Dict) -> str:
|
|
|
259
261
|
|
|
260
262
|
# Add context-specific suggestions
|
|
261
263
|
if "model_type must be one of" in error_message:
|
|
262
|
-
enhanced += "\n\n
|
|
264
|
+
enhanced += f"\n\n{symbol('hint')} Quick Start Examples:"
|
|
263
265
|
enhanced += (
|
|
264
266
|
"\n microlens-submit add-solution EVENT123 1S1L " "--param t0=2459123.5 --param u0=0.1 --param tE=20.0"
|
|
265
267
|
)
|
|
@@ -275,7 +277,7 @@ def format_cli_error_with_suggestions(error_message: str, context: Dict) -> str:
|
|
|
275
277
|
enhanced += "\n Multiple parameters: --param t0=2459123.5 " "--param u0=0.1 --param tE=20.0"
|
|
276
278
|
|
|
277
279
|
elif "Cannot use --param with --params-file" in error_message:
|
|
278
|
-
enhanced += "\n\n
|
|
280
|
+
enhanced += f"\n\n{symbol('hint')} Parameter Options:"
|
|
279
281
|
enhanced += "\n Use --param for individual parameters on command line"
|
|
280
282
|
enhanced += "\n Use --params-file for parameters from a JSON/YAML file"
|
|
281
283
|
enhanced += "\n Choose one method, not both"
|
|
@@ -11,6 +11,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional
|
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, Field
|
|
13
13
|
|
|
14
|
+
from ..text_symbols import symbol
|
|
14
15
|
from .solution import Solution
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
@@ -122,11 +123,11 @@ class Event(BaseModel):
|
|
|
122
123
|
|
|
123
124
|
# Provide feedback about the created solution
|
|
124
125
|
alias_info = f" with alias '{alias}'" if alias else ""
|
|
125
|
-
print(f"
|
|
126
|
+
print(f"{symbol('check')} Created solution {solution_id}{alias_info}")
|
|
126
127
|
print(f" Model: {model_type}, Parameters: {len(parameters)}")
|
|
127
128
|
if alias:
|
|
128
|
-
print(f"
|
|
129
|
-
print("
|
|
129
|
+
print(f" {symbol('warning')} Note: Alias '{alias}' will be validated for uniqueness when saved")
|
|
130
|
+
print(f" {symbol('save')} Remember to call submission.save() to persist to disk")
|
|
130
131
|
|
|
131
132
|
return sol
|
|
132
133
|
|
|
@@ -322,14 +323,14 @@ class Event(BaseModel):
|
|
|
322
323
|
try:
|
|
323
324
|
if full_path.exists():
|
|
324
325
|
full_path.unlink()
|
|
325
|
-
print(f"
|
|
326
|
+
print(f"{symbol('trash')} Removed temporary notes file: {notes_path}")
|
|
326
327
|
except OSError:
|
|
327
|
-
print(f"
|
|
328
|
+
print(f"{symbol('warning')} Warning: Could not remove temporary file {notes_path}")
|
|
328
329
|
|
|
329
330
|
# Remove from solutions dict
|
|
330
331
|
del self.solutions[solution_id]
|
|
331
332
|
|
|
332
|
-
print(f"
|
|
333
|
+
print(f"{symbol('trash')} Removed solution {solution_id[:8]}... from event {self.event_id}")
|
|
333
334
|
return True
|
|
334
335
|
|
|
335
336
|
def remove_all_solutions(self, force: bool = False) -> int:
|
|
@@ -366,7 +367,9 @@ class Event(BaseModel):
|
|
|
366
367
|
removed_count += 1
|
|
367
368
|
except ValueError:
|
|
368
369
|
if not force:
|
|
369
|
-
print(
|
|
370
|
+
print(
|
|
371
|
+
f"{symbol('warning')} Skipped saved solution {solution_id[:8]}... (use force=True to remove)"
|
|
372
|
+
)
|
|
370
373
|
else:
|
|
371
374
|
# Force=True should override the saved check
|
|
372
375
|
if self.remove_solution(solution_id, force=True):
|
|
@@ -15,6 +15,7 @@ from typing import Dict, List, Optional
|
|
|
15
15
|
|
|
16
16
|
from pydantic import BaseModel, Field
|
|
17
17
|
|
|
18
|
+
from ..text_symbols import symbol
|
|
18
19
|
from .event import Event
|
|
19
20
|
from .solution import Solution
|
|
20
21
|
|
|
@@ -348,22 +349,22 @@ class Submission(BaseModel):
|
|
|
348
349
|
|
|
349
350
|
def print_solution_status(self) -> None:
|
|
350
351
|
status = self.get_solution_status()
|
|
351
|
-
print("
|
|
352
|
+
print(f"{symbol('progress')} Solution Status Summary:")
|
|
352
353
|
print(f" Total solutions: {status['total']}")
|
|
353
354
|
print(f" Saved to disk: {status['saved']}")
|
|
354
355
|
print(f" Unsaved (in memory): {status['unsaved']}")
|
|
355
356
|
if status["unsaved"] > 0:
|
|
356
|
-
print("
|
|
357
|
+
print(f" {symbol('save')} Call submission.save() to persist unsaved solutions")
|
|
357
358
|
if status["duplicate_aliases"]:
|
|
358
|
-
print("
|
|
359
|
+
print(f" {symbol('error')} Alias conflicts found:")
|
|
359
360
|
for error in status["duplicate_aliases"]:
|
|
360
361
|
print(f" {error}")
|
|
361
|
-
print("
|
|
362
|
+
print(f" {symbol('hint')} Resolve conflicts before saving")
|
|
362
363
|
for event_id, event_status in status["events"].items():
|
|
363
|
-
print(f"\n
|
|
364
|
+
print(f"\n{symbol('folder')} Event {event_id}:")
|
|
364
365
|
print(f" Solutions: {event_status['saved']} saved, {event_status['unsaved']} unsaved")
|
|
365
366
|
for sol_id, sol_status in event_status["solutions"].items():
|
|
366
|
-
status_icon = "
|
|
367
|
+
status_icon = symbol("check") if sol_status["saved"] else symbol("pending")
|
|
367
368
|
alias_info = f" (alias: {sol_status['alias']})" if sol_status["alias"] else ""
|
|
368
369
|
active_info = "" if sol_status["is_active"] else " [inactive]"
|
|
369
370
|
print(f" {status_icon} {sol_id} - {sol_status['model_type']}{alias_info}{active_info}")
|
|
@@ -372,25 +373,25 @@ class Submission(BaseModel):
|
|
|
372
373
|
# Run comprehensive validation first
|
|
373
374
|
validation_errors = self.run_validation()
|
|
374
375
|
if validation_errors:
|
|
375
|
-
print("
|
|
376
|
+
print(f"{symbol('warning')} Save completed with validation warnings:")
|
|
376
377
|
for error in validation_errors:
|
|
377
378
|
print(f" {error}")
|
|
378
|
-
print("
|
|
379
|
+
print(f"{symbol('hint')} Fix validation errors before exporting for submission")
|
|
379
380
|
|
|
380
381
|
if not force:
|
|
381
|
-
print("
|
|
382
|
+
print(f"{symbol('save')} Submission saved locally (incomplete - not ready for submission)")
|
|
382
383
|
else:
|
|
383
|
-
print("
|
|
384
|
+
print(f"{symbol('save')} Submission saved locally (forced save with validation errors)")
|
|
384
385
|
else:
|
|
385
|
-
print("
|
|
386
|
+
print(f"{symbol('check')} Submission saved successfully (ready for export)")
|
|
386
387
|
|
|
387
388
|
# Check for alias conflicts (existing behavior)
|
|
388
389
|
alias_errors = self._validate_alias_uniqueness()
|
|
389
390
|
if alias_errors:
|
|
390
|
-
print("
|
|
391
|
+
print(f"{symbol('error')} Save failed due to alias validation errors:")
|
|
391
392
|
for error in alias_errors:
|
|
392
393
|
print(f" {error}")
|
|
393
|
-
print("
|
|
394
|
+
print(f"{symbol('hint')} Solutions with duplicate aliases remain in memory but are not saved")
|
|
394
395
|
print(" Use different aliases or remove aliases to resolve conflicts")
|
|
395
396
|
raise ValueError("Alias validation failed:\n" + "\n".join(alias_errors))
|
|
396
397
|
|
|
@@ -420,9 +421,9 @@ class Submission(BaseModel):
|
|
|
420
421
|
for sol in event.solutions.values():
|
|
421
422
|
sol.saved = True
|
|
422
423
|
if unsaved_count > 0:
|
|
423
|
-
print(f"
|
|
424
|
+
print(f"{symbol('check')} Successfully saved {unsaved_count} new solution(s) to disk")
|
|
424
425
|
else:
|
|
425
|
-
print("
|
|
426
|
+
print(f"{symbol('check')} Successfully saved submission to disk")
|
|
426
427
|
saved_aliases = [
|
|
427
428
|
f"{event_id} {sol.alias}"
|
|
428
429
|
for event_id, event in self.events.items()
|
|
@@ -430,17 +431,17 @@ class Submission(BaseModel):
|
|
|
430
431
|
if sol.alias and sol.saved
|
|
431
432
|
]
|
|
432
433
|
if saved_aliases:
|
|
433
|
-
print(f"
|
|
434
|
+
print(f"{symbol('clipboard')} Saved aliases: {', '.join(saved_aliases)}")
|
|
434
435
|
|
|
435
436
|
def export(self, output_path: str) -> None:
|
|
436
437
|
# Run comprehensive validation first - export is strict
|
|
437
438
|
validation_errors = self.run_validation()
|
|
438
439
|
if validation_errors:
|
|
439
|
-
print("
|
|
440
|
+
print(f"{symbol('error')} Export failed due to validation errors:")
|
|
440
441
|
for error in validation_errors:
|
|
441
442
|
print(f" {error}")
|
|
442
|
-
print("
|
|
443
|
-
print("
|
|
443
|
+
print(f"{symbol('hint')} Fix validation errors before exporting for submission")
|
|
444
|
+
print(f"{symbol('hint')} Use submission.save() to save incomplete work locally")
|
|
444
445
|
raise ValueError("Validation failed:\n" + "\n".join(validation_errors))
|
|
445
446
|
|
|
446
447
|
project = Path(self.project_path)
|
|
@@ -559,11 +560,11 @@ class Submission(BaseModel):
|
|
|
559
560
|
try:
|
|
560
561
|
if full_path.exists():
|
|
561
562
|
full_path.unlink()
|
|
562
|
-
print(f"
|
|
563
|
+
print(f"{symbol('trash')} Removed temporary notes file: {notes_path}")
|
|
563
564
|
except OSError as e:
|
|
564
|
-
print(f"
|
|
565
|
+
print(f"{symbol('warning')} Warning: Could not remove temporary file {notes_path}: {e}")
|
|
565
566
|
del self.events[event_id]
|
|
566
|
-
print(f"
|
|
567
|
+
print(f"{symbol('trash')} Removed event '{event_id}' with {len(event.solutions)} solutions")
|
|
567
568
|
return True
|
|
568
569
|
|
|
569
570
|
# ... (all methods from Submission class, unchanged, including docstrings)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Console-friendly symbols with graceful fallbacks.
|
|
2
|
+
|
|
3
|
+
Some Windows terminals still use legacy code pages (e.g., CP1252) that cannot
|
|
4
|
+
encode emoji-style glyphs. Attempting to print those characters raises
|
|
5
|
+
``UnicodeEncodeError``. This module exposes a :func:`symbol` helper that
|
|
6
|
+
returns the Unicode glyph when the current output stream supports it and
|
|
7
|
+
otherwise falls back to a readable ASCII alternative.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Dict, Tuple
|
|
14
|
+
|
|
15
|
+
_SYMBOL_MAP: Dict[str, Tuple[str, str]] = {
|
|
16
|
+
"check": ("✅", "[OK]"),
|
|
17
|
+
"warning": ("⚠️", "[WARN]"),
|
|
18
|
+
"error": ("❌", "[ERROR]"),
|
|
19
|
+
"save": ("💾", "[SAVE]"),
|
|
20
|
+
"progress": ("📊", "[STATUS]"),
|
|
21
|
+
"folder": ("📁", "[EVENT]"),
|
|
22
|
+
"pending": ("⏳", "[PENDING]"),
|
|
23
|
+
"clipboard": ("📋", "[ALIASES]"),
|
|
24
|
+
"hint": ("💡", "[HINT]"),
|
|
25
|
+
"note": ("💡", "[NOTE]"),
|
|
26
|
+
"trash": ("🗑️", "[REMOVE]"),
|
|
27
|
+
"search": ("🔍", "[DRY RUN]"),
|
|
28
|
+
"arrow": ("→", "->"),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _can_encode(text: str) -> bool:
|
|
33
|
+
"""Return ``True`` if ``text`` can be encoded using the current stdout/stderr."""
|
|
34
|
+
|
|
35
|
+
def _stream_supports(stream) -> bool | None:
|
|
36
|
+
if stream is None:
|
|
37
|
+
return None
|
|
38
|
+
encoding = getattr(stream, "encoding", None)
|
|
39
|
+
if not encoding:
|
|
40
|
+
return None
|
|
41
|
+
try:
|
|
42
|
+
text.encode(encoding)
|
|
43
|
+
except UnicodeEncodeError:
|
|
44
|
+
return False
|
|
45
|
+
else:
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
# Prefer the actively used stdout/stderr streams.
|
|
49
|
+
for attr in ("stdout", "stderr"):
|
|
50
|
+
result = _stream_supports(getattr(sys, attr, None))
|
|
51
|
+
if result is not None:
|
|
52
|
+
return result
|
|
53
|
+
|
|
54
|
+
# Fall back to the original streams if the active ones are unavailable.
|
|
55
|
+
for attr in ("__stdout__", "__stderr__"):
|
|
56
|
+
result = _stream_supports(getattr(sys, attr, None))
|
|
57
|
+
if result:
|
|
58
|
+
return True
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def symbol(name: str) -> str:
|
|
63
|
+
"""Return a console-friendly symbol for ``name``.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
name: Symbol identifier such as ``"check"`` or ``"warning"``.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The preferred Unicode glyph when supported, otherwise a readable ASCII
|
|
70
|
+
fallback.
|
|
71
|
+
"""
|
|
72
|
+
unicode_char, fallback = _SYMBOL_MAP.get(name, ("", ""))
|
|
73
|
+
if unicode_char and _can_encode(unicode_char):
|
|
74
|
+
return unicode_char
|
|
75
|
+
return fallback or unicode_char
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: microlens-submit
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.2
|
|
4
4
|
Summary: A tool for managing and submitting microlensing solutions
|
|
5
5
|
Home-page: https://github.com/AmberLee2427/microlens-submit
|
|
6
6
|
Author: Amber Malpas
|
|
@@ -41,6 +41,11 @@ Requires-Dist: isort; extra == "dev"
|
|
|
41
41
|
Requires-Dist: sphinx; extra == "dev"
|
|
42
42
|
Requires-Dist: sphinx_rtd_theme; extra == "dev"
|
|
43
43
|
Requires-Dist: importlib_resources; extra == "dev"
|
|
44
|
+
Requires-Dist: python-dotenv; extra == "dev"
|
|
45
|
+
Dynamic: author
|
|
46
|
+
Dynamic: home-page
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
Dynamic: requires-python
|
|
44
49
|
|
|
45
50
|
<p align="center">
|
|
46
51
|
<a href="https://github.com/AmberLee2427/microlens-submit">
|
|
@@ -52,11 +57,7 @@ Requires-Dist: importlib_resources; extra == "dev"
|
|
|
52
57
|
|
|
53
58
|
*A stateful submission toolkit for the RGES-PIT Microlensing Data Challenge.*
|
|
54
59
|
|
|
55
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
56
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
57
|
-
[](https://pypi.org/project/microlens-submit/)
|
|
58
|
-
[](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)
|
|
59
|
-
[](https://opensource.org/licenses/MIT)
|
|
60
|
+
[](https://pypi.org/project/microlens-submit/)[](https://microlens-submit.readthedocs.io/en/latest/?badge=latest)[](https://github.com/AmberLee2427/microlens-submit/actions/workflows/ci.yml)[](https://pypi.org/project/microlens-submit/)[](https://opensource.org/licenses/MIT)
|
|
60
61
|
|
|
61
62
|
<br>
|
|
62
63
|
|
|
@@ -93,8 +94,21 @@ The CLI is the recommended way to interact with your submission project.
|
|
|
93
94
|
|
|
94
95
|
You can pass ``--no-color`` to any command if your terminal does not support ANSI colors.
|
|
95
96
|
|
|
96
|
-
1. Initialize your project:
|
|
97
|
+
1. Initialize your project:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
microlens-submit init --team-name "Planet Pounders" --tier "advanced"
|
|
101
|
+
# if a project directory was provided to `init`, you should now `cd` into that project
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
To pass validation, you need to have provided a `repo_url` and `hardware_info` to the project and have a git project initialized in your sumission-project directory.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
microlens-submit set-repo-url <url> ./
|
|
108
|
+
microlens-submit set-hardware-info --cpu-details "intel i7 xxx" --ram-gb 32 ./
|
|
109
|
+
```
|
|
97
110
|
2. Add a new solution to an event:
|
|
111
|
+
|
|
98
112
|
```bash
|
|
99
113
|
microlens-submit add-solution ogle-2025-blg-0042 1S2L \
|
|
100
114
|
--param t0=555.5 \
|
|
@@ -102,18 +116,28 @@ You can pass ``--no-color`` to any command if your terminal does not support ANS
|
|
|
102
116
|
--param tE=25.0 \
|
|
103
117
|
--notes "This is a great fit!"
|
|
104
118
|
```
|
|
119
|
+
|
|
105
120
|
Model types must be one of `1S1L`, `1S2L`, `2S1L`, `2S2L`, `1S3L`, `2S3L`, or `other`.
|
|
106
121
|
This will create a new solution and print its unique `solution_id`.
|
|
122
|
+
|
|
107
123
|
You can run the same command with `--dry-run` first to verify the
|
|
108
124
|
parsed input without saving anything.
|
|
125
|
+
|
|
109
126
|
3. **Bulk import multiple solutions from a CSV file:**
|
|
127
|
+
|
|
110
128
|
```bash
|
|
111
129
|
microlens-submit import-solutions tests/data/test_import.csv --dry-run
|
|
112
130
|
```
|
|
113
|
-
|
|
131
|
+
|
|
132
|
+
See the file `tests/data/test_import.csv` for a comprehensive example covering all features and edge cases.
|
|
133
|
+
You can use this file as a template for your own imports.
|
|
134
|
+
|
|
114
135
|
4. Deactivate a solution that didn't work out: `microlens-submit deactivate <solution_id>`
|
|
136
|
+
|
|
115
137
|
5. List all solutions for an event: `microlens-submit list-solutions ogle-2025-blg-0042`
|
|
138
|
+
|
|
116
139
|
6. Validate solutions and check for issues: `microlens-submit validate-solution <solution_id>`
|
|
140
|
+
|
|
117
141
|
7. Export your final submission: `microlens-submit export final_submission.zip`
|
|
118
142
|
|
|
119
143
|
**Note:** When you add a solution, it's automatically validated and any warnings are displayed. Use `--dry-run` to check validation without saving.
|
|
@@ -174,3 +198,23 @@ A comprehensive test CSV file is provided at `tests/data/test_import.csv`. This
|
|
|
174
198
|
If you use **microlens-submit** in your research, please cite the project using
|
|
175
199
|
the metadata provided in the `CITATION.cff` file. Most reference managers can
|
|
176
200
|
import this file directly.
|
|
201
|
+
|
|
202
|
+
Bibtex:
|
|
203
|
+
```
|
|
204
|
+
@software{malpas_2025_17460500,
|
|
205
|
+
author = {Malpas, Amber},
|
|
206
|
+
title = {microlens-submit},
|
|
207
|
+
month = oct,
|
|
208
|
+
year = 2025,
|
|
209
|
+
publisher = {Zenodo},
|
|
210
|
+
version = {v0.16.1},
|
|
211
|
+
doi = {10.5281/zenodo.17460500},
|
|
212
|
+
url = {https://doi.org/10.5281/zenodo.17460500},
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Cite without version:
|
|
217
|
+
Malpas, A. (2025). microlens-submit. Zenodo. https://doi.org/10.5281/zenodo.17459752
|
|
218
|
+
|
|
219
|
+
Cite current version:
|
|
220
|
+
Malpas, A. (2025). microlens-submit (v0.16.1). Zenodo. https://doi.org/10.5281/zenodo.17459753
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "microlens-submit"
|
|
9
|
-
version = "0.16.
|
|
9
|
+
version = "0.16.2"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name="Amber Malpas", email="malpas.1@osu.edu" },
|
|
12
12
|
{name = "Roman Science Platform Team", email = "roman-science-platform@stsci.edu"}
|
|
@@ -36,7 +36,7 @@ dependencies = [
|
|
|
36
36
|
"rich>=13.0.0",
|
|
37
37
|
"pyyaml>=6.0",
|
|
38
38
|
"markdown>=3.4.0",
|
|
39
|
-
|
|
39
|
+
'importlib_resources>=1.0.0; python_version < "3.9"',
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
[project.urls]
|
|
@@ -60,12 +60,13 @@ dev = [
|
|
|
60
60
|
"sphinx",
|
|
61
61
|
"sphinx_rtd_theme",
|
|
62
62
|
"importlib_resources",
|
|
63
|
+
"python-dotenv",
|
|
63
64
|
]
|
|
64
65
|
|
|
65
66
|
# Setuptools package configuration
|
|
66
67
|
[tool.setuptools.packages.find]
|
|
67
68
|
where = ["."]
|
|
68
|
-
include = ["microlens_submit
|
|
69
|
+
include = ["microlens_submit", "microlens_submit.*"]
|
|
69
70
|
exclude = ["tests*", "docs*"]
|
|
70
71
|
|
|
71
72
|
[tool.setuptools.package-data]
|
|
@@ -8,8 +8,8 @@ from setuptools import find_packages, setup
|
|
|
8
8
|
if __name__ == "__main__":
|
|
9
9
|
setup(
|
|
10
10
|
name="microlens-submit",
|
|
11
|
-
version="0.16.
|
|
12
|
-
packages=find_packages(),
|
|
11
|
+
version="0.16.2",
|
|
12
|
+
packages=find_packages(include=("microlens_submit", "microlens_submit.*")),
|
|
13
13
|
include_package_data=True,
|
|
14
14
|
package_data={
|
|
15
15
|
"microlens_submit": ["assets/*"],
|
|
@@ -61,20 +61,22 @@ Note:
|
|
|
61
61
|
The test suite ensures CLI functionality matches API behavior.
|
|
62
62
|
"""
|
|
63
63
|
|
|
64
|
-
# Install package in editable mode to ensure assets are available
|
|
64
|
+
# Install package in editable mode to ensure assets are available when running tests
|
|
65
|
+
import os
|
|
65
66
|
import subprocess
|
|
66
67
|
import sys
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
if os.environ.get("MICROLENS_SKIP_EDITABLE_INSTALL") != "1":
|
|
70
|
+
try:
|
|
71
|
+
subprocess.run(
|
|
72
|
+
[sys.executable, "-m", "pip", "install", "-e", "."],
|
|
73
|
+
capture_output=True,
|
|
74
|
+
check=True,
|
|
75
|
+
)
|
|
76
|
+
except subprocess.CalledProcessError:
|
|
77
|
+
# If we're not in the right directory or package isn't set up, continue anyway
|
|
78
|
+
# The asset check fixture will catch missing assets
|
|
79
|
+
pass
|
|
78
80
|
|
|
79
81
|
import json
|
|
80
82
|
import zipfile
|
|
@@ -388,10 +390,13 @@ def test_cli_compare_solutions_skips_zero_data_points():
|
|
|
388
390
|
|
|
389
391
|
result = runner.invoke(app, ["compare-solutions", "evt"])
|
|
390
392
|
assert result.exit_code == 0
|
|
391
|
-
# Should only show the valid solution in the table
|
|
392
|
-
#
|
|
393
|
-
#
|
|
394
|
-
|
|
393
|
+
# Should only show the valid solution in the table. Depending on console support
|
|
394
|
+
# Rich may render Unicode box characters or ASCII fallbacks. Count the header row
|
|
395
|
+
# that contains the "Relative" column (ignoring the footer line that describes BIC).
|
|
396
|
+
header_lines = [
|
|
397
|
+
line for line in result.stdout.splitlines() if "Relative" in line and "Relative probabilities" not in line
|
|
398
|
+
]
|
|
399
|
+
assert len(header_lines) == 1
|
|
395
400
|
|
|
396
401
|
|
|
397
402
|
def test_params_file_option_and_bands():
|
|
File without changes
|
|
File without changes
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/assets/github-desktop_logo.png
RENAMED
|
File without changes
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/assets/rges-pit_logo.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/__init__.py
RENAMED
|
File without changes
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit/cli/commands/dossier.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{microlens_submit-0.16.0 → microlens_submit-0.16.2}/microlens_submit.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|