pyobs-core 2.0.0.dev86__tar.gz → 2.0.0.dev88__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (449) hide show
  1. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/PKG-INFO +1 -1
  2. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/backend/observationarchive.py +34 -14
  3. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/backend/taskarchive.py +33 -11
  4. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/task.py +4 -0
  5. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/archive/archive.py +2 -0
  6. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/archive/local_archive.py +9 -2
  7. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/archive/pyobs_archive.py +7 -2
  8. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/http.py +16 -2
  9. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyproject.toml +1 -1
  10. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/.gitignore +0 -0
  11. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/LICENSE +0 -0
  12. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/__init__.py +0 -0
  13. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/application.py +0 -0
  14. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/background_task.py +0 -0
  15. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/cli/__init__.py +0 -0
  16. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/cli/_cli.py +0 -0
  17. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/cli/pyobs.py +0 -0
  18. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/cli/pyobsd.py +0 -0
  19. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/cli/pyobsw.py +0 -0
  20. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/__init__.py +0 -0
  21. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/comm.py +0 -0
  22. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/commlogging.py +0 -0
  23. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/dummy/__init__.py +0 -0
  24. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/dummy/dummycomm.py +0 -0
  25. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/local/__init__.py +0 -0
  26. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/local/localcomm.py +0 -0
  27. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/local/localnetwork.py +0 -0
  28. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/proxy.py +0 -0
  29. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/__init__.py +0 -0
  30. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/rpc.py +0 -0
  31. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/serializer.py +0 -0
  32. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xep_0009/__init__.py +0 -0
  33. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xep_0009/binding.py +0 -0
  34. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xep_0009/rpc.py +0 -0
  35. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xep_0009_timeout/__init__.py +0 -0
  36. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xep_0009_timeout/rpc.py +0 -0
  37. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xep_0009_timeout/stanza/RPC.py +0 -0
  38. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xep_0009_timeout/stanza/__init__.py +0 -0
  39. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xmppclient.py +0 -0
  40. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/comm/xmpp/xmppcomm.py +0 -0
  41. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/__init__.py +0 -0
  42. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/badweather.py +0 -0
  43. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/event.py +0 -0
  44. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/exposurestatuschanged.py +0 -0
  45. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/filterchanged.py +0 -0
  46. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/focusfound.py +0 -0
  47. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/goodweather.py +0 -0
  48. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/log.py +0 -0
  49. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/modechanged.py +0 -0
  50. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/moduleclosed.py +0 -0
  51. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/moduleopened.py +0 -0
  52. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/motionstatuschanged.py +0 -0
  53. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/move.py +0 -0
  54. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/newimage.py +0 -0
  55. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/newspectrum.py +0 -0
  56. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/offsets.py +0 -0
  57. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/roofclosing.py +0 -0
  58. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/roofopened.py +0 -0
  59. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/taskfailed.py +0 -0
  60. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/taskfinished.py +0 -0
  61. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/taskstarted.py +0 -0
  62. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/events/testevent.py +0 -0
  63. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/__init__.py +0 -0
  64. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/image.py +0 -0
  65. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/__init__.py +0 -0
  66. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/altazoffsets.py +0 -0
  67. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/exptime.py +0 -0
  68. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/genericoffset.py +0 -0
  69. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/onskydistance.py +0 -0
  70. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/pixeloffsets.py +0 -0
  71. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/radecoffsets.py +0 -0
  72. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/meta/skyoffsets.py +0 -0
  73. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processor.py +0 -0
  74. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/__init__.py +0 -0
  75. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/_daobackgroundremover.py +0 -0
  76. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/annotation/__init__.py +0 -0
  77. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/annotation/_pillow.py +0 -0
  78. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/annotation/circle.py +0 -0
  79. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/annotation/crosshair.py +0 -0
  80. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/annotation/text.py +0 -0
  81. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/astrometry/__init__.py +0 -0
  82. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/astrometry/_dotnet_request.py +0 -0
  83. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/astrometry/_dotnet_request_builder.py +0 -0
  84. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/astrometry/_dotnet_request_logger.py +0 -0
  85. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/astrometry/_dotnet_response_saver.py +0 -0
  86. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/astrometry/astrometry.py +0 -0
  87. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/astrometry/dotnet.py +0 -0
  88. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/calibration/__init__.py +0 -0
  89. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/calibration/_calibration_cache.py +0 -0
  90. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/calibration/_ccddata_calibrator.py +0 -0
  91. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/calibration/calibration.py +0 -0
  92. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/detection/__init__.py +0 -0
  93. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/detection/_pysep_stats_calculator.py +0 -0
  94. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/detection/_source_catalog.py +0 -0
  95. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/detection/daophot.py +0 -0
  96. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/detection/pysep.py +0 -0
  97. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/detection/simpledisk.py +0 -0
  98. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/detection/sourcedetection.py +0 -0
  99. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/exptime/__init__.py +0 -0
  100. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/exptime/exptime.py +0 -0
  101. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/exptime/star.py +0 -0
  102. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/__init__.py +0 -0
  103. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/addfitsheaders.py +0 -0
  104. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/download.py +0 -0
  105. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/flip.py +0 -0
  106. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/grayscale.py +0 -0
  107. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/httpserver.py +0 -0
  108. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/normalize.py +0 -0
  109. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/save.py +0 -0
  110. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/saveimage.py +0 -0
  111. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/smooth.py +0 -0
  112. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/image/softbin.py +0 -0
  113. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/misc/__init__.py +0 -0
  114. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/misc/addmask.py +0 -0
  115. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/misc/catalogcircularmask.py +0 -0
  116. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/misc/circularmask.py +0 -0
  117. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/misc/createfilename.py +0 -0
  118. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/misc/imagesourcefilter.py +0 -0
  119. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/misc/removebackground.py +0 -0
  120. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/modules/__init__.py +0 -0
  121. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/modules/getfitsheaders.py +0 -0
  122. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/__init__.py +0 -0
  123. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/astrometry.py +0 -0
  124. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/brighteststar.py +0 -0
  125. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/brighteststar_guiding.py +0 -0
  126. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/dummyoffsets.py +0 -0
  127. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/dummyskyoffsets.py +0 -0
  128. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/fitsheader.py +0 -0
  129. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/offsets.py +0 -0
  130. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/pixeloffsets.py +0 -0
  131. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/projected.py +0 -0
  132. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/offsets/spilled_light.py +0 -0
  133. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/__init__.py +0 -0
  134. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/_photometry_calculator.py +0 -0
  135. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/_photutil_aperture_photometry.py +0 -0
  136. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/_sep_aperture_photometry.py +0 -0
  137. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/aperture_photometry.py +0 -0
  138. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/photometry.py +0 -0
  139. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/photutil.py +0 -0
  140. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/photometry/pysep.py +0 -0
  141. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/wcs/__init__.py +0 -0
  142. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/images/processors/wcs/solarhelioprojective.py +0 -0
  143. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IAbortable.py +0 -0
  144. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IAcquisition.py +0 -0
  145. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IAutoFocus.py +0 -0
  146. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IAutoGuiding.py +0 -0
  147. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IAutonomous.py +0 -0
  148. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IBinning.py +0 -0
  149. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ICalibrate.py +0 -0
  150. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ICamera.py +0 -0
  151. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IConfig.py +0 -0
  152. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ICooling.py +0 -0
  153. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IData.py +0 -0
  154. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IDataSequence.py +0 -0
  155. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IDome.py +0 -0
  156. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IExposure.py +0 -0
  157. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IExposureTime.py +0 -0
  158. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IFilters.py +0 -0
  159. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IFitsHeaderAfter.py +0 -0
  160. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IFitsHeaderBefore.py +0 -0
  161. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IFlatField.py +0 -0
  162. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IFocusModel.py +0 -0
  163. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IFocuser.py +0 -0
  164. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IGain.py +0 -0
  165. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IImageFormat.py +0 -0
  166. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IImageType.py +0 -0
  167. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IMode.py +0 -0
  168. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IModule.py +0 -0
  169. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IMotion.py +0 -0
  170. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IMultiFiber.py +0 -0
  171. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IOffsetsAltAz.py +0 -0
  172. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IOffsetsRaDec.py +0 -0
  173. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingAltAz.py +0 -0
  174. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingBody.py +0 -0
  175. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingHeliocentricPolar.py +0 -0
  176. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingHeliographicStonyhurst.py +0 -0
  177. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingHelioprojective.py +0 -0
  178. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingOrbitalElements.py +0 -0
  179. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingRaDec.py +0 -0
  180. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IPointingSeries.py +0 -0
  181. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IReady.py +0 -0
  182. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IRoof.py +0 -0
  183. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IRotation.py +0 -0
  184. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IRunnable.py +0 -0
  185. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IRunning.py +0 -0
  186. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IScriptRunner.py +0 -0
  187. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ISpectrograph.py +0 -0
  188. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IStartStop.py +0 -0
  189. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IStructuredConfig.py +0 -0
  190. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ISyncTarget.py +0 -0
  191. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ITelescope.py +0 -0
  192. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ITemperatures.py +0 -0
  193. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ITrackingMode.py +0 -0
  194. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/ITrackingRate.py +0 -0
  195. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IVideo.py +0 -0
  196. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IWeather.py +0 -0
  197. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/IWindow.py +0 -0
  198. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/__init__.py +0 -0
  199. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/interfaces/interface.py +0 -0
  200. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/__init__.py +0 -0
  201. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/camerasettings.py +0 -0
  202. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/fitsheader.py +0 -0
  203. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/fitsnamespace.py +0 -0
  204. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/follow.py +0 -0
  205. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/motionstatus.py +0 -0
  206. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/pipeline.py +0 -0
  207. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/waitformotion.py +0 -0
  208. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/mixins/weatheraware.py +0 -0
  209. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/__init__.py +0 -0
  210. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/__init__.py +0 -0
  211. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/adaptive.py +0 -0
  212. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/basecamera.py +0 -0
  213. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/basespectrograph.py +0 -0
  214. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/basevideo.py +0 -0
  215. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/dummycamera.py +0 -0
  216. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/dummyspectrograph.py +0 -0
  217. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/dummyvideo.py +0 -0
  218. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/camera/pipelinecamera.py +0 -0
  219. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/flatfield/__init__.py +0 -0
  220. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/flatfield/flatfield.py +0 -0
  221. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/flatfield/pointing.py +0 -0
  222. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/flatfield/scheduler.py +0 -0
  223. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/focus/__init__.py +0 -0
  224. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/focus/dummyautofocus.py +0 -0
  225. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/focus/focusmodel.py +0 -0
  226. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/focus/focusseries.py +0 -0
  227. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/image/__init__.py +0 -0
  228. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/image/imagewatcher.py +0 -0
  229. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/image/imagewriter.py +0 -0
  230. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/image/pipeline.py +0 -0
  231. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/image/seeing.py +0 -0
  232. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/module.py +0 -0
  233. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/__init__.py +0 -0
  234. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/_base.py +0 -0
  235. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/_baseguiding.py +0 -0
  236. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/acquisition.py +0 -0
  237. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/autoguiding.py +0 -0
  238. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/dummyacquisition.py +0 -0
  239. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/dummyguiding.py +0 -0
  240. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/guidingstatistics/__init__.py +0 -0
  241. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/guidingstatistics/guidingstatistics.py +0 -0
  242. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/guidingstatistics/pixeloffset.py +0 -0
  243. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/guidingstatistics/skyoffsets.py +0 -0
  244. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/guidingstatistics/uptime.py +0 -0
  245. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/pointing/scienceframeguiding.py +0 -0
  246. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/robotic/__init__.py +0 -0
  247. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/robotic/mastermind.py +0 -0
  248. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/robotic/pointing.py +0 -0
  249. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/robotic/scheduler.py +0 -0
  250. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/robotic/scriptrunner.py +0 -0
  251. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/roof/__init__.py +0 -0
  252. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/roof/basedome.py +0 -0
  253. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/roof/baseroof.py +0 -0
  254. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/roof/dummyroof.py +0 -0
  255. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/telescope/__init__.py +0 -0
  256. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/telescope/_dummytelescopebase.py +0 -0
  257. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/telescope/basetelescope.py +0 -0
  258. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/telescope/dummyaltaztelescope.py +0 -0
  259. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/telescope/dummyradectelescope.py +0 -0
  260. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/telescope/dummysolartelescope.py +0 -0
  261. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/test/__init__.py +0 -0
  262. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/test/standalone.py +0 -0
  263. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/__init__.py +0 -0
  264. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/dummymode.py +0 -0
  265. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/fluentlogger.py +0 -0
  266. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/httpfilecache.py +0 -0
  267. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/kiosk.py +0 -0
  268. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/matrix.py +0 -0
  269. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/stellarium.py +0 -0
  270. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/telegram.py +0 -0
  271. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/utils/trigger.py +0 -0
  272. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/weather/__init__.py +0 -0
  273. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/weather/mockweather.py +0 -0
  274. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/weather/weather.py +0 -0
  275. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/weather/weather_api.py +0 -0
  276. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/modules/weather/weather_state.py +0 -0
  277. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/object.py +0 -0
  278. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/py.typed +0 -0
  279. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/__init__.py +0 -0
  280. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/observation.py +0 -0
  281. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/__init__.py +0 -0
  282. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/_executor.py +0 -0
  283. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/astroplanscheduler.py +0 -0
  284. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/constraints/__init__.py +0 -0
  285. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/constraints/airmassconstraint.py +0 -0
  286. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/constraints/constraint.py +0 -0
  287. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/constraints/moonilluminationconstraint.py +0 -0
  288. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/constraints/moonseparationconstraint.py +0 -0
  289. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/constraints/solarelevationconstraint.py +0 -0
  290. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/constraints/timeconstraint.py +0 -0
  291. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/dataprovider.py +0 -0
  292. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/__init__.py +0 -0
  293. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/aftertime.py +0 -0
  294. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/beforetime.py +0 -0
  295. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/constant.py +0 -0
  296. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/follow.py +0 -0
  297. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/interval.py +0 -0
  298. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/merit.py +0 -0
  299. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/pernight.py +0 -0
  300. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/random.py +0 -0
  301. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/timewindow.py +0 -0
  302. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/merits/transit.py +0 -0
  303. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/observationarchiveevolution.py +0 -0
  304. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/ondemandscheduler.py +0 -0
  305. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/__init__.py +0 -0
  306. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/dynamictarget.py +0 -0
  307. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/heliocentricpolartarget.py +0 -0
  308. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/helioprojectivetarget.py +0 -0
  309. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/picker/__init__.py +0 -0
  310. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/picker/csvpicker.py +0 -0
  311. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/picker/picker.py +0 -0
  312. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/siderealtarget.py +0 -0
  313. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/targets/target.py +0 -0
  314. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scheduler/taskscheduler.py +0 -0
  315. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/__init__.py +0 -0
  316. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/calibration/__init__.py +0 -0
  317. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/calibration/darkbias.py +0 -0
  318. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/calibration/pointing.py +0 -0
  319. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/calibration/skyflats.py +0 -0
  320. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/control/__init__.py +0 -0
  321. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/control/cases.py +0 -0
  322. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/control/conditional.py +0 -0
  323. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/control/parallel.py +0 -0
  324. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/control/selector.py +0 -0
  325. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/control/sequential.py +0 -0
  326. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/exceptions.py +0 -0
  327. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/imaging/__init__.py +0 -0
  328. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/imaging/autofocus.py +0 -0
  329. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/imaging/imaging.py +0 -0
  330. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/imaging/transitimaging.py +0 -0
  331. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/script.py +0 -0
  332. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/utils/__init__.py +0 -0
  333. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/utils/callmodule.py +0 -0
  334. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/utils/debugtrigger.py +0 -0
  335. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/scripts/utils/log.py +0 -0
  336. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/__init__.py +0 -0
  337. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/backend/__init__.py +0 -0
  338. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/filesystem/__init__.py +0 -0
  339. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/filesystem/observationarchive.py +0 -0
  340. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/filesystem/taskarchive.py +0 -0
  341. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/__init__.py +0 -0
  342. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/_portal.py +0 -0
  343. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/_schedulereader.py +0 -0
  344. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/_schedulewriter.py +0 -0
  345. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/configdb.py +0 -0
  346. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/mockobservationarchive.py +0 -0
  347. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/observationarchive.py +0 -0
  348. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/scripts/__init__.py +0 -0
  349. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/scripts/autofocus.py +0 -0
  350. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/scripts/default.py +0 -0
  351. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/scripts/script.py +0 -0
  352. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/task.py +0 -0
  353. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/taskarchive.py +0 -0
  354. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/lco/taskrunner.py +0 -0
  355. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/memory/__init__.py +0 -0
  356. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/memory/observationarchive.py +0 -0
  357. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/memory/taskarchive.py +0 -0
  358. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/observationarchive.py +0 -0
  359. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/storage/taskarchive.py +0 -0
  360. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/taskrunner.py +0 -0
  361. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/__init__.py +0 -0
  362. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/archive/__init__.py +0 -0
  363. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/exptime/__init__.py +0 -0
  364. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/exptime/exptime.py +0 -0
  365. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/exptime/stellarexptime.py +0 -0
  366. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/__init__.py +0 -0
  367. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/exptimeeval.py +0 -0
  368. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/flatfielder.py +0 -0
  369. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/pointing/__init__.py +0 -0
  370. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/pointing/base.py +0 -0
  371. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/pointing/static.py +0 -0
  372. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/priorities/__init__.py +0 -0
  373. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/priorities/archive.py +0 -0
  374. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/priorities/base.py +0 -0
  375. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/priorities/const.py +0 -0
  376. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/robotic/utils/skyflats/scheduler.py +0 -0
  377. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/__init__.py +0 -0
  378. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/average.py +0 -0
  379. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/cache.py +0 -0
  380. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/config.py +0 -0
  381. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/config_schema.py +0 -0
  382. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/coordinates.py +0 -0
  383. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/curvefit.py +0 -0
  384. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/enums.py +0 -0
  385. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/exceptions.py +0 -0
  386. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/fits.py +0 -0
  387. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/focusseries/__init__.py +0 -0
  388. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/focusseries/base.py +0 -0
  389. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/focusseries/photometry.py +0 -0
  390. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/focusseries/projection.py +0 -0
  391. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/grids/__init__.py +0 -0
  392. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/grids/filters.py +0 -0
  393. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/grids/grid.py +0 -0
  394. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/grids/gridnode.py +0 -0
  395. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/grids/pipeline.py +0 -0
  396. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/__init__.py +0 -0
  397. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/__init__.py +0 -0
  398. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/binningwidget.py +0 -0
  399. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/datadisplaywidget.py +0 -0
  400. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/exposewidget.py +0 -0
  401. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/exposuretimewidget.py +0 -0
  402. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/imageformatwidget.py +0 -0
  403. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/listpickerdialog.py +0 -0
  404. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/gui/camera/windowingwidget.py +0 -0
  405. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/influxdb.py +0 -0
  406. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/logger.py +0 -0
  407. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/logging/__init__.py +0 -0
  408. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/logging/context.py +0 -0
  409. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/logging/resolvableerror.py +0 -0
  410. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/modulegui.py +0 -0
  411. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/offsets/__init__.py +0 -0
  412. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/offsets/applyaltazoffsets.py +0 -0
  413. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/offsets/applyoffsets.py +0 -0
  414. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/offsets/applyradecoffsets.py +0 -0
  415. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/parallel.py +0 -0
  416. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/pipeline/__init__.py +0 -0
  417. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/pipeline/pipeline.py +0 -0
  418. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/pipeline/progress.py +0 -0
  419. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/pipeline/reduction.py +0 -0
  420. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/pipeline/reduction_base.py +0 -0
  421. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/publisher/__init__.py +0 -0
  422. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/publisher/csv.py +0 -0
  423. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/publisher/http.py +0 -0
  424. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/publisher/log.py +0 -0
  425. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/publisher/multi.py +0 -0
  426. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/publisher/publisher.py +0 -0
  427. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/serialization.py +0 -0
  428. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/shellcommand.py +0 -0
  429. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/threads/__init__.py +0 -0
  430. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/threads/lockwithabort.py +0 -0
  431. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/time.py +0 -0
  432. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/units.py +0 -0
  433. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/utils/versions.py +0 -0
  434. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/version.py +0 -0
  435. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/__init__.py +0 -0
  436. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/archivefile.py +0 -0
  437. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/bufferedfile.py +0 -0
  438. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/file.py +0 -0
  439. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/filelists/__init__.py +0 -0
  440. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/filelists/filelist.py +0 -0
  441. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/filelists/testing.py +0 -0
  442. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/httpfile.py +0 -0
  443. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/localfile.py +0 -0
  444. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/memfile.py +0 -0
  445. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/sftpfile.py +0 -0
  446. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/smbfile.py +0 -0
  447. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/sshfile.py +0 -0
  448. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/tempfile.py +0 -0
  449. {pyobs_core-2.0.0.dev86 → pyobs_core-2.0.0.dev88}/pyobs/vfs/vfs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyobs-core
