microlens-submit 0.16.4__py3-none-any.whl → 0.16.5__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.
@@ -5,7 +5,7 @@ validate, and export a challenge submission using either the Python API or
5
5
  the command line interface.
6
6
  """
7
7
 
8
- __version__ = "0.16.4"
8
+ __version__ = "0.16.5"
9
9
 
10
10
  from .models import Event, Solution, Submission
11
11
  from .utils import load
@@ -12,6 +12,7 @@ from pathlib import Path
12
12
  from .. import __version__
13
13
  from ..models import Event, Submission
14
14
  from .solution_page import generate_solution_page
15
+ from .utils import resolve_dossier_asset_path
15
16
 
16
17
 
17
18
  def generate_event_page(event: Event, submission: Submission, output_dir: Path) -> None:
@@ -50,7 +51,17 @@ def generate_event_page(event: Event, submission: Submission, output_dir: Path)
50
51
  to individual solution pages.
51
52
  """
52
53
  # Prepare output directory (already created)
53
- html = _generate_event_page_content(event, submission)
54
+ project_root = Path(submission.project_path)
55
+ event_data_link = ""
56
+ if hasattr(event, "event_data_path") and event.event_data_path:
57
+ event_data_link = resolve_dossier_asset_path(
58
+ event.event_data_path,
59
+ project_root,
60
+ output_dir,
61
+ subdir="event-data",
62
+ prefix=f"{event.event_id}_event_data",
63
+ )
64
+ html = _generate_event_page_content(event, submission, event_data_link=event_data_link)
54
65
  with (output_dir / f"{event.event_id}.html").open("w", encoding="utf-8") as f:
55
66
  f.write(html)
56
67
 
@@ -59,7 +70,12 @@ def generate_event_page(event: Event, submission: Submission, output_dir: Path)
59
70
  generate_solution_page(sol, event, submission, output_dir)
60
71
 
61
72
 
62
- def _generate_event_page_content(event: Event, submission: Submission) -> str:
73
+ def _generate_event_page_content(
74
+ event: Event,
75
+ submission: Submission,
76
+ *,
77
+ event_data_link: str = "",
78
+ ) -> str:
63
79
  """Generate the HTML content for an event dossier page.
64
80
 
65
81
  Creates the complete HTML content for a single event page, including
@@ -163,10 +179,10 @@ def _generate_event_page_content(event: Event, submission: Submission) -> str:
163
179
  )
164
180
  # Optional raw data link
165
181
  raw_data_html = ""
166
- if hasattr(event, "event_data_path") and event.event_data_path:
182
+ if event_data_link:
167
183
  raw_data_html = (
168
184
  f'<p class="text-rtd-text">Raw Event Data: '
169
- f'<a href="{event.event_data_path}" '
185
+ f'<a href="{event_data_link}" '
170
186
  f'class="text-rtd-accent hover:underline">Download Data</a></p>'
171
187
  )
172
188
  # HTML content
@@ -15,6 +15,7 @@ from ..models.solution import Solution
15
15
  from .dashboard import _generate_dashboard_content
16
16
  from .event_page import _generate_event_page_content
17
17
  from .solution_page import _generate_solution_page_content
18
+ from .utils import resolve_dossier_asset_path
18
19
 
19
20
 
20
21
  def generate_full_dossier_report_html(submission: Submission, output_dir: Path) -> None:
@@ -63,19 +64,57 @@ def generate_full_dossier_report_html(submission: Submission, output_dir: Path)
63
64
  all_html_sections.append('<hr class="my-8 border-t-2 border-rtd-accent">') # Divider after dashboard
64
65
 
65
66
  # Events and solutions
67
+ project_root = Path(submission.project_path)
66
68
  for event in submission.events.values():
67
- event_html = _generate_event_page_content(event, submission)
69
+ event_data_link = ""
70
+ if hasattr(event, "event_data_path") and event.event_data_path:
71
+ event_data_link = resolve_dossier_asset_path(
72
+ event.event_data_path,
73
+ project_root,
74
+ output_dir,
75
+ subdir="event-data",
76
+ prefix=f"{event.event_id}_event_data",
77
+ )
78
+ event_html = _generate_event_page_content(event, submission, event_data_link=event_data_link)
68
79
  event_body = extract_main_content_body(event_html, section_type="event", section_id=event.event_id)
69
80
  all_html_sections.append(event_body)
70
81
  all_html_sections.append('<hr class="my-8 border-t-2 border-rtd-accent">') # Divider after event
71
82
 
72
83
  for sol in event.get_active_solutions():
73
- sol_html = _generate_solution_page_content(sol, event, submission)
84
+ lc_plot = resolve_dossier_asset_path(
85
+ sol.lightcurve_plot_path,
86
+ project_root,
87
+ output_dir,
88
+ subdir="plots",
89
+ prefix=f"{event.event_id}_{sol.solution_id}_lightcurve",
90
+ )
91
+ lens_plot = resolve_dossier_asset_path(
92
+ sol.lens_plane_plot_path,
93
+ project_root,
94
+ output_dir,
95
+ subdir="plots",
96
+ prefix=f"{event.event_id}_{sol.solution_id}_lens",
97
+ )
98
+ posterior = resolve_dossier_asset_path(
99
+ sol.posterior_path,
100
+ project_root,
101
+ output_dir,
102
+ subdir="posteriors",
103
+ prefix=f"{event.event_id}_{sol.solution_id}_posterior",
104
+ )
105
+ sol_html = _generate_solution_page_content(
106
+ sol,
107
+ event,
108
+ submission,
109
+ lc_plot=lc_plot,
110
+ lens_plot=lens_plot,
111
+ posterior=posterior,
112
+ )
74
113
  sol_body = extract_main_content_body(
75
114
  sol_html,
76
115
  section_type="solution",
77
116
  section_id=sol.solution_id,
78
- project_root=Path(submission.project_path),
117
+ project_root=project_root,
79
118
  solution=sol,
80
119
  )
81
120
  all_html_sections.append(sol_body)
@@ -8,6 +8,7 @@ notes rendering, and evaluator-only sections.
8
8
 
9
9
  from datetime import datetime
10
10
  from pathlib import Path
11
+ from typing import Optional
11
12
 
12
13
  import markdown
13
14
 
@@ -15,6 +16,7 @@ from .. import __version__
15
16
  from ..models.event import Event
16
17
  from ..models.solution import Solution
17
18
  from ..models.submission import Submission
19
+ from .utils import resolve_dossier_asset_path
18
20
 
19
21
 
20
22
  def generate_solution_page(solution: Solution, event: Event, submission: Submission, output_dir: Path) -> None:
@@ -55,12 +57,49 @@ def generate_solution_page(solution: Solution, event: Event, submission: Submiss
55
57
  Notes are rendered with syntax highlighting for code blocks.
56
58
  """
