roman-snpit-snappl 0.20.0__tar.gz → 0.21.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.

Potentially problematic release.


This version of roman-snpit-snappl might be problematic. Click here for more details.

Files changed (147) hide show
  1. {roman_snpit_snappl-0.20.0/roman_snpit_snappl.egg-info → roman_snpit_snappl-0.21.0}/PKG-INFO +1 -1
  2. roman_snpit_snappl-0.21.0/changes/98.docs.rst +1 -0
  3. roman_snpit_snappl-0.21.0/changes/99.feature.rst +1 -0
  4. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/usage.rst +53 -4
  5. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0/roman_snpit_snappl.egg-info}/PKG-INFO +1 -1
  6. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/roman_snpit_snappl.egg-info/SOURCES.txt +4 -0
  7. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/_version.py +3 -3
  8. roman_snpit_snappl-0.21.0/snappl/db/baseview.py +266 -0
  9. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/db.py +11 -1
  10. roman_snpit_snappl-0.21.0/snappl/db/migrations/20251028_segmap.sql +45 -0
  11. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/webserver.py +114 -58
  12. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/diaobject.py +14 -0
  13. roman_snpit_snappl-0.21.0/snappl/segmap.py +184 -0
  14. roman_snpit_snappl-0.20.0/snappl/db/baseview.py +0 -88
  15. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.cruft.json +0 -0
  16. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/CODEOWNERS +0 -0
  17. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +0 -0
  18. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md +0 -0
  19. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/ISSUE_TEMPLATE/PR_TEMPLATE.md +0 -0
  20. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/dependabot.yml +0 -0
  21. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/labeler.yml +0 -0
  22. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/workflows/changelog.yml +0 -0
  23. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/workflows/run_labeler.yml +0 -0
  24. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/workflows/run_snappl_tests.yml +0 -0
  25. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/workflows/sphinx-deploy.yml +0 -0
  26. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.github/workflows/sub_package_update.yml +0 -0
  27. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.gitignore +0 -0
  28. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/.pre-commit-config.yaml +0 -0
  29. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/CHANGES.rst +0 -0
  30. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/CITATION.cff +0 -0
  31. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/CODE_OF_CONDUCT.md +0 -0
  32. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/CONTRIBUTING.md +0 -0
  33. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/LICENSE +0 -0
  34. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/MANIFEST.in +0 -0
  35. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/README.rst +0 -0
  36. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/.gitkeep +0 -0
  37. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/10.snappl.rst +0 -0
  38. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/13.bugfix.rst +0 -0
  39. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/14.snappl.rst +0 -0
  40. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/15.feature.rst +0 -0
  41. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/16.feature.rst +0 -0
  42. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/18.feature.rst +0 -0
  43. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/20.bugfix.rst +0 -0
  44. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/23.snappl.rst +0 -0
  45. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/26.feature.rst +0 -0
  46. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/29.feature.rst +0 -0
  47. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/3.snappl.rst +0 -0
  48. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/31.feature.rst +0 -0
  49. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/35.snappl.rst +0 -0
  50. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/36.snappl.rst +0 -0
  51. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/37.snappl.rst +0 -0
  52. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/40.snappl.rst +0 -0
  53. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/41.snappl.rst +0 -0
  54. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/43.snappl.rst +0 -0
  55. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/47.feature.rst +0 -0
  56. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/49.docs.rst +0 -0
  57. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/5.snappl.rst +0 -0
  58. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/54.snappl.rst +0 -0
  59. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/57.snappl.rst +0 -0
  60. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/58.snappl.rst +0 -0
  61. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/61.bugfix.rst +0 -0
  62. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/62.snappl.rst +0 -0
  63. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/63.snappl.rst +0 -0
  64. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/65.bugfix.rst +0 -0
  65. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/68.feature.rst +0 -0
  66. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/72.snappl.rst +0 -0
  67. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/73.feature.rst +0 -0
  68. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/74.bugfix.rst +0 -0
  69. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/79.snappl.rst +0 -0
  70. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/8.snappl.rst +0 -0
  71. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/81.snappl.rst +0 -0
  72. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/82.snappl.rst +0 -0
  73. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/83.snappl.rst +0 -0
  74. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/84.feature.rst +0 -0
  75. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/85.feature.rst +0 -0
  76. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/86.snappl.rst +0 -0
  77. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/87.docs.rst +0 -0
  78. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/89.feature.rst +0 -0
  79. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/9.snappl.rst +0 -0
  80. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/91.feature.rst +0 -0
  81. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/92.docs.rst +0 -0
  82. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/changes/94.snappl.rst +0 -0
  83. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/codespell-ignore.txt +0 -0
  84. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/postgres/Dockerfile +0 -0
  85. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/postgres/postgresql.conf +0 -0
  86. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/postgres/run_postgres.sh +0 -0
  87. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/webserver/Dockerfile +0 -0
  88. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/webserver/cert.pem +0 -0
  89. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/webserver/config-test.yaml +0 -0
  90. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/webserver/key.pem +0 -0
  91. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docker/webserver/roman-snpit-server.py +0 -0
  92. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/Makefile +0 -0
  93. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/_static/logo_black_filled.png +0 -0
  94. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/api.rst +0 -0
  95. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/changes.rst +0 -0
  96. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/conf.py +0 -0
  97. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/database_schema.rst +0 -0
  98. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/index.rst +0 -0
  99. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/installation.rst +0 -0
  100. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/docs/make.bat +0 -0
  101. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/experimentation/README.md +0 -0
  102. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/experimentation/ap_phot_simulated_images.py +0 -0
  103. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/experimentation/play_with_photutils.py +0 -0
  104. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/licenses/.DS_Store +0 -0
  105. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/licenses/LICENSE.rst +0 -0
  106. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/licenses/README.rst +0 -0
  107. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/licenses/TEMPLATE_LICENSE.rst +0 -0
  108. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/pyproject.toml +0 -0
  109. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/roman_snpit_snappl.egg-info/dependency_links.txt +0 -0
  110. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/roman_snpit_snappl.egg-info/not-zip-safe +0 -0
  111. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/roman_snpit_snappl.egg-info/requires.txt +0 -0
  112. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/roman_snpit_snappl.egg-info/top_level.txt +0 -0
  113. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/setup.cfg +0 -0
  114. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/setup.py +0 -0
  115. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/__init__.py +0 -0
  116. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/_dev/__init__.py +0 -0
  117. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/_dev/scm_version.py +0 -0
  118. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/admin/load_ou2024_l2images.py +0 -0
  119. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/admin/load_snana_ou2024_diaobject.py +0 -0
  120. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/config.py +0 -0
  121. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/data/README.rst +0 -0
  122. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/20251008_init.sql +0 -0
  123. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/20251017_objdetcount.sql +0 -0
  124. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/20251024_l2image_mjd.sql +0 -0
  125. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/20251025_lightcurve.sql +0 -0
  126. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/apply_migrations.py +0 -0
  127. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/schema_to_rst.py +0 -0
  128. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/scorched_earth.py +0 -0
  129. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/migrations/wipe_all_data.py +0 -0
  130. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/static/romansnpit.css +0 -0
  131. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/static/romansnpit.js +0 -0
  132. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/static/romansnpit_start.js +0 -0
  133. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/templates/base.html +0 -0
  134. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/db/templates/romansnpitdb.html +0 -0
  135. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/dbclient.py +0 -0
  136. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/http.py +0 -0
  137. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/image.py +0 -0
  138. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/image_simulator.py +0 -0
  139. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/imagecollection.py +0 -0
  140. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/lightcurve.py +0 -0
  141. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/logger.py +0 -0
  142. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/provenance.py +0 -0
  143. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/psf.py +0 -0
  144. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/sed.py +0 -0
  145. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/utils.py +0 -0
  146. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/snappl/wcs.py +0 -0
  147. {roman_snpit_snappl-0.20.0 → roman_snpit_snappl-0.21.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roman_snpit_snappl
3
- Version: 0.20.0
3
+ Version: 0.21.0
4
4
  Summary: General, database, and photometry utilities for the Roman SNPIT
5
5
  Author: Roman Supernova Project Infrastructure Team
6
6
  Maintainer-email: Roman SN PIT <raknop@lbl.gov>
@@ -0,0 +1 @@
1
+ Add lightcurve reading and writing to the nov 2025 primer in the usage docs
@@ -0,0 +1 @@
1
+ Add storage and database tracking of segmentation maps
@@ -145,6 +145,14 @@ However, we recommend against that. While 4500 is perhaps not an overwhelming n
145
145
  Finding Objects
146
146
  ===============
147
147
 
148
+ You may just be given an diaobject id. In that case, all you have to do is::
149
+
150
+ from snappl.diaobject import DiaObject
151
+
152
+ obj = DiaObject.get_object( diaobject_id=<id> )
153
+
154
+ where ``<id>`` is the diaobject id you were given. ``obj`` will be a ``DiaObject`` object.
155
+
148
156
  There is also an interface that lets you find objects. For instance, if you want to find all objects within 100 arcseconds of a given location, you could run::
149
157
 
150
158
  from snappl.diaobject import DiaObject
@@ -158,7 +166,7 @@ Here, you can use ``ou2024`` for ``TAG`` and ``load_ou2024_diaobject`` for ``PRO
158
166
  Getting Better Object Positions
159
167
  ===============================
160
168
 
161
- ``DiaObject.find_objects`` will return a list of ``DiaObject`` objects, and these include properties ``ra`` and ``dec``. **However, the positions in the DiaObject object should be viewed as approximate.** They will be the position it had when the object was first discovered. For objects loaded from truth tables, they will be perfect, but of course we won't have truth tables for the real survey. Often, the first discovery will be a relatively low S/N point, and much better positions can be determined; doing so will be one of the jobs of ``phrosty``.
169
+ The ``DiaObject`` you got from ``DiaObject.get_object`` or ``DiaObject.find_object`` include properties ``ra`` and ``dec``. **However, the positions in the DiaObject object should be viewed as approximate.** They will be the position it had when the object was first discovered. For objects loaded from truth tables, they will be perfect, but of course we won't have truth tables for the real survey. Often, the first discovery will be a relatively low S/N point, and much better positions can be determined; doing so will be one of the jobs of ``phrosty``.
162
170
 
163
171
  To get an improved position for an object, assume you have the object in the variable ``diaobj``. You can then call::
164
172
 
@@ -181,10 +189,36 @@ See :ref:`nov2025-provtags` below to figure out what ``TAG`` and ``PROCESS`` sho
181
189
  **Important**: if you use an updated DiaObject position, then the provenance of that position should be one of your upstream provenances; see :ref:`nov2025-making-prov`.
182
190
 
183
191
 
184
- Finding Lightcurves
192
+ Reading Lightcurves
185
193
  ===================
186
194
 
187
- TODO
195
+ To read a lightcurve, you need three or four things:
196
+
197
+ * The ``diaobject_id`` of the object whose lightcurve you want
198
+ * The ``provenance_tag`` of the provenance you want to get lightcurves from
199
+ * The ``process`` to go with the provenance tag
200
+ * (Optional) The ``band`` of the lightcurve
201
+
202
+ The ``provenance_tag`` and ``process`` will be given to you through our fake orchestration process, and may possibly be found in :ref:`nov2025-provtags`. The ``diaobject_id`` will be given to you through the fake orchestration process.
203
+
204
+ To get the lightcurves::
205
+
206
+ from snappl.lightcurve import Lightcurve
207
+
208
+ ltcvs = Lightcurve.find_lightcurves( <diaobject_id>, provenance_tag=<tag>, process=<proc>, dbclient=dbclient )
209
+
210
+ That will return a list of lightcurves. If you also specify ``band=<band>``, that will be a 1-element list with the lightcurve just for that band. (Or a 0-element list if it's not found.)
211
+
212
+ Each element of the list will be a ``Lightcurve`` object. You can find the actual lightcurve data in the ``.lightcurve`` property as an astropy QTable. You can find the metadata dictionary either in the ``.lightcurve.meta`` or in the ``.meta`` property (though the latter will intially be ``None`` until you access the ``.lightcurve`` property). Guaranteed metadata can be found in the `lightcurve schema specification on the PIT wiki <https://github.com/Roman-Supernova-PIT/Roman-Supernova-PIT/wiki/lightcurve>`_. You should probably ignore the ``filepath`` in the metadata, because ``snappl`` has already read the file for you (and put it in the ``.lightcurve`` property).
213
+
214
+
215
+ Finding Segmentation Maps
216
+ =========================
217
+
218
+ You will need to ``from snappl.segmap import SegmentationMap`` and then call ``SegmentationMap.find_segmaps``. You need to pass a provenance tag and a process to ``find_segmaps``; you will be given these (see :ref:`nov2025-provtags`). Beyond that, look at the docstring for that function to see what you can search on.
219
+
220
+ You will get back a list of ``SegmentationMap`` objects. A ``SegmentationMap`` object has several attributes, including ``band``, and eight attributes ``ra_corner_00``, ``dec_corner_00``, etc., where ``00`` is the lower-left pixel, ``01`` upper-left pixel, ``10`` is the lower-right pixel, and ``11`` is the upper-right pixel. The most important field is ``.image``. This is a ``snappl.image.Image`` object. You can get the 2d numpy array of the image data of the segmentaton map from the ``.data`` property of the ``Image`` object.
221
+
188
222
 
189
223
  .. _nov2025-making-prov:
190
224
 
@@ -250,9 +284,24 @@ This will (I believe) return a dictionary that's the same as what you'd get back
250
284
  Saving Lightcurves
251
285
  ==================
252
286
 
253
- TODO
287
+ Lightcurves saved to the database are for only a single band. If you have a multiband lightcurve, from the point of view of the database that's several different lightcurves.
288
+
289
+ To write a Lightcurve, first create a ``Lightcurve`` object::
290
+
291
+ from snappl.lightcurve import Lightcurve
292
+
293
+ ltcv = Lightcurve( data=<data>, meta=<meta> )
294
+
295
+ where ``<meta>`` is a dictionary with metadata, and ``<data>`` is one of an astropy Table, a pandas DataFrame, or a dictionary of lists. On the `lightcurve schema specification on the PIT wiki <https://github.com/Roman-Supernova-PIT/Roman-Supernova-PIT/wiki/lightcurve>`_ you can find the mandatory fields in the metadata dictionary; it's allowed to have additional ones as well. Likewise, there you can find the mandatory columns (and the order of those columns) in the data array. You may also have additional columns in that data array.
296
+
297
+ In order to create a lightcurve, you will need to make a provenance for it; see :ref:`nov2025-making-prov`. Make sure to include the ``diaobject position`` provenance as an upstream provenance if you used a position other than the cheesy approximate one that comes with the ``DiaObject``.
298
+
299
+ Once you have your lightcurve object, do two things::
254
300
 
301
+ ltcv.write()
302
+ ltcv.save_to_db( dbclient=dbclient )
255
303
 
304
+ The first one writes the actual file; it will write it in the standard location, and will populate the ``.filepath`` property with the location of the file *relative to the configured base directory for lightcurves* (which is in config option ``system.paths.lightcurves``). The second call saves a record to the database with information about your lightcurve.
256
305
 
257
306
  .. _nov2025-provtags:
258
307
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roman_snpit_snappl
3
- Version: 0.20.0
3
+ Version: 0.21.0
4
4
  Summary: General, database, and photometry utilities for the Roman SNPIT
5
5
  Author: Roman Supernova Project Infrastructure Team
6
6
  Maintainer-email: Roman SN PIT <raknop@lbl.gov>
@@ -70,6 +70,8 @@ changes/9.snappl.rst
70
70
  changes/91.feature.rst
71
71
  changes/92.docs.rst
72
72
  changes/94.snappl.rst
73
+ changes/98.docs.rst
74
+ changes/99.feature.rst
73
75
  docker/postgres/Dockerfile
74
76
  docker/postgres/postgresql.conf
75
77
  docker/postgres/run_postgres.sh
@@ -115,6 +117,7 @@ snappl/logger.py
115
117
  snappl/provenance.py
116
118
  snappl/psf.py
117
119
  snappl/sed.py
120
+ snappl/segmap.py
118
121
  snappl/utils.py
119
122
  snappl/wcs.py
120
123
  snappl/_dev/__init__.py
@@ -129,6 +132,7 @@ snappl/db/migrations/20251008_init.sql
129
132
  snappl/db/migrations/20251017_objdetcount.sql
130
133
  snappl/db/migrations/20251024_l2image_mjd.sql
131
134
  snappl/db/migrations/20251025_lightcurve.sql
135
+ snappl/db/migrations/20251028_segmap.sql
132
136
  snappl/db/migrations/apply_migrations.py
133
137
  snappl/db/migrations/schema_to_rst.py
134
138
  snappl/db/migrations/scorched_earth.py
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.20.0'
32
- __version_tuple__ = version_tuple = (0, 20, 0)
31
+ __version__ = version = '0.21.0'
32
+ __version_tuple__ = version_tuple = (0, 21, 0)
33
33
 
34
- __commit_id__ = commit_id = 'g3f6286e64'
34
+ __commit_id__ = commit_id = 'g7a9941597'
@@ -0,0 +1,266 @@
1
+ from types import SimpleNamespace
2
+ import simplejson
3
+
4
+ import flask
5
+ import flask.views
6
+ from psycopg import sql
7
+
8
+ from snappl.db import db
9
+ from snappl.logger import SNLogger
10
+ from snappl.utils import SNPITJsonEncoder
11
+
12
+
13
+ # ======================================================================
14
+
15
+ class BaseView( flask.views.View ):
16
+ """A BaseView that all other views can be based on.
17
+
18
+ If the view doesn't override dispatch_request, then it must define a
19
+ function do_the_things. That should return a dict, list, string,
20
+ tuple, or ...something else.
21
+
22
+ If it returns a dict or a list, the web server will send to the
23
+ client application/json with status 200. If the result is a string,
24
+ it the web server will send to the client text/plain with status
25
+ 200. If it's a tuple, just let Flask deal with that tuple to figure
26
+ out what the web server should send to the client. Otherwise, the
27
+ web server will send to the client application/octet-stream with
28
+ status 200.
29
+
30
+ Subclasses that do not override dispatch_request do not need to call
31
+ check_auth. However, if they do override it, they should call that
32
+ if the results shouldn't be sent back to an unauthenticated user.
33
+
34
+ """
35
+
36
+ _admin_required = False
37
+
38
+ def __init__( self, *args, **kwargs ):
39
+ super().__init__( *args, **kwargs )
40
+
41
+
42
+ def check_json_keys( self, needed_keys, allowed_keys, minmax_keys=set() ):
43
+ if not flask.request.is_json:
44
+ raise RuntimeError( "Expected json POST data, didn't get any." )
45
+
46
+ all_allowed_keys = allowed_keys.copy()
47
+ for kw in minmax_keys:
48
+ for edge in [ 'min', 'max' ]:
49
+ all_allowed_keys.add( f"{kw}_{edge}" )
50
+
51
+ data = flask.request.json
52
+ passed_keys = set( data.keys() )
53
+ if not needed_keys.issubset( passed_keys ):
54
+ raise RuntimeError( f"Missing required keys: {needed_keys - passed_keys}" )
55
+ if not passed_keys.issubset( all_allowed_keys ):
56
+ raise RuntimeError( f"Unknown keys: {passed_keys - all_allowed_keys}" )
57
+ return data
58
+
59
+ def get_provenance_id( self, data, dbcon=None ):
60
+ with db.DBCon( dictcursor=True ) as dbcon:
61
+ provid = None
62
+ if 'provenance' in data:
63
+ provid = data['provenance']
64
+ del data['provenance']
65
+ elif ( 'provenance_tag' in data ) and ( 'process' in data ):
66
+ rows = dbcon.execute( "SELECT provenance_id FROM provenance_tag "
67
+ "WHERE tag=%(tag)s AND process=%(proc)s",
68
+ {'tag': data['provenance_tag'], 'proc': data['process'] } )
69
+ if len(rows) == 0:
70
+ raise RuntimeError( f"Unknown provenance with tag {data['provenance_tag']} "
71
+ f"and process {data['process']}" )
72
+ if len(rows) > 1:
73
+ raise RuntimeError ( f"Database corruption: multiple provenances with tag {data['provenance_tag']}"
74
+ f"and process {data['process']}; this should never happen." )
75
+ provid = db.Provenance.get( rows[0]['provenance_id'] ).id
76
+ del data['provenance_tag']
77
+ del data['process']
78
+ else:
79
+ raise RuntimeError( "Must give either provenance_id, or both of provenance_tag and process" )
80
+
81
+ return data, provid
82
+
83
+
84
+ # Warning : only call this next one if you've made sure that there is no SQL injection in keys!
85
+ def build_sql_insert( self, keys ):
86
+ keysql = None
87
+ subs = None
88
+ for key in keys:
89
+ if keysql is None:
90
+ keysql = sql.SQL( "{key}" ).format( key=sql.Identifier( key ) )
91
+ else:
92
+ keysql += sql.SQL( ", {key}" ).format( key=sql.Identifier( key ) )
93
+ if subs is None:
94
+ subs = f"%({key})s"
95
+ else:
96
+ subs += f", %({key})s"
97
+ return keysql, subs
98
+
99
+
100
+ # WARNING. Do not use this unless you're sure that everything in
101
+ # the passed arrays have no SQL injection attacks.
102
+ def make_sql_conditions( self, data, equalses=[], minmaxes=[],
103
+ q3ctriplets=[], cornerpolypairs=[],
104
+ conditions=[], subdict={} ):
105
+ finalclause = None
106
+
107
+ if any( i in data for i in [ 'order_by', 'limit', 'offset' ] ):
108
+ orderbyclause = None
109
+ limitclause = None
110
+ offsetclause = None
111
+
112
+ if 'order_by' in data:
113
+ orderby = data['order_by']
114
+ if not isinstance( orderby, list ):
115
+ orderby = [ orderby ]
116
+ orderbyclause = sql.SQL( " ORDER BY " )
117
+ comma = ""
118
+ for o in orderby:
119
+ orderbyclause += sql.SQL( f"{comma}{{orderby}}" ).format( orderby=sql.Identifier(o) )
120
+ comma = ","
121
+ del data['order_by']
122
+ finalclause = orderbyclause
123
+
124
+ if 'limit' in data:
125
+ limitclause = sql.SQL( " LIMIT %(finalclause_limit)s" )
126
+ subdict['finalclause_limit'] = data['limit']
127
+ del data['limit']
128
+ if finalclause is None:
129
+ finalclause = limitclause
130
+ else:
131
+ finalclause += limitclause
132
+
133
+ if 'offset' in data:
134
+ offsetclause = sql.SQL( " OFFSET %(finalclause_offset)s" )
135
+ subdict['finalclause_offset'] = data['offset']
136
+ del data['offset']
137
+ if finalclause is None:
138
+ finalclause = offsetclause
139
+ else:
140
+ finalclause += offsetclause
141
+
142
+ finalclause = sql.SQL("") if finalclause is None else finalclause
143
+
144
+ for i, triplet in enumerate(q3ctriplets):
145
+ presencecheck = [ ( i in data and data[i] is not None ) for i in triplet ]
146
+ if any( presencecheck ):
147
+ if not all( presencecheck ):
148
+ raise RuntimeError( f"Must include all or none of {triplet}" )
149
+ barf = f"q3c_triplet_{i}"
150
+ formatsubs = { f"{barf}_0": sql.Identifier( triplet[0] ),
151
+ f"{barf}_1": sql.Identifier( triplet[1] ) }
152
+ conditions.append( sql.SQL( f"q3c_radial_query({{{barf}_0}},{{{barf}_1}},"
153
+ f"%({barf}_0)s,%({barf}_1)s,%({barf}_2)s)"
154
+ ).format( **formatsubs ) )
155
+ subdict.update( { f"{barf}_0": data[triplet[0]],
156
+ f"{barf}_1": data[triplet[1]],
157
+ f"{barf}_2": data[triplet[2]] / 3600. } )
158
+ del data[triplet[0]]
159
+ del data[triplet[1]]
160
+ del data[triplet[2]]
161
+
162
+ for i, pair in enumerate(cornerpolypairs):
163
+ presencecheck = [ ( i in data and data[i] is not None ) for i in pair ]
164
+ if any( presencecheck ):
165
+ if not all( presencecheck ):
166
+ raise RuntimeError( f"Must include both or neither of {pair}" )
167
+ barf = f"corner_pair_{i}"
168
+ # THINKING AHEAD : this poly query doesn't use the q3c index
169
+ # As the number of database rows get large, we should look
170
+ # at performance. We many need to do this in two steps,
171
+ # which would mean using a temp table. First step would use
172
+ # regular indexes on the eight corner variables and use
173
+ # LEAST and GREATEST with ra and dec. Then, a second query
174
+ # would use the poly query on the temp table resulting from
175
+ # that first query. (Or maybe you can do it all with clever
176
+ # nested queries.) This becomes very complicated.
177
+ conditions.append( sql.SQL( f"q3c_poly_query(%({barf}_0)s, %({barf}_1)s, "
178
+ f"ARRAY[ ra_corner_00, dec_corner_00, ra_corner_01, dec_corner_01, "
179
+ f" ra_corner_11, dec_corner_11, ra_corner_10, dec_corner_10 ] )"
180
+ ) )
181
+ subdict.update( { f"{barf}_0": data[pair[0]],
182
+ f"{barf}_1": data[pair[1]] } )
183
+ del data[pair[0]]
184
+ del data[pair[1]]
185
+
186
+ for kw in equalses:
187
+ if ( kw in data ) and ( data[kw] is not None ):
188
+ conditions.append( sql.SQL( f"{kw}=%({kw})s" ) )
189
+ # I hate that we're doing str here, but we have a legacy of diaobject names
190
+ # being either strings or integers
191
+ subdict[kw] = str( data[kw] )
192
+ del data[kw]
193
+
194
+ for kw in minmaxes:
195
+ if kw in data:
196
+ if data['kw'] is not None:
197
+ conditions.append( sql.SQL( f"{kw}=%({kw})s" ) )
198
+ subdict[kw] = data[kw]
199
+ del data[kw]
200
+ for edge, op in zip( [ 'min', 'max' ], [ '>=', '<=' ] ):
201
+ if f'{kw}_{edge}' in data:
202
+ if data[f'{kw}_{edge}'] is not None:
203
+ conditions.append( sql.SQL( f"{kw} {op} %({kw}_{edge})s" ) )
204
+ subdict[f'{kw}_{edge}'] = data[f'{kw}_{edge}']
205
+ del data[f'{kw}_{edge}']
206
+
207
+ _and = None
208
+ for condition in conditions:
209
+ if _and is None:
210
+ condstr = condition
211
+ else:
212
+ _and = " AND "
213
+ condstr += sql.SQL(_and) + condition
214
+
215
+ return data, condstr, subdict, finalclause
216
+
217
+
218
+ def check_auth( self ):
219
+ self.username = flask.session['username'] if 'username' in flask.session else '(None)'
220
+ self.displayname = flask.session['userdisplayname'] if 'userdisplayname' in flask.session else '(None)'
221
+ self.authenticated = ( 'authenticated' in flask.session ) and flask.session['authenticated']
222
+ self.user = None
223
+ if self.authenticated:
224
+ with db.DB() as conn:
225
+ cursor = conn.cursor()
226
+ cursor.execute( "SELECT id,username,displayname,email FROM authuser WHERE username=%(username)s",
227
+ {'username': self.username } )
228
+ rows = cursor.fetchall()
229
+ if len(rows) > 1:
230
+ self.authenticated = False
231
+ raise RuntimeError( f"Error, more than one {self.username} in database, "
232
+ f"this should never happen." )
233
+ if len(rows) == 0:
234
+ self.authenticated = False
235
+ raise ValueError( f"Error, failed to find user {self.username} in database" )
236
+ row = rows[0]
237
+ self.user = SimpleNamespace( id=row[0], username=row[1], displayname=row[2], email=row[3] )
238
+ # Verify that session displayname and database displayname match? Eh. Whatevs.
239
+ return self.authenticated
240
+
241
+ def dispatch_request( self, *args, **kwargs ):
242
+ if not self.check_auth():
243
+ return "Not logged in", 500
244
+ if ( self._admin_required ) and ( not self.user.isadmin ):
245
+ return "Action requires admin", 500
246
+ try:
247
+ retval = self.do_the_things( *args, **kwargs )
248
+ # Can't just use the default JSON handling, because it
249
+ # writes out NaN which is not standard JSON and which
250
+ # the javascript JSON parser chokes on. Sigh.
251
+ if isinstance( retval, dict ) or isinstance( retval, list ):
252
+ # SNLogger.warning( f"Dumping to json: {retval}" )
253
+ return ( simplejson.dumps( retval, ignore_nan=True, cls=SNPITJsonEncoder ),
254
+ 200, { 'Content-Type': 'application/json' } )
255
+ elif isinstance( retval, str ):
256
+ return retval, 200, { 'Content-Type': 'text/plain; charset=utf-8' }
257
+ elif isinstance( retval, tuple ):
258
+ return retval
259
+ else:
260
+ return retval, 200, { 'Content-Type': 'application/octet-stream' }
261
+ except Exception as ex:
262
+ # sio = io.StringIO()
263
+ # traceback.print_exc( file=sio )
264
+ # SNLogger.debug( sio.getvalue() )
265
+ SNLogger.exception( str(ex) )
266
+ return str(ex), 500
@@ -13,6 +13,7 @@ from contextlib import contextmanager
13
13
 
14
14
  import numpy as np
15
15
  import psycopg
16
+ from psycopg import sql
16
17
 
17
18
  from snappl.config import Config
18
19
  from snappl.logger import SNLogger
@@ -266,7 +267,8 @@ class DBCon:
266
267
 
267
268
  """
268
269
  if self.echoqueries and not silent:
269
- SNLogger.debug( f"Sending query\n{q}\nwith substitutions: {subdict}" )
270
+ qprint = q.as_string() if isinstance( q, sql.Composable ) else q
271
+ SNLogger.debug( f"Sending query\n{qprint}\nwith substitutions: {subdict}" )
270
272
 
271
273
  if self.alwaysexplain and not silent:
272
274
  self.cursor.execute( f"EXPLAIN {q}", subdict )
@@ -1074,6 +1076,14 @@ class SummedImage( DBBase ):
1074
1076
  _pk = [ 'id' ]
1075
1077
 
1076
1078
 
1079
+ # ======================================================================
1080
+
1081
+ class SegMap( DBBase ):
1082
+ __tablename__ = "segmap"
1083
+ _tablemeta = None
1084
+ _pk = [ 'id' ]
1085
+
1086
+
1077
1087
  # ======================================================================
1078
1088
 
1079
1089
  class Lightcurve( DBBase ):
@@ -0,0 +1,45 @@
1
+ CREATE TABLE segmap(
2
+ id UUID PRIMARY KEY,
3
+ provenance_id UUID NOT NULL,
4
+ band text NOT NULL,
5
+ ra double precision NOT NULL,
6
+ dec double precision NOT NULL,
7
+ ra_corner_00 real NOT NULL,
8
+ ra_corner_01 real NOT NULL,
9
+ ra_corner_10 real NOT NULL,
10
+ ra_corner_11 real NOT NULL,
11
+ dec_corner_00 real NOT NULL,
12
+ dec_corner_01 real NOT NULL,
13
+ dec_corner_10 real NOT NULL,
14
+ dec_corner_11 real NOT NULL,
15
+ filepath text NOT NULL,
16
+ width smallint,
17
+ height smallint,
18
+ format smallint NOT NULL DEFAULT 0,
19
+ l2image_id UUID DEFAULT NULL
20
+ );
21
+ CREATE INDEX ix_segmap_band ON segmap (band);
22
+ CREATE INDEX ix_segmap_q3c ON segmap (q3c_ang2ipix(ra,dec));
23
+ CREATE INDEX ix_segmap_ra00 ON segmap (ra_corner_00);
24
+ CREATE INDEX ix_segmap_ra01 ON segmap (ra_corner_01);
25
+ CREATE INDEX ix_segmap_ra10 ON segmap (ra_corner_10);
26
+ CREATE INDEX ix_segmap_ra11 ON segmap (ra_corner_11);
27
+ CREATE INDEX ix_segmap_dec00 ON segmap (dec_corner_00);
28
+ CREATE INDEX ix_segmap_dec01 ON segmap (dec_corner_01);
29
+ CREATE INDEX ix_segmap_dec10 ON segmap (dec_corner_10);
30
+ CREATE INDEX ix_segmap_dec11 ON segmap (dec_corner_11);
31
+ CREATE UNIQUE INDEX ix_segmap_filepath ON segmap (filepath);
32
+ CREATE INDEX iX_segmap_l2image ON segmap (l2image_id);
33
+ ALTER TABLE segmap ADD CONSTRAINT fk_segmap_l2image
34
+ FOREIGN KEY(l2image_id) REFERENCES l2image(id) ON DELETE RESTRICT;
35
+ COMMENT ON TABLE segmap IS 'Segmentation maps';
36
+ COMMENT ON COLUMN segmap.ra_corner_00 IS 'RA of pixel (0,0)';
37
+ COMMENT ON COLUMN segmap.ra_corner_01 IS 'RA of pixel (0,height-1)';
38
+ COMMENT ON COLUMN segmap.ra_corner_10 IS 'RA of pixel (width-1,0)';
39
+ COMMENT ON COLUMN segmap.ra_corner_11 IS 'RA of pixel (width-1,height-1)';
40
+ COMMENT ON COLUMN segmap.dec_corner_00 IS 'Dec of pixel (0,0)';
41
+ COMMENT ON COLUMN segmap.dec_corner_01 IS 'Dec of pixel (0,height-1)';
42
+ COMMENT ON COLUMN segmap.dec_corner_10 IS 'Dec of pixel (width-1,0)';
43
+ COMMENT ON COLUMN segmap.dec_corner_11 IS 'Dec of pixel (width-1,height-1)';
44
+ COMMENT ON COLUMN segmap.l2image_id IS 'This segmentation map is for this image';
45
+ COMMENT ON COLUMN segmap.format IS '0=Unknown, 1=OpenUniverse2024FITSImage';