3
- Version: 2.0.0.dev86
3
+ Version: 2.0.0.dev88
4
4
  Summary: robotic telescope software
5
5
  Author-email: Tim-Oliver Husser <thusser@uni-goettingen.de>
6
6
  License-Expression: MIT
@@ -60,23 +60,43 @@ class BackendObservationArchive(ObservationArchive):
60
60
 
61
61
  while True:
62
62
  try:
63
- last_update = await self.last_update_time()
64
- if self._last_update is None or self._last_update < last_update:
65
- self._observations = await self._get_schedule()
66
- sorted_obs = sorted(
67
- filter(lambda o: o.state == ObservationState.PENDING, self._observations),
68
- key=lambda o: o.start,
69
- )
70
- if len(sorted_obs) == 0:
71
- log.info("Downloaded new schedule.")
72
- else:
73
- obs = sorted_obs[0]
74
- log.info("Downloaded new schedule. Next observation is task %s at %s.", obs.task, obs.start)
75
- self._last_update = last_update
63
+ await self._update()
76
64
  except Exception as e:
77
65
  log.error("Failed to update observations from backend: %s", e)
78
66
  await asyncio.sleep(5)
79
67
 
68
+ async def _update(self) -> None:
69
+ """Fetch the schedule from the backend and apply it if anything changed.
70
+
71
+ Re-fetches unconditionally on every poll instead of gating on the backend's
72
+ ``last_observation_update`` marker (per-process Django ``LocMemCache``, unreliable across
73
+ gunicorn workers -- see ``BackendTaskArchive``). Changes are detected by comparing full
74
+ observation contents via ``model_dump(use_task_id=True)``, keyed by observation ID so a
75
+ stable reordering of the same items is not mistaken for a change: the backend serializes
76
+ ``task`` as a plain FK ID, while cached copies get their ``task`` replaced by a full
77
+ ``Task`` when the mastermind calls ``fetch_task()``, so the ID normalization keeps both
78
+ sides comparable. The comparison includes ``state`` (``Observation.__eq__`` ignores it),
79
+ which matters for in-set transitions such as ``pending`` -> ``in_progress``; observations
80
+ leaving the fetched set -- e.g. ``window_expired``, which the server-side ``state``/
81
+ ``end_after`` filters drop -- are picked up as list shrinkage by the unconditional
82
+ refetch.
83
+ """
84
+ observations = await self._get_schedule()
85
+ if {o.id: o.model_dump(use_task_id=True) for o in observations} != {
86
+ o.id: o.model_dump(use_task_id=True) for o in self._observations
87
+ }:
88
+ self._observations = observations
89
+ self._last_update = Time.now()
90
+ sorted_obs = sorted(
91
+ filter(lambda o: o.state == ObservationState.PENDING, self._observations),
92
+ key=lambda o: o.start,
93
+ )
94
+ if len(sorted_obs) == 0:
95
+ log.info("Downloaded new schedule.")
96
+ else:
97
+ obs = sorted_obs[0]
98
+ log.info("Downloaded new schedule. Next observation is task %s at %s.", obs.task, obs.start)
99
+
80
100
  async def last_update_time(self) -> Time:
81
101
  """Fetches last schedule update time."""
82
102
  res = await http_request_with_retries(self._session, urljoin(self._url, "/api/last_observation_update/"))
@@ -229,7 +249,7 @@ class BackendObservationArchive(ObservationArchive):
229
249
  params["end_before"] = end_before.isot
230
250
  if end_after is not None:
231
251
  params["end_after"] = end_after.isot
232
- observations = await http_request_paginated(self._session, url, params=params)
252
+ observations = await http_request_paginated(self._session, url, params=params, strict=True)
233
253
  return ObservationList([self.pyobs_model_validate(Observation, obs) for obs in observations])
234
254
 
235
255
 
@@ -56,18 +56,36 @@ class BackendTaskArchive(TaskArchive):
56
56
  """Update tasks in background."""
57
57
  while True:
58
58
  try:
59
- last_update = await self.last_update_time()
60
- if self._last_update is None or self._last_update < last_update:
61
- self._projects = await self._get_projects()
62
- self._tasks = await self._get_tasks()
63
- log.info("Downloaded new tasks/projects.")
64
- self._last_update = last_update
65
- if self._on_tasks_changed is not None:
66
- await self._on_tasks_changed()
59
+ await self._update()
67
60
  except Exception as e:
68
61
  log.error("Failed to update tasks from backend: %s", e)