57
59
  # Prepare output directory (already created)
58
- html = _generate_solution_page_content(solution, event, submission)
60
+ project_root = Path(submission.project_path)
61
+ lc_plot = resolve_dossier_asset_path(
62
+ solution.lightcurve_plot_path,
63
+ project_root,
64
+ output_dir,
65
+ subdir="plots",
66
+ prefix=f"{event.event_id}_{solution.solution_id}_lightcurve",
67
+ )
68
+ lens_plot = resolve_dossier_asset_path(
69
+ solution.lens_plane_plot_path,
70
+ project_root,
71
+ output_dir,
72
+ subdir="plots",
73
+ prefix=f"{event.event_id}_{solution.solution_id}_lens",
74
+ )
75
+ posterior = resolve_dossier_asset_path(
76
+ solution.posterior_path,
77
+ project_root,
78
+ output_dir,
79
+ subdir="posteriors",
80
+ prefix=f"{event.event_id}_{solution.solution_id}_posterior",
81
+ )
82
+ html = _generate_solution_page_content(
83
+ solution,
84
+ event,
85
+ submission,
86
+ lc_plot=lc_plot,
87
+ lens_plot=lens_plot,
88
+ posterior=posterior,
89
+ )
59
90
  with (output_dir / f"{solution.solution_id}.html").open("w", encoding="utf-8") as f:
60
91
  f.write(html)
61
92
 
62
93
 
63
- def _generate_solution_page_content(solution: Solution, event: Event, submission: Submission) -> str:
94
+ def _generate_solution_page_content(
95
+ solution: Solution,
96
+ event: Event,
97
+ submission: Submission,
98
+ *,
99
+ lc_plot: Optional[str] = None,
100
+ lens_plot: Optional[str] = None,
101
+ posterior: Optional[str] = None,
102
+ ) -> str:
64
103
  """Generate the HTML content for a solution dossier page.
65
104
 
66
105
  Creates the complete HTML content for a single solution page, including
@@ -138,9 +177,9 @@ def _generate_solution_page_content(solution: Solution, event: Event, submission
138
177
  # Higher-order effects
139
178
  hoe_str = ", ".join(solution.higher_order_effects) if solution.higher_order_effects else "None"
140
179
  # Plot paths (relative to solution page)
141
- lc_plot = solution.lightcurve_plot_path or ""
142
- lens_plot = solution.lens_plane_plot_path or ""
143
- posterior = solution.posterior_path or ""
180
+ lc_plot = lc_plot if lc_plot is not None else (solution.lightcurve_plot_path or "")
181
+ lens_plot = lens_plot if lens_plot is not None else (solution.lens_plane_plot_path or "")
182
+ posterior = posterior if posterior is not None else (solution.posterior_path or "")
144
183
  # Physical parameters table
145
184
  phys_rows = []
146
185
  phys = solution.physical_parameters or {}
@@ -6,7 +6,11 @@ generation package, including hardware formatting, GitHub URL parsing,
6
6
  and other helper functions.
7
7
  """
