microlens-submit 0.12.2__py3-none-any.whl → 0.16.1__py3-none-any.whl
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/__init__.py +7 -157
- microlens_submit/cli/__init__.py +5 -0
- microlens_submit/cli/__main__.py +6 -0
- microlens_submit/cli/commands/__init__.py +1 -0
- microlens_submit/cli/commands/dossier.py +139 -0
- microlens_submit/cli/commands/export.py +177 -0
- microlens_submit/cli/commands/init.py +172 -0
- microlens_submit/cli/commands/solutions.py +722 -0
- microlens_submit/cli/commands/validation.py +241 -0
- microlens_submit/cli/main.py +120 -0
- microlens_submit/dossier/__init__.py +51 -0
- microlens_submit/dossier/dashboard.py +503 -0
- microlens_submit/dossier/event_page.py +370 -0
- microlens_submit/dossier/full_report.py +330 -0
- microlens_submit/dossier/solution_page.py +534 -0
- microlens_submit/dossier/utils.py +111 -0
- microlens_submit/error_messages.py +283 -0
- microlens_submit/models/__init__.py +28 -0
- microlens_submit/models/event.py +406 -0
- microlens_submit/models/solution.py +569 -0
- microlens_submit/models/submission.py +569 -0
- microlens_submit/tier_validation.py +208 -0
- microlens_submit/utils.py +373 -0
- microlens_submit/validate_parameters.py +478 -180
- {microlens_submit-0.12.2.dist-info → microlens_submit-0.16.1.dist-info}/METADATA +52 -14
- microlens_submit-0.16.1.dist-info/RECORD +32 -0
- microlens_submit/api.py +0 -1257
- microlens_submit/cli.py +0 -1803
- microlens_submit/dossier.py +0 -1443
- microlens_submit-0.12.2.dist-info/RECORD +0 -13
- {microlens_submit-0.12.2.dist-info → microlens_submit-0.16.1.dist-info}/WHEEL +0 -0
- {microlens_submit-0.12.2.dist-info → microlens_submit-0.16.1.dist-info}/entry_points.txt +0 -0
- {microlens_submit-0.12.2.dist-info → microlens_submit-0.16.1.dist-info}/licenses/LICENSE +0 -0
- {microlens_submit-0.12.2.dist-info → microlens_submit-0.16.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enhanced error messaging for microlens-submit.
|
|
3
|
+
|
|
4
|
+
This module provides improved error messages with suggestions and context
|
|
5
|
+
to help users understand and fix issues more easily.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from typing import Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_model_type_suggestions(invalid_type: str) -> List[str]:
|
|
13
|
+
"""Get suggestions for model type corrections."""
|
|
14
|
+
valid_types = ["1S1L", "1S2L", "2S1L", "2S2L", "1S3L", "2S3L", "other"]
|
|
15
|
+
|
|
16
|
+
# Common typos and their corrections
|
|
17
|
+
common_typos = {
|
|
18
|
+
"1s1l": "1S1L",
|
|
19
|
+
"1s2l": "1S2L",
|
|
20
|
+
"2s1l": "2S1L",
|
|
21
|
+
"2s2l": "2S2L",
|
|
22
|
+
"1s3l": "1S3L",
|
|
23
|
+
"2s3l": "2S3L",
|
|
24
|
+
"1S1l": "1S1L",
|
|
25
|
+
"1S2l": "1S2L",
|
|
26
|
+
"2S1l": "2S1L",
|
|
27
|
+
"2S2l": "2S2L",
|
|
28
|
+
"1S3l": "1S3L",
|
|
29
|
+
"2S3l": "2S3L",
|
|
30
|
+
"1sl1": "1S1L",
|
|
31
|
+
"1sl2": "1S2L",
|
|
32
|
+
"2sl1": "2S1L",
|
|
33
|
+
"2sl2": "2S2L",
|
|
34
|
+
"1sl3": "1S3L",
|
|
35
|
+
"2sl3": "2S3L",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
suggestions = []
|
|
39
|
+
|
|
40
|
+
# Check for exact typo match
|
|
41
|
+
if invalid_type in common_typos:
|
|
42
|
+
suggestions.append(f"Did you mean '{common_typos[invalid_type]}'?")
|
|
43
|
+
|
|
44
|
+
# Check for similar patterns
|
|
45
|
+
if invalid_type.upper() in valid_types:
|
|
46
|
+
suggestions.append(f"Model types are case-sensitive. Try '{invalid_type.upper()}'")
|
|
47
|
+
|
|
48
|
+
# Check for partial matches
|
|
49
|
+
for valid_type in valid_types:
|
|
50
|
+
if valid_type.lower() in invalid_type.lower() or invalid_type.lower() in valid_type.lower():
|
|
51
|
+
if valid_type not in suggestions:
|
|
52
|
+
suggestions.append(f"Did you mean '{valid_type}'?")
|
|
53
|
+
|
|
54
|
+
return suggestions
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_parameter_suggestions(model_type: str, user_param: str) -> List[str]:
|
|
58
|
+
"""Get suggestions for missing parameter corrections."""
|
|
59
|
+
# Common parameter typos and their corrections
|
|
60
|
+
common_typos = {
|
|
61
|
+
"t0": ["t_0", "T0", "T_0"],
|
|
62
|
+
"u0": ["u_0", "U0", "U_0"],
|
|
63
|
+
"tE": ["te", "TE", "t_e", "T_E", "einstein_time"],
|
|
64
|
+
"s": ["sep", "separation", "S"],
|
|
65
|
+
"q": ["mass_ratio", "Q", "ratio"],
|
|
66
|
+
"alpha": ["angle", "ANGLE", "ALPHA"],
|
|
67
|
+
"piEN": ["pien", "PIEN", "pi_en", "PI_EN"],
|
|
68
|
+
"piEE": ["piee", "PIEE", "pi_ee", "PI_EE"],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
suggestions = []
|
|
72
|
+
# If the user_param matches a typo for any canonical param,
|
|
73
|
+
# suggest the canonical param
|
|
74
|
+
for canonical, typos in common_typos.items():
|
|
75
|
+
for typo in typos:
|
|
76
|
+
if user_param.lower() == typo.lower():
|
|
77
|
+
suggestions.append(f"Did you mean '{canonical}' instead of '{user_param}'?")
|
|
78
|
+
# Also suggest case sensitivity if relevant
|
|
79
|
+
for canonical in common_typos.keys():
|
|
80
|
+
if user_param.lower() == canonical.lower() and user_param != canonical:
|
|
81
|
+
suggestions.append(f"Parameter names are case-sensitive. Try '{canonical}'")
|
|
82
|
+
return suggestions
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_higher_order_effect_suggestions(invalid_effect: str) -> List[str]:
|
|
86
|
+
"""Get suggestions for higher-order effect corrections."""
|
|
87
|
+
valid_effects = [
|
|
88
|
+
"parallax",
|
|
89
|
+
"finite-source",
|
|
90
|
+
"lens-orbital-motion",
|
|
91
|
+
"xallarap",
|
|
92
|
+
"gaussian-process",
|
|
93
|
+
"stellar-rotation",
|
|
94
|
+
"fitted-limb-darkening",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
# Common typos and their corrections
|
|
98
|
+
common_typos = {
|
|
99
|
+
"parallax": ["paralax", "parallax", "PARALLAX"],
|
|
100
|
+
"finite-source": [
|
|
101
|
+
"finite_source",
|
|
102
|
+
"finite source",
|
|
103
|
+
"finite-source",
|
|
104
|
+
"FINITE-SOURCE",
|
|
105
|
+
],
|
|
106
|
+
"lens-orbital-motion": [
|
|
107
|
+
"lens_orbital_motion",
|
|
108
|
+
"lens orbital motion",
|
|
109
|
+
"LENS-ORBITAL-MOTION",
|
|
110
|
+
],
|
|
111
|
+
"xallarap": ["xallarap", "XALLARAP", "xallarap"],
|
|
112
|
+
"gaussian-process": [
|
|
113
|
+
"gaussian_process",
|
|
114
|
+
"gaussian process",
|
|
115
|
+
"GAUSSIAN-PROCESS",
|
|
116
|
+
],
|
|
117
|
+
"stellar-rotation": [
|
|
118
|
+
"stellar_rotation",
|
|
119
|
+
"stellar rotation",
|
|
120
|
+
"STELLAR-ROTATION",
|
|
121
|
+
],
|
|
122
|
+
"fitted-limb-darkening": [
|
|
123
|
+
"fitted_limb_darkening",
|
|
124
|
+
"fitted limb darkening",
|
|
125
|
+
"FITTED-LIMB-DARKENING",
|
|
126
|
+
],
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
suggestions = []
|
|
130
|
+
|
|
131
|
+
# Check for exact typo match
|
|
132
|
+
if invalid_effect in common_typos:
|
|
133
|
+
for typo in common_typos[invalid_effect]:
|
|
134
|
+
suggestions.append(f"Did you mean '{typo}' instead of '{invalid_effect}'?")
|
|
135
|
+
|
|
136
|
+
# Check for case variations
|
|
137
|
+
if invalid_effect.lower() in [e.lower() for e in valid_effects]:
|
|
138
|
+
for effect in valid_effects:
|
|
139
|
+
if effect.lower() == invalid_effect.lower():
|
|
140
|
+
suggestions.append(f"Effect names are case-sensitive. Try '{effect}'")
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
# Check for partial matches
|
|
144
|
+
for valid_effect in valid_effects:
|
|
145
|
+
if valid_effect.lower() in invalid_effect.lower() or invalid_effect.lower() in valid_effect.lower():
|
|
146
|
+
if valid_effect not in suggestions:
|
|
147
|
+
suggestions.append(f"Did you mean '{valid_effect}'?")
|
|
148
|
+
|
|
149
|
+
return suggestions
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def format_validation_message(message: str, suggestions: Optional[List[str]] = None) -> str:
|
|
153
|
+
"""Format a validation message with optional suggestions."""
|
|
154
|
+
if not suggestions:
|
|
155
|
+
return message
|
|
156
|
+
|
|
157
|
+
formatted = message
|
|
158
|
+
if len(suggestions) == 1:
|
|
159
|
+
formatted += f"\n💡 Suggestion: {suggestions[0]}"
|
|
160
|
+
else:
|
|
161
|
+
formatted += "\n💡 Suggestions:"
|
|
162
|
+
for suggestion in suggestions:
|
|
163
|
+
formatted += f"\n • {suggestion}"
|
|
164
|
+
|
|
165
|
+
return formatted
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def enhance_validation_messages(messages: List[str], model_type: str, parameters: Dict) -> List[str]:
|
|
169
|
+
"""Enhance validation messages with helpful suggestions."""
|
|
170
|
+
enhanced_messages = []
|
|
171
|
+
|
|
172
|
+
for message in messages:
|
|
173
|
+
suggestions = []
|
|
174
|
+
|
|
175
|
+
# Check for model type errors
|
|
176
|
+
if "Unknown model type:" in message:
|
|
177
|
+
# Extract the invalid model type from the message
|
|
178
|
+
match = re.search(r"Unknown model type: '([^']+)'", message)
|
|
179
|
+
if match:
|
|
180
|
+
invalid_type = match.group(1)
|
|
181
|
+
suggestions = get_model_type_suggestions(invalid_type)
|
|
182
|
+
|
|
183
|
+
# Check for missing parameter errors
|
|
184
|
+
elif "Missing required core parameter" in message:
|
|
185
|
+
# Extract the missing parameter from the message
|
|
186
|
+
match = re.search(r"Missing required core parameter '([^']+)'", message)
|
|
187
|
+
if match:
|
|
188
|
+
missing_param = match.group(1)
|
|
189
|
+
suggestions = get_parameter_suggestions(model_type, missing_param)
|
|
190
|
+
|
|
191
|
+
# Check for missing higher-order effect parameters
|
|
192
|
+
elif "Missing required parameter" in message and "for effect" in message:
|
|
193
|
+
# Extract the missing parameter and effect from the message
|
|
194
|
+
match = re.search(r"Missing required parameter '([^']+)' for effect '([^']+)'", message)
|
|
195
|
+
if match:
|
|
196
|
+
missing_param = match.group(1)
|
|
197
|
+
suggestions = get_parameter_suggestions(model_type, missing_param)
|
|
198
|
+
|
|
199
|
+
# Check for unknown higher-order effect errors
|
|
200
|
+
elif "Unknown higher-order effect:" in message:
|
|
201
|
+
# Extract the invalid effect from the message
|
|
202
|
+
match = re.search(r"Unknown higher-order effect: '([^']+)'", message)
|
|
203
|
+
if match:
|
|
204
|
+
invalid_effect = match.group(1)
|
|
205
|
+
suggestions = get_higher_order_effect_suggestions(invalid_effect)
|
|
206
|
+
|
|
207
|
+
# Check for unrecognized parameter warnings
|
|
208
|
+
elif "Parameter" in message and "not recognized" in message:
|
|
209
|
+
# Extract the unrecognized parameter from the message
|
|
210
|
+
match = re.search(r"Parameter '([^']+)' not recognized", message)
|
|
211
|
+
if not match:
|
|
212
|
+
# Try with "Warning:" prefix
|
|
213
|
+
match = re.search(r"Warning: Parameter '([^']+)' not recognized", message)
|
|
214
|
+
if match:
|
|
215
|
+
unrecognized_param = match.group(1)
|
|
216
|
+
suggestions = get_parameter_suggestions(model_type, unrecognized_param)
|
|
217
|
+
|
|
218
|
+
enhanced_messages.append(format_validation_message(message, suggestions))
|
|
219
|
+
|
|
220
|
+
return enhanced_messages
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def get_quick_fix_suggestions(model_type: str, missing_params: List[str]) -> List[str]:
|
|
224
|
+
"""Get quick fix suggestions for common missing parameters."""
|
|
225
|
+
suggestions = []
|
|
226
|
+
|
|
227
|
+
# Common parameter examples for each model type
|
|
228
|
+
model_examples = {
|
|
229
|
+
"1S1L": {"t0": "2459123.5", "u0": "0.1", "tE": "20.0"},
|
|
230
|
+
"1S2L": {
|
|
231
|
+
"t0": "2459123.5",
|
|
232
|
+
"u0": "0.1",
|
|
233
|
+
"tE": "20.0",
|
|
234
|
+
"s": "1.2",
|
|
235
|
+
"q": "0.001",
|
|
236
|
+
"alpha": "45.0",
|
|
237
|
+
},
|
|
238
|
+
"2S1L": {
|
|
239
|
+
"t0": "2459123.5",
|
|
240
|
+
"u0": "0.1",
|
|
241
|
+
"tE": "20.0",
|
|
242
|
+
"q_flux": "0.5",
|
|
243
|
+
"t_star": "0.1",
|
|
244
|
+
},
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if model_type in model_examples:
|
|
248
|
+
examples = model_examples[model_type]
|
|
249
|
+
for param in missing_params:
|
|
250
|
+
if param in examples:
|
|
251
|
+
suggestions.append(f"Add --param {param}={examples[param]}")
|
|
252
|
+
|
|
253
|
+
return suggestions
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def format_cli_error_with_suggestions(error_message: str, context: Dict) -> str:
|
|
257
|
+
"""Format CLI errors with contextual suggestions."""
|
|
258
|
+
enhanced = error_message
|
|
259
|
+
|
|
260
|
+
# Add context-specific suggestions
|
|
261
|
+
if "model_type must be one of" in error_message:
|
|
262
|
+
enhanced += "\n\n💡 Quick Start Examples:"
|
|
263
|
+
enhanced += (
|
|
264
|
+
"\n microlens-submit add-solution EVENT123 1S1L " "--param t0=2459123.5 --param u0=0.1 --param tE=20.0"
|
|
265
|
+
)
|
|
266
|
+
enhanced += (
|
|
267
|
+
"\n microlens-submit add-solution EVENT123 1S2L "
|
|
268
|
+
"--param t0=2459123.5 --param u0=0.1 --param tE=20.0 "
|
|
269
|
+
"--param s=1.2 --param q=0.001 --param alpha=45.0"
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
elif "Invalid parameter format" in error_message:
|
|
273
|
+
enhanced += "\n\n�� Parameter Format:"
|
|
274
|
+
enhanced += "\n Use --param name=value (e.g., --param t0=2459123.5)"
|
|
275
|
+
enhanced += "\n Multiple parameters: --param t0=2459123.5 " "--param u0=0.1 --param tE=20.0"
|
|
276
|
+
|
|
277
|
+
elif "Cannot use --param with --params-file" in error_message:
|
|
278
|
+
enhanced += "\n\n💡 Parameter Options:"
|
|
279
|
+
enhanced += "\n Use --param for individual parameters on command line"
|
|
280
|
+
enhanced += "\n Use --params-file for parameters from a JSON/YAML file"
|
|
281
|
+
enhanced += "\n Choose one method, not both"
|
|
282
|
+
|
|
283
|
+
return enhanced
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for microlens-submit.
|
|
3
|
+
|
|
4
|
+
This package contains the core data models for managing
|
|
5
|
+
microlensing challenge submissions:
|
|
6
|
+
- Solution: Individual model fit with parameters and metadata
|
|
7
|
+
- Event: Container for solutions to a single microlensing event
|
|
8
|
+
- Submission: Top-level container for a submission project
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> from microlens_submit.models import Solution, Event, Submission
|
|
12
|
+
>>>
|
|
13
|
+
>>> # Create a submission
|
|
14
|
+
>>> submission = Submission()
|
|
15
|
+
>>> submission.team_name = "Team Alpha"
|
|
16
|
+
>>>
|
|
17
|
+
>>> # Add an event and solution
|
|
18
|
+
>>> event = submission.get_event("EVENT001")
|
|
19
|
+
>>> solution = event.add_solution("1S1L", {"t0": 2459123.5, "u0": 0.1, "tE": 20.0})
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from .event import Event
|
|
23
|
+
from .solution import Solution
|
|
24
|
+
from .submission import Submission
|
|
25
|
+
|
|
26
|
+
Event.model_rebuild()
|
|
27
|
+
|
|
28
|
+
__all__ = ["Solution", "Event", "Submission"]
|