69
62
  await asyncio.sleep(5)
70
63
 
64
+ async def _update(self) -> None:
65
+ """Fetch tasks/projects from the backend and apply them if anything changed.
66
+
67
+ Re-fetches unconditionally on every poll instead of gating on the backend's
68
+ ``last_task_update`` marker: that marker is computed from a per-process Django
69
+ ``LocMemCache`` and is unreliable across gunicorn workers, so gating on it let the
70
+ mastermind run stale tasks forever. Real changes are detected by comparing the downloaded
71
+ content against the cached copy. The comparison uses ``model_dump()`` rather than pydantic
72
+ ``==``, which also compares runtime attributes (e.g. ``Task._cant_run_reason`` set by
73
+ ``can_run()``) and would flag unchanged tasks as changed on every poll; it is keyed by ID
74
+ so that a stable reordering of the same items (e.g. an unordered backend queryset) is not
75
+ mistaken for a change.
76
+ """
77
+ projects = await self._get_projects()
78
+ tasks = await self._get_tasks()
79
+ if {p.code: p.model_dump() for p in projects} != {p.code: p.model_dump() for p in self._projects} or {
80
+ t.id: t.model_dump() for t in tasks
81
+ } != {t.id: t.model_dump() for t in self._tasks}:
82
+ self._projects = projects
83
+ self._tasks = tasks
84
+ self._last_update = Time.now()
85
+ log.info("Downloaded new tasks/projects.")
86
+ if self._on_tasks_changed is not None:
87
+ await self._on_tasks_changed()
88
+
71
89
  async def last_update_time(self) -> Time:
72
90
  """Fetches last schedule update time."""
73
91
  res = await http_request_with_retries(self._session, urljoin(self._url, "/api/last_task_update/"))
@@ -75,16 +93,20 @@ class BackendTaskArchive(TaskArchive):
75
93
 
76
94
  async def _get_projects(self) -> list[Project]:
77
95
  """Fetch projects from backend."""
78
- projects = await http_request_paginated(self._session, urljoin(self._url, "/api/projects/"))
96
+ projects = await http_request_paginated(self._session, urljoin(self._url, "/api/projects/"), strict=True)
79
97
  return [self.pyobs_model_validate(Project, project) for project in projects]
80
98
 
81
99
  async def _get_tasks(self) -> list[Task]:
82
100
  """Fetch tasks from backend."""
83
- tasks = await http_request_paginated(self._session, urljoin(self._url, "/api/tasks/"))
101
+ tasks = await http_request_paginated(self._session, urljoin(self._url, "/api/tasks/"), strict=True)
84
102
  return [self.pyobs_model_validate(Task, task) for task in tasks]
85
103
 
86
104
  async def last_changed(self) -> Time | None:
87
- """Returns time when last time any tasks changed."""
105
+ """Returns time when last time any tasks changed (as observed by this archive).
106
+
107
+ This is the local time at which the last content change was detected by the polling loop,
108
+ not the backend's marker timestamp -- the marker is per-process and unreliable.
109
+ """
88
110
  return self._last_update