8
8
 
9
+ import hashlib
10
+ import shutil
11
+ from pathlib import Path
9
12
  from typing import Any, Dict, Optional
13
+ from urllib.parse import quote, urlparse
10
14
 
11
15
 
12
16
  def format_hardware_info(hardware_info: Optional[Dict[str, Any]]) -> str:
@@ -109,3 +113,62 @@ def extract_github_repo_name(repo_url: str) -> str:
109
113
  return f"{parts[-2]}/{parts[-1]}"
110
114
 
111
115
  return None
116
+
117
+
118
+ def resolve_dossier_asset_path(
119
+ path_value: Optional[str],
120
+ project_root: Path,
121
+ output_dir: Path,
122
+ *,
123
+ subdir: str,
124
+ prefix: Optional[str] = None,
125
+ ) -> str:
126
+ """Resolve and copy a local asset into the dossier assets folder.
127
+
128
+ If the input path is a URL, it is returned unchanged. If it is a local
129
+ filesystem path, it is resolved relative to the project root, copied into
130
+ the dossier assets folder, and returned as a URL-encoded relative path.
131
+
132
+ Args:
133
+ path_value: The original path or URL string.
134
+ project_root: Root directory of the submission project.
135
+ output_dir: Dossier output directory containing the HTML files.
136
+ subdir: Subdirectory under assets/ for the copied file.
137
+ prefix: Optional prefix for the copied file name to avoid collisions.
138
+
139
+ Returns:
140
+ str: A URL or a relative path suitable for HTML src/href attributes.
141
+ """
142
+ if not path_value:
143
+ return ""
144
+
145
+ parsed = urlparse(path_value)
146
+ if parsed.scheme in {"http", "https", "data"}:
147
+ return path_value
148
+
149
+ if parsed.scheme == "file":
150
+ source_path = Path(parsed.path)
151
+ else:
152
+ source_path = Path(path_value).expanduser()
153
+
154
+ if not source_path.is_absolute():
155
+ source_path = (project_root / source_path).resolve()
156
+ else:
157
+ source_path = source_path.resolve()
158
+
159
+ if not source_path.exists():
160
+ return path_value
161
+
162
+ assets_dir = output_dir / "assets" / subdir
163
+ assets_dir.mkdir(parents=True, exist_ok=True)
164
+
165
+ digest = hashlib.sha1(str(source_path).encode("utf-8")).hexdigest()[:8]
166
+ safe_prefix = prefix or "asset"
167
+ dest_name = f"{safe_prefix}_{digest}_{source_path.name}"
168
+ dest_path = assets_dir / dest_name
169
+
170
+ if not dest_path.exists():
171
+ shutil.copy2(source_path, dest_path)
172
+
173
+ rel_path = dest_path.relative_to(output_dir).as_posix()
174
+ return quote(rel_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microlens-submit
3
- Version: 0.16.4
3
+ Version: 0.16.5
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
@@ -95,6 +95,10 @@ The CLI is the recommended way to interact with your submission project.
95
95
 
96
96
  You can pass ``--no-color`` to any command if your terminal does not support ANSI colors.
97
97
 
98
+ **Windows note:** If ``microlens-submit`` is not found after ``pip install``, your Python Scripts folder is likely missing from PATH.
99
+ Try ``py -m pip install microlens-submit`` and run ``py -m microlens_submit.cli --help``, or add the Scripts path shown by
100
+ ``py -m pip show -f microlens-submit`` to PATH.
101
+
98
102
  1. Initialize your project:
99
103
 
100
104
  ```bash
@@ -1,4 +1,4 @@
1
- microlens_submit/__init__.py,sha256=UdGr8NQ2klUr7cLPboLkHX6qRsn_rTwVfv3LrJ6Z8FE,399
1
+ microlens_submit/__init__.py,sha256=fyqDCZ_UClGfQ7zkhWJC8HFJqh2RQDudEXw2LwhLIYs,399
2
2
  microlens_submit/error_messages.py,sha256=8Wzx1NQiAF7hUOjlt-uGhtPC7akknv_PDkokoxhWquk,10411
3
3
  microlens_submit/text_symbols.py,sha256=PbQSkqF_FwTBs45TkUL0zZl74IYDz7L4xVxy8eKxQsU,3146
4
4
  microlens_submit/tier_validation.py,sha256=FbVHQwItqQnrCeKhkQSgQmMT6dgreTGOptRRYf-AYVE,6649
@@ -17,17 +17,17 @@ microlens_submit/cli/commands/solutions.py,sha256=GvbAiiTaCaqcDRHOAReaMY-gDLlahR
17
17
  microlens_submit/cli/commands/validation.py,sha256=1M8mYSNopsA30u3S-yFBd3iJZxsDoNa0vhwo9brJ1VQ,9028
18
18
  microlens_submit/dossier/__init__.py,sha256=INAacbrY0Wi5ueH8c7b156bGzelyUFcynbE7_YRiku0,1948
19
19
  microlens_submit/dossier/dashboard.py,sha256=4OvTUCxIC4LbAqKwimIFhi65fNo5MMJswiQ5OWtyWFA,19907
20
- microlens_submit/dossier/event_page.py,sha256=F9waw-Ce2_4ikdCPo-hNURUSYEPGMCfvsY3PbCXpsFg,14425
21
- microlens_submit/dossier/full_report.py,sha256=zQXoo6ZQfwv_NNFFel3ZYW1DgnqD--VU0L7J7p9yEng,12864
22
- microlens_submit/dossier/solution_page.py,sha256=qp2JaDamHD__3bwOzZ3CRj2UUSCiOf94wNyOfkQUGGU,23593
23
- microlens_submit/dossier/utils.py,sha256=LopBbVg6nzQasL1lnaI63y3bpmqYqBeDEwfB_NqEeCA,3845
20
+ microlens_submit/dossier/event_page.py,sha256=7740o3tpW9Urv7GSzYdp2TiphvDi6U7XnjlLZYipvLw,14878
21
+ microlens_submit/dossier/full_report.py,sha256=cNkaUML93oRnARp4VnLkbRMudrByWyj06ohvmH4Qcq0,14310
22
+ microlens_submit/dossier/solution_page.py,sha256=-5kgkOZ9ziNRNAFpVeT_9-6aCcQRL4vv9APopKDFeAw,24748
23
+ microlens_submit/dossier/utils.py,sha256=-DbWByBMsEeQZ-eUyRT74O_3lakE1vHKUD62jPj1_t4,5839
24
24
  microlens_submit/models/__init__.py,sha256=1sHFjAWyFtGgQBRSo8lBYiPzToo4tIoHP3uBjtgJSPY,861
25
25
  microlens_submit/models/event.py,sha256=ifQqE7d7PJTTI9lGylwWV3EGxgyyNGiJtHbm_DLmuys,17105
26
26
  microlens_submit/models/solution.py,sha256=ollTpKv8zMSEqIL2Q9gXJTbaX0fWZt6rg76edBmYOWQ,23629
27
27
  microlens_submit/models/submission.py,sha256=f_ewUFhXghnh-pn077bkfBg_6jVbcN_jRhy2wVdKUgk,27941
28
- microlens_submit-0.16.4.dist-info/licenses/LICENSE,sha256=cy1qkVR-kGxD6FXVsparmU2vHJXYeoyAAHv6SgT67sw,1069
29
- microlens_submit-0.16.4.dist-info/METADATA,sha256=cs2ufm5y99az4OLvMvDN_Y7982LhJKFolxVU9zRO-Ig,10367
30
- microlens_submit-0.16.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- microlens_submit-0.16.4.dist-info/entry_points.txt,sha256=kA85yhxYrpQnUvVZCRS2giz52gaf1ZOfZFjY4RHIZ2s,62
32
- microlens_submit-0.16.4.dist-info/top_level.txt,sha256=uJ9_bADYRySlhEpP-8vTm90ZLV2SrKEzutAaRx8WF0k,17
33
- microlens_submit-0.16.4.dist-info/RECORD,,
28
+ microlens_submit-0.16.5.dist-info/licenses/LICENSE,sha256=cy1qkVR-kGxD6FXVsparmU2vHJXYeoyAAHv6SgT67sw,1069
29
+ microlens_submit-0.16.5.dist-info/METADATA,sha256=4mSk_0rksMNxU7NOl2dEA7J73Zvb1SwJ-DKGW00WPkM,10673
30
+ microlens_submit-0.16.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
31
+ microlens_submit-0.16.5.dist-info/entry_points.txt,sha256=kA85yhxYrpQnUvVZCRS2giz52gaf1ZOfZFjY4RHIZ2s,62
32
+ microlens_submit-0.16.5.dist-info/top_level.txt,sha256=uJ9_bADYRySlhEpP-8vTm90ZLV2SrKEzutAaRx8WF0k,17
33
+ microlens_submit-0.16.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5