89
111
 
90
112
  async def get_projects(self) -> list[Project]:
@@ -45,6 +45,10 @@ class Task(PolymorphicBaseModel):
45
45
  static_target: Target | None = Field(default=None, alias="target")
46
46
  script: dict[str, Any] = Field(default_factory=dict)
47
47
  active: bool = True
48
+ updated_at: str | None = None
49
+ """Last-modification timestamp as emitted by pyobs-robotic-backend (DB-derived ``updated_at``,
50
+ added in pyobs-robotic-backend#84). Carried for round-trip fidelity so the strict ``Task``
51
+ model (``extra="forbid"``) accepts the field the backend now sends; not used by pyobs itself."""
48
52
 
49
53
  _resolved_target: Target | None = PrivateAttr(default=None)
50
54
  _cant_run_reason: str | None = None
@@ -42,6 +42,7 @@ class Archive(PolymorphicBaseModel, metaclass=ABCMeta):
42
42
  binning: str | None = None,
43
43
  filter_name: str | None = None,
44
44
  rlevel: int | None = None,
45
+ obsnum: str | None = None,
45
46
  ) -> dict[str, list[Any]]: ...
46
47
 
47
48
  @abstractmethod
@@ -57,6 +58,7 @@ class Archive(PolymorphicBaseModel, metaclass=ABCMeta):
57
58
  binning: str | None = None,
58
59
  filter_name: str | None = None,
59
60
  rlevel: int | None = None,
61
+ obsnum: str | None = None,
60
62
  ) -> list[FrameInfo]: ...
61
63
 
62
64
  @abstractmethod
@@ -48,6 +48,7 @@ class LocalArchive(Archive):
48
48
  "site",
49
49
  "telescope",
50
50
  "rlevel",
51
+ "obsnum",
51
52
  ]
52
53
  }
53
54
  for filename in filenames:
@@ -61,6 +62,7 @@ class LocalArchive(Archive):
61
62
  columns["site"].append(hdr["SITEID"] if "SITEID" in hdr else None)
62
63
  columns["telescope"].append(hdr["TELID"] if "TELID" in hdr else None)
63
64
  columns["rlevel"].append(hdr["RLEVEL"] if "RLEVEL" in hdr else None)
65
+ columns["obsnum"].append(str(hdr["OBSNUM"]) if "OBSNUM" in hdr else None)
64
66
  columns["filename"] = filenames
65
67
  self._data = pd.DataFrame(columns)
66
68
 
@@ -76,6 +78,7 @@ class LocalArchive(Archive):
76
78
  binning: str | None = None,
77
79
  filter_name: str | None = None,
78
80
  rlevel: int | None = None,
81
+ obsnum: str | None = None,
79
82
  ) -> pd.DataFrame:
80
83
  data = self._data
81
84
  if start is not None:
@@ -98,6 +101,8 @@ class LocalArchive(Archive):
98
101
  data = data[data["filter"] == filter_name]
99
102
  if rlevel is not None:
100
103
  data = data[data["rlevel"] == rlevel]
104
+ if obsnum is not None:
105
+ data = data[data["obsnum"] == obsnum]
101
106
  return data
102
107
 
103
108
  async def list_options(
@@ -112,9 +117,10 @@ class LocalArchive(Archive):
112
117
  binning: str | None = None,
113
118
  filter_name: str | None = None,
114
119
  rlevel: int | None = None,
120
+ obsnum: str | None = None,
115
121
  ) -> dict[str, list[Any]]:
116
122
  data = self._filter_data(
117
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
123
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
118
124
  )
119
125
  return {
120
126
  "binnings": list(data["binning"].unique()),
@@ -137,9 +143,10 @@ class LocalArchive(Archive):
137
143
  binning: str | None = None,
138
144
  filter_name: str | None = None,
139
145
  rlevel: int | None = None,
146
+ obsnum: str | None = None,
140
147
  ) -> list[FrameInfo]:
141
148
  data = self._filter_data(
142
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
149
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
143
150
  )
144
151
  infos: list[FrameInfo] = []
145
152
  for _, row in data.iterrows():
@@ -68,10 +68,11 @@ class PyobsArchive(Archive):
68
68
  binning: str | None = None,
69
69
  filter_name: str | None = None,
70
70
  rlevel: int | None = None,
71
+ obsnum: str | None = None,
71
72
  ) -> dict[str, list[Any]]:
72
73
  url = urllib.parse.urljoin(self.url, "frames/aggregate/")
73
74
  params = self._build_query(
74
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
75
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
75
76
  )
76
77
  async with aiohttp.ClientSession() as session:
77
78
  async with session.get(url, params=params, headers=self._headers, timeout=self._timeout) as response:
@@ -91,10 +92,11 @@ class PyobsArchive(Archive):
91
92
  binning: str | None = None,
92
93
  filter_name: str | None = None,
93
94
  rlevel: int | None = None,
95
+ obsnum: str | None = None,
94
96
  ) -> list[FrameInfo]:
95
97
  url = urllib.parse.urljoin(self.url, "frames/")
96
98
  params = self._build_query(
97
- start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel
99
+ start, end, night, site, telescope, instrument, image_type, binning, filter_name, rlevel, obsnum=obsnum
98
100
  )
99
101
  frames: list[FrameInfo] = []
100
102
  params["offset"] = 0
@@ -123,6 +125,7 @@ class PyobsArchive(Archive):
123
125
  binning: str | None = None,
124
126
  filter_name: str | None = None,
125
127
  rlevel: int | None = None,
128
+ obsnum: str | None = None,
126
129
  ) -> dict[str, Any]:
127
130
  params: dict[str, Any] = {}
128
131
  if start is not None:
@@ -145,6 +148,8 @@ class PyobsArchive(Archive):
145
148
  params["FILTER"] = filter_name
146
149
  if rlevel is not None:
147
150
  params["RLEVEL"] = rlevel
151
+ if obsnum is not None:
152
+ params["OBSNUM"] = obsnum
148
153
  return params
149
154
 
150
155
  async def download_frames(self, infos: list[FrameInfo]) -> list[Image]:
@@ -46,13 +46,20 @@ async def http_request_with_retries(
46
46
 
47
47
 
48
48
  async def http_request_paginated(
49
- session: aiohttp.ClientSession, url: str, method: str = "get", expected_status: int = 200, **kwargs: Any
49
+ session: aiohttp.ClientSession,
50
+ url: str,
51
+ method: str = "get",
52
+ expected_status: int = 200,
53
+ strict: bool = False,
54
+ **kwargs: Any,
50
55
  ) -> list[dict[str, Any]]:
51
56
  """Fetches all pages of a DRF-style paginated list endpoint and returns the combined results.
52
57
 
53
58
  If a page beyond the first becomes invalid mid-fetch (DRF's "Invalid page." 404, which can
54
59
  happen when concurrent writes shift page boundaries on a live-growing dataset), pagination
55
- stops early with whatever was already fetched instead of failing the whole request.
60
+ stops early with whatever was already fetched instead of failing the whole request. With
61
+ ``strict=True`` that truncation is raised as an error instead, so callers that treat the
62
+ result as authoritative (e.g. replacing a cached list) never apply a partial result silently.
56
63
  """
57
64
  results: list[dict[str, Any]] = []
58
65
  next_url: str | None = url
@@ -69,6 +76,13 @@ async def http_request_paginated(
69
76
  and isinstance(e.body, dict)
70
77
  and e.body.get("detail") == "Invalid page."
71
78
  ):
79
+ if strict:
80
+ log.warning(
81
+ "Pagination page became invalid mid-fetch, raising instead of returning a "
82
+ "truncated result of %d item(s).",
83
+ len(results),
84
+ )
85
+ raise
72
86
  log.warning("Pagination page became invalid mid-fetch, stopping early with %d result(s).", len(results))
73
87
  break
74
88
  raise
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyobs-core"
3
- version = "2.0.0.dev86"
3
+ version = "2.0.0.dev88"
4
4
  description = "robotic telescope software"
5
5
  authors = [{ name = "Tim-Oliver Husser", email = "thusser@uni-goettingen.de" }]
6
6
  requires-python = ">=3.11"