mostlyright-data 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. mostlyright/data_harness/__init__.py +158 -0
  2. mostlyright/data_harness/acquisition/__init__.py +55 -0
  3. mostlyright/data_harness/acquisition/http.py +2773 -0
  4. mostlyright/data_harness/acquisition/parsing.py +809 -0
  5. mostlyright/data_harness/acquisition/ranges.py +495 -0
  6. mostlyright/data_harness/acquisition/result_download.py +360 -0
  7. mostlyright/data_harness/acquisition/retention_admission.py +248 -0
  8. mostlyright/data_harness/acquisition/sandbox.py +4888 -0
  9. mostlyright/data_harness/acquisition/url_policy.py +530 -0
  10. mostlyright/data_harness/agent_runtime.py +2743 -0
  11. mostlyright/data_harness/assets/logo-ink.svg +31 -0
  12. mostlyright/data_harness/backends/__init__.py +28 -0
  13. mostlyright/data_harness/backends/pandas_backend.py +350 -0
  14. mostlyright/data_harness/backends/polars_backend.py +366 -0
  15. mostlyright/data_harness/backends/protocol.py +124 -0
  16. mostlyright/data_harness/backends/reference.py +83 -0
  17. mostlyright/data_harness/backends/registry.py +55 -0
  18. mostlyright/data_harness/backends/restrictions.py +126 -0
  19. mostlyright/data_harness/canonical.py +333 -0
  20. mostlyright/data_harness/catalog_job.py +625 -0
  21. mostlyright/data_harness/cli.py +5398 -0
  22. mostlyright/data_harness/contracts.py +53 -0
  23. mostlyright/data_harness/coordinator.py +1307 -0
  24. mostlyright/data_harness/deploy.py +924 -0
  25. mostlyright/data_harness/deploy_target.py +312 -0
  26. mostlyright/data_harness/deployment_evidence.py +1067 -0
  27. mostlyright/data_harness/event_presentation.py +576 -0
  28. mostlyright/data_harness/events.py +2152 -0
  29. mostlyright/data_harness/fast_delimited.py +239 -0
  30. mostlyright/data_harness/fleet.py +237 -0
  31. mostlyright/data_harness/formats.py +236 -0
  32. mostlyright/data_harness/governors.py +1163 -0
  33. mostlyright/data_harness/hosted_bootstrap.py +972 -0
  34. mostlyright/data_harness/hosted_crawler.py +1115 -0
  35. mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
  36. mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
  37. mostlyright/data_harness/hosted_crawler_job.py +1277 -0
  38. mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
  39. mostlyright/data_harness/hosted_dataset.py +1500 -0
  40. mostlyright/data_harness/hosted_deploy.py +3037 -0
  41. mostlyright/data_harness/hosted_handoff.py +62 -0
  42. mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
  43. mostlyright/data_harness/hosted_ingestion_job.py +356 -0
  44. mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
  45. mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
  46. mostlyright/data_harness/hosted_session_worker.py +3554 -0
  47. mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
  48. mostlyright/data_harness/hosted_worker.py +6784 -0
  49. mostlyright/data_harness/ingestion/__init__.py +56 -0
  50. mostlyright/data_harness/ingestion/contracts.py +461 -0
  51. mostlyright/data_harness/ingestion/faults.py +42 -0
  52. mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
  53. mostlyright/data_harness/ingestion/spool.py +130 -0
  54. mostlyright/data_harness/ingestion/store.py +885 -0
  55. mostlyright/data_harness/key_seam.py +434 -0
  56. mostlyright/data_harness/linux_process_boundary.py +262 -0
  57. mostlyright/data_harness/local_contracts.py +2880 -0
  58. mostlyright/data_harness/local_search/__init__.py +5 -0
  59. mostlyright/data_harness/local_search/build_index.py +1087 -0
  60. mostlyright/data_harness/local_search/contracts.py +920 -0
  61. mostlyright/data_harness/local_search/query_trace.py +266 -0
  62. mostlyright/data_harness/local_search/retrieval.py +700 -0
  63. mostlyright/data_harness/local_search/sealed.py +474 -0
  64. mostlyright/data_harness/local_search/service.py +784 -0
  65. mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
  66. mostlyright/data_harness/nbrender/__init__.py +12 -0
  67. mostlyright/data_harness/nbrender/chrome.py +359 -0
  68. mostlyright/data_harness/nbrender/code_body.py +266 -0
  69. mostlyright/data_harness/nbrender/document.py +407 -0
  70. mostlyright/data_harness/nbrender/frame.py +275 -0
  71. mostlyright/data_harness/nbrender/interactive.py +337 -0
  72. mostlyright/data_harness/nbrender/markdown_body.py +477 -0
  73. mostlyright/data_harness/nbrender/mr_components.py +134 -0
  74. mostlyright/data_harness/nbrender/outputs_data.py +595 -0
  75. mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
  76. mostlyright/data_harness/nbrender/outputs_source.py +260 -0
  77. mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
  78. mostlyright/data_harness/nbrender/outputs_text.py +400 -0
  79. mostlyright/data_harness/nbrender/parse.py +394 -0
  80. mostlyright/data_harness/nbrender/status.py +40 -0
  81. mostlyright/data_harness/nbrender/tokens.py +1295 -0
  82. mostlyright/data_harness/notebook.py +1710 -0
  83. mostlyright/data_harness/offline.py +2049 -0
  84. mostlyright/data_harness/operation_registry.py +1007 -0
  85. mostlyright/data_harness/operator_setup.py +239 -0
  86. mostlyright/data_harness/pipeline.py +6428 -0
  87. mostlyright/data_harness/plan_graph.py +2026 -0
  88. mostlyright/data_harness/preparation/__init__.py +104 -0
  89. mostlyright/data_harness/preparation/contracts.py +1017 -0
  90. mostlyright/data_harness/preparation/engine.py +221 -0
  91. mostlyright/data_harness/preparation/errors.py +14 -0
  92. mostlyright/data_harness/preparation/gates.py +751 -0
  93. mostlyright/data_harness/preparation/joins.py +574 -0
  94. mostlyright/data_harness/preparation/profile.py +384 -0
  95. mostlyright/data_harness/preparation/table.py +217 -0
  96. mostlyright/data_harness/preparation/transforms.py +568 -0
  97. mostlyright/data_harness/progress_events.py +534 -0
  98. mostlyright/data_harness/readers/__init__.py +46 -0
  99. mostlyright/data_harness/readers/containers.py +963 -0
  100. mostlyright/data_harness/readers/contracts.py +542 -0
  101. mostlyright/data_harness/readers/delimited.py +257 -0
  102. mostlyright/data_harness/readers/grib2/__init__.py +33 -0
  103. mostlyright/data_harness/readers/grib2/admission.py +722 -0
  104. mostlyright/data_harness/readers/grib2/decode.py +1009 -0
  105. mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
  106. mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
  107. mostlyright/data_harness/readers/json_tabular.py +485 -0
  108. mostlyright/data_harness/readers/registry.py +514 -0
  109. mostlyright/data_harness/readers/samples/README.md +110 -0
  110. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
  111. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
  112. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
  113. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
  114. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
  115. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
  116. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
  117. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
  118. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
  119. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
  120. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  121. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
  122. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  123. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
  124. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
  125. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
  126. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
  127. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
  128. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
  129. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
  130. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
  131. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
  132. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
  133. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
  134. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
  135. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
  136. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  137. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  138. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  139. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  140. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
  141. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
  142. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
  143. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
  144. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  145. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
  146. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
  147. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
  148. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  149. mostlyright/data_harness/readers/samples.py +582 -0
  150. mostlyright/data_harness/readers/spreadsheet.py +803 -0
  151. mostlyright/data_harness/readers/tabular.py +510 -0
  152. mostlyright/data_harness/recipe.py +5321 -0
  153. mostlyright/data_harness/repair/__init__.py +78 -0
  154. mostlyright/data_harness/repair/adapters.py +274 -0
  155. mostlyright/data_harness/repair/contracts.py +872 -0
  156. mostlyright/data_harness/repair/coordinator.py +1099 -0
  157. mostlyright/data_harness/repair/errors.py +16 -0
  158. mostlyright/data_harness/review.py +2533 -0
  159. mostlyright/data_harness/rowset.py +283 -0
  160. mostlyright/data_harness/serving.py +1975 -0
  161. mostlyright/data_harness/serving_edge.py +590 -0
  162. mostlyright/data_harness/serving_http.py +1031 -0
  163. mostlyright/data_harness/session_probes.py +759 -0
  164. mostlyright/data_harness/signing.py +101 -0
  165. mostlyright/data_harness/source_discovery.py +898 -0
  166. mostlyright/data_harness/sources/__init__.py +209 -0
  167. mostlyright/data_harness/sources/_adapter_steps.py +213 -0
  168. mostlyright/data_harness/sources/adapters.py +1214 -0
  169. mostlyright/data_harness/sources/cadence.py +1428 -0
  170. mostlyright/data_harness/sources/cadence_emission.py +453 -0
  171. mostlyright/data_harness/sources/cadence_history.py +546 -0
  172. mostlyright/data_harness/sources/catalog/__init__.py +17 -0
  173. mostlyright/data_harness/sources/catalog/admission.py +477 -0
  174. mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
  175. mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
  176. mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
  177. mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
  178. mostlyright/data_harness/sources/catalog/channel.py +523 -0
  179. mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
  180. mostlyright/data_harness/sources/catalog/contracts.py +825 -0
  181. mostlyright/data_harness/sources/catalog/coverage.py +137 -0
  182. mostlyright/data_harness/sources/catalog/delta.py +1340 -0
  183. mostlyright/data_harness/sources/catalog/embedding.py +532 -0
  184. mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
  185. mostlyright/data_harness/sources/catalog/fill.py +3889 -0
  186. mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
  187. mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
  188. mostlyright/data_harness/sources/catalog/gating.py +374 -0
  189. mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
  190. mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
  191. mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
  192. mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
  193. mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
  194. mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
  195. mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
  196. mostlyright/data_harness/sources/catalog/health.py +447 -0
  197. mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
  198. mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
  199. mostlyright/data_harness/sources/catalog/neural.py +1618 -0
  200. mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
  201. mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
  202. mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
  203. mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
  204. mostlyright/data_harness/sources/catalog/recommend.py +171 -0
  205. mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
  206. mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
  207. mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
  208. mostlyright/data_harness/sources/catalog/sealed.py +560 -0
  209. mostlyright/data_harness/sources/catalog/search.py +230 -0
  210. mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
  211. mostlyright/data_harness/sources/catalog/update.py +891 -0
  212. mostlyright/data_harness/sources/collections.py +815 -0
  213. mostlyright/data_harness/sources/contracts.py +2223 -0
  214. mostlyright/data_harness/sources/deletion.py +761 -0
  215. mostlyright/data_harness/sources/fitness.py +162 -0
  216. mostlyright/data_harness/sources/governance.py +163 -0
  217. mostlyright/data_harness/sources/hosted.py +173 -0
  218. mostlyright/data_harness/sources/integration.py +218 -0
  219. mostlyright/data_harness/sources/range_reader.py +418 -0
  220. mostlyright/data_harness/sources/registry.py +514 -0
  221. mostlyright/data_harness/sources/rights_rule.py +59 -0
  222. mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
  223. mostlyright/data_harness/sources/sports.py +521 -0
  224. mostlyright/data_harness/sources/stream.py +524 -0
  225. mostlyright/data_harness/sources/stream_connector.py +418 -0
  226. mostlyright/data_harness/sources/stream_recorder.py +1404 -0
  227. mostlyright/data_harness/studio_boundary.py +2019 -0
  228. mostlyright/data_harness/thin/__init__.py +37 -0
  229. mostlyright/data_harness/thin/acquire.py +1137 -0
  230. mostlyright/data_harness/thin/acquire_cancel.py +579 -0
  231. mostlyright/data_harness/thin/approvals.py +617 -0
  232. mostlyright/data_harness/thin/commands.py +406 -0
  233. mostlyright/data_harness/thin/download.py +194 -0
  234. mostlyright/data_harness/thin/narrative.py +589 -0
  235. mostlyright/data_harness/thin/parity.py +1070 -0
  236. mostlyright/data_harness/thin/propose.py +2759 -0
  237. mostlyright/data_harness/thin/research.py +1663 -0
  238. mostlyright/data_harness/thin/router.py +924 -0
  239. mostlyright/data_harness/thin/runs.py +519 -0
  240. mostlyright/data_harness/thin/session.py +281 -0
  241. mostlyright/data_harness/thin/stream.py +501 -0
  242. mostlyright/data_harness/thin/transport.py +187 -0
  243. mostlyright/data_harness/thin/vocabulary.py +368 -0
  244. mostlyright/data_harness/thin/workers.py +164 -0
  245. mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
  246. mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
  247. mostlyright/data_harness/unit_flow.py +927 -0
  248. mostlyright/data_harness/units.py +572 -0
  249. mostlyright/data_harness/ux/__init__.py +9 -0
  250. mostlyright/data_harness/ux/approve.py +485 -0
  251. mostlyright/data_harness/ux/author_yaml.py +597 -0
  252. mostlyright/data_harness/ux/cloud_auth.py +447 -0
  253. mostlyright/data_harness/ux/commands/__init__.py +260 -0
  254. mostlyright/data_harness/ux/commands/approve.py +136 -0
  255. mostlyright/data_harness/ux/commands/auth.py +744 -0
  256. mostlyright/data_harness/ux/commands/author.py +79 -0
  257. mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
  258. mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
  259. mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
  260. mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
  261. mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
  262. mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
  263. mostlyright/data_harness/ux/commands/deploy.py +134 -0
  264. mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
  265. mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
  266. mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
  267. mostlyright/data_harness/ux/commands/diff.py +74 -0
  268. mostlyright/data_harness/ux/commands/index.py +84 -0
  269. mostlyright/data_harness/ux/commands/inventory.py +47 -0
  270. mostlyright/data_harness/ux/commands/list_builds.py +143 -0
  271. mostlyright/data_harness/ux/commands/login.py +63 -0
  272. mostlyright/data_harness/ux/commands/peek.py +236 -0
  273. mostlyright/data_harness/ux/commands/plan_check.py +90 -0
  274. mostlyright/data_harness/ux/commands/preflight.py +97 -0
  275. mostlyright/data_harness/ux/commands/record.py +107 -0
  276. mostlyright/data_harness/ux/commands/review_setup.py +47 -0
  277. mostlyright/data_harness/ux/commands/search.py +440 -0
  278. mostlyright/data_harness/ux/commands/show.py +61 -0
  279. mostlyright/data_harness/ux/commands/whoami.py +37 -0
  280. mostlyright/data_harness/ux/credential_native.py +551 -0
  281. mostlyright/data_harness/ux/credential_store.py +1055 -0
  282. mostlyright/data_harness/ux/credentials.py +631 -0
  283. mostlyright/data_harness/ux/diffing.py +444 -0
  284. mostlyright/data_harness/ux/headline.py +671 -0
  285. mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
  286. mostlyright/data_harness/ux/hosted_run_status.py +619 -0
  287. mostlyright/data_harness/ux/inventory.py +427 -0
  288. mostlyright/data_harness/ux/local_review.py +375 -0
  289. mostlyright/data_harness/ux/login.py +691 -0
  290. mostlyright/data_harness/ux/path_kind.py +147 -0
  291. mostlyright/data_harness/ux/peek.py +1000 -0
  292. mostlyright/data_harness/ux/plain_file.py +178 -0
  293. mostlyright/data_harness/ux/plan_check.py +311 -0
  294. mostlyright/data_harness/ux/preflight.py +918 -0
  295. mostlyright/data_harness/ux/readers.py +1124 -0
  296. mostlyright/data_harness/ux/remediation.py +2195 -0
  297. mostlyright/data_harness/ux/render.py +657 -0
  298. mostlyright/data_harness/ux/workload.py +1077 -0
  299. mostlyright/data_harness/viewer.py +3713 -0
  300. mostlyright/data_harness/visual_run/__init__.py +83 -0
  301. mostlyright/data_harness/visual_run/authoring.py +235 -0
  302. mostlyright/data_harness/visual_run/contracts.py +673 -0
  303. mostlyright/data_harness/visual_run/materialize.py +486 -0
  304. mostlyright/data_harness/visual_run/observations.py +874 -0
  305. mostlyright/data_harness/visual_run/query.py +259 -0
  306. mostlyright/data_harness/visual_run/reducer.py +280 -0
  307. mostlyright/data_harness/visual_run/sdk.py +892 -0
  308. mostlyright/data_harness/visual_run/store.py +584 -0
  309. mostlyright/data_harness/visual_run/transport.py +239 -0
  310. mostlyright/data_harness/watch.py +2999 -0
  311. mostlyright_data-0.9.0.dist-info/METADATA +607 -0
  312. mostlyright_data-0.9.0.dist-info/RECORD +314 -0
  313. mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
  314. mostlyright_data-0.9.0.dist-info/entry_points.txt +12 -0
@@ -0,0 +1,521 @@
1
+ """Recorded sports-source comparison and OpenLigaDB governance policy."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from mostlyright.data_harness.acquisition.sandbox import CrawlerSandbox
10
+ from mostlyright.data_harness.canonical import canonical_sha256, parse_json, sha256_bytes
11
+ from mostlyright.data_harness.local_contracts import (
12
+ EvidenceReference as ProposalEvidence,
13
+ )
14
+ from mostlyright.data_harness.local_contracts import SourceProposal, TimeRange
15
+ from mostlyright.data_harness.sources.adapters import (
16
+ AdapterGovernance,
17
+ PublicHttpsAdapter,
18
+ )
19
+ from mostlyright.data_harness.sources.contracts import (
20
+ FITNESS_DIMENSIONS,
21
+ AdapterDescriptor,
22
+ EvidenceReference,
23
+ LicenseObligation,
24
+ RightsEvidence,
25
+ SourceContractError,
26
+ SourceObservation,
27
+ evaluate_obligations,
28
+ )
29
+ from mostlyright.data_harness.sources.fitness import (
30
+ FitnessSignals,
31
+ build_fitness_report,
32
+ select_one_source,
33
+ )
34
+
35
+ OPENLIGADB_ORIGIN = "https://openligadb.de/"
36
+ OPENLIGADB_API_HOST = "api.openligadb.de"
37
+ STATSBOMB_ORIGIN = "https://github.com/statsbomb/open-data"
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class SportsComparison:
42
+ """Digests of both recorded sports fitness reports and of the one selected source."""
43
+
44
+ openligadb_report_digest: str
45
+ statsbomb_report_digest: str
46
+ selected_source_id: str
47
+ selected_report_digest: str
48
+ selected_observation_digest: str
49
+ selected_rights_digest: str
50
+ evidence_manifest_sha256: str
51
+ selected_sample_sha256: str
52
+
53
+
54
+ _PACKAGE_TOKEN = object()
55
+ _LIFECYCLE_TRANSITIONS = {
56
+ "candidate": frozenset({"derivative"}),
57
+ "derivative": frozenset({"redistribution", "publication"}),
58
+ "redistribution": frozenset({"publication"}),
59
+ "publication": frozenset(),
60
+ }
61
+
62
+
63
+ @dataclass(frozen=True, init=False)
64
+ class GovernedSportsPackage:
65
+ """Only lifecycle object allowed to advance ODbL-governed source material."""
66
+
67
+ lifecycle: str
68
+ rights_digest: str
69
+ intended_use: str
70
+ attached_artifact_digests: tuple[str, ...]
71
+ predecessor_digest: str | None
72
+ package_digest: str
73
+ _rights: RightsEvidence
74
+
75
+ def __init__(
76
+ self,
77
+ *,
78
+ lifecycle: str,
79
+ rights: RightsEvidence,
80
+ intended_use: str,
81
+ attached_artifact_digests: tuple[str, ...],
82
+ predecessor_digest: str | None,
83
+ _token: object,
84
+ ) -> None:
85
+ if _token is not _PACKAGE_TOKEN:
86
+ raise SourceContractError(
87
+ "GOVERNED_PACKAGE_FACTORY",
88
+ "package",
89
+ "governed source packages must use the controlled lifecycle factory",
90
+ )
91
+ evaluate_obligations(
92
+ rights=rights,
93
+ lifecycle=lifecycle,
94
+ intended_use=intended_use,
95
+ attached_artifact_digests=attached_artifact_digests,
96
+ )
97
+ payload = {
98
+ "schema_version": "governed-sports-package.v1",
99
+ "lifecycle": lifecycle,
100
+ "rights_digest": rights.digest,
101
+ "intended_use": intended_use,
102
+ "attached_artifact_digests": list(attached_artifact_digests),
103
+ "predecessor_digest": predecessor_digest,
104
+ }
105
+ object.__setattr__(self, "lifecycle", lifecycle)
106
+ object.__setattr__(self, "rights_digest", rights.digest)
107
+ object.__setattr__(self, "intended_use", intended_use)
108
+ object.__setattr__(self, "attached_artifact_digests", attached_artifact_digests)
109
+ object.__setattr__(self, "predecessor_digest", predecessor_digest)
110
+ object.__setattr__(self, "package_digest", canonical_sha256(payload))
111
+ object.__setattr__(self, "_rights", rights)
112
+
113
+ def advance(
114
+ self,
115
+ *,
116
+ lifecycle: str,
117
+ intended_use: str,
118
+ attached_artifact_digests: tuple[str, ...],
119
+ ) -> GovernedSportsPackage:
120
+ if lifecycle not in _LIFECYCLE_TRANSITIONS[self.lifecycle]:
121
+ raise SourceContractError(
122
+ "LIFECYCLE_TRANSITION",
123
+ "package.lifecycle",
124
+ f"cannot advance {self.lifecycle} to {lifecycle}",
125
+ )
126
+ return GovernedSportsPackage(
127
+ lifecycle=lifecycle,
128
+ rights=self._rights,
129
+ intended_use=intended_use,
130
+ attached_artifact_digests=attached_artifact_digests,
131
+ predecessor_digest=self.package_digest,
132
+ _token=_PACKAGE_TOKEN,
133
+ )
134
+
135
+
136
+ def create_governed_sports_package(
137
+ *,
138
+ rights: RightsEvidence,
139
+ intended_use: str,
140
+ attached_artifact_digests: tuple[str, ...],
141
+ ) -> GovernedSportsPackage:
142
+ """Create a candidate only after worker-owned candidate obligation evaluation."""
143
+
144
+ return GovernedSportsPackage(
145
+ lifecycle="candidate",
146
+ rights=rights,
147
+ intended_use=intended_use,
148
+ attached_artifact_digests=attached_artifact_digests,
149
+ predecessor_digest=None,
150
+ _token=_PACKAGE_TOKEN,
151
+ )
152
+
153
+
154
+ def openligadb_descriptor() -> AdapterDescriptor:
155
+ return AdapterDescriptor(
156
+ adapter_id="external.openligadb",
157
+ adapter_version="1.0.0",
158
+ source_class="external_adapter",
159
+ data_formats=("json",),
160
+ capabilities=("snapshot", "historical", "live", "refresh"),
161
+ )
162
+
163
+
164
+ def openligadb_adapter(
165
+ *,
166
+ sandbox: CrawlerSandbox,
167
+ governance: AdapterGovernance,
168
+ ) -> PublicHttpsAdapter:
169
+ """Return the source-specific adapter over the generic pinned HTTPS boundary."""
170
+
171
+ return PublicHttpsAdapter(
172
+ sandbox=sandbox,
173
+ allowed_hostnames=(OPENLIGADB_API_HOST,),
174
+ governance=governance,
175
+ descriptor=openligadb_descriptor(),
176
+ )
177
+
178
+
179
+ def openligadb_rights(
180
+ *,
181
+ evidence: EvidenceReference,
182
+ attribution_digest: str,
183
+ notice_digest: str,
184
+ share_alike_digest: str,
185
+ downstream_access_digest: str,
186
+ ) -> RightsEvidence:
187
+ """Encode ODbL conditions as lifecycle-specific deterministic gates."""
188
+
189
+ all_stages = ("candidate", "derivative", "redistribution", "publication")
190
+ public_stages = ("derivative", "redistribution", "publication")
191
+ redistribution_stages = ("redistribution", "publication")
192
+ return RightsEvidence(
193
+ status="conditional",
194
+ lawful_basis="Open Database License 1.0 as recorded by the source project",
195
+ allowed_uses=("analysis", "redistribute_open_database"),
196
+ evidence=(evidence,),
197
+ obligations=(
198
+ LicenseObligation(
199
+ obligation_id="odbl.attribution",
200
+ applies_at=all_stages,
201
+ permitted_uses=("analysis", "redistribute_open_database"),
202
+ prohibited_uses=("proprietary_relicense",),
203
+ required_artifact_digests=(attribution_digest,),
204
+ escalation_when_ambiguous=True,
205
+ ),
206
+ LicenseObligation(
207
+ obligation_id="odbl.notice",
208
+ applies_at=all_stages,
209
+ permitted_uses=("analysis", "redistribute_open_database"),
210
+ prohibited_uses=("proprietary_relicense",),
211
+ required_artifact_digests=(notice_digest,),
212
+ escalation_when_ambiguous=True,
213
+ ),
214
+ LicenseObligation(
215
+ obligation_id="odbl.share-alike",
216
+ applies_at=public_stages,
217
+ permitted_uses=("analysis", "redistribute_open_database"),
218
+ prohibited_uses=("proprietary_relicense",),
219
+ required_artifact_digests=(share_alike_digest,),
220
+ escalation_when_ambiguous=True,
221
+ ),
222
+ LicenseObligation(
223
+ obligation_id="odbl.downstream-access",
224
+ applies_at=redistribution_stages,
225
+ permitted_uses=("redistribute_open_database",),
226
+ prohibited_uses=("proprietary_relicense",),
227
+ required_artifact_digests=(downstream_access_digest,),
228
+ escalation_when_ambiguous=True,
229
+ ),
230
+ ),
231
+ )
232
+
233
+
234
+ def compare_recorded_sports_sources(
235
+ *,
236
+ fixture_root: Path,
237
+ observed_at: str,
238
+ intended_use: str,
239
+ openligadb_rights_evidence: RightsEvidence,
240
+ statsbomb_rights_evidence: RightsEvidence,
241
+ openligadb_attached_artifact_digests: tuple[str, ...],
242
+ statsbomb_attached_artifact_digests: tuple[str, ...],
243
+ ) -> SportsComparison:
244
+ """Compare recorded liveness/rights evidence and deterministically select one source.
245
+
246
+ ``fixture_root`` must hold ``source-evidence-manifest.json`` and every file that manifest
247
+ names. The manifest declares schema version ``recorded-source-evidence.v1`` and a
248
+ ``captured_at`` equal to ``observed_at``. Its ``sources`` list holds exactly two records, one
249
+ with source id ``openligadb-matches`` and one with ``statsbomb-open-data``. Each record names
250
+ a documentation file and an optional sample file that sit directly in ``fixture_root`` and
251
+ hash to the digests it records. The checked-in fixture is ``tests/fixtures/h3``; it ships with
252
+ the tests, not with the package.
253
+ """
254
+
255
+ manifest_path = fixture_root / "source-evidence-manifest.json"
256
+ manifest_raw = manifest_path.read_bytes()
257
+ manifest = _recorded_evidence_manifest(manifest_raw, fixture_root, observed_at)
258
+ sources = {item["source_id"]: item for item in manifest["sources"]}
259
+ openliga_record = sources["openligadb-matches"]
260
+ statsbomb_record = sources["statsbomb-open-data"]
261
+ openliga_file = fixture_root / openliga_record["documentation_path"]
262
+ statsbomb_file = fixture_root / statsbomb_record["documentation_path"]
263
+ openliga_digest = sha256_bytes(openliga_file.read_bytes())
264
+ statsbomb_digest = sha256_bytes(statsbomb_file.read_bytes())
265
+ openliga_evidence = EvidenceReference(
266
+ uri=OPENLIGADB_ORIGIN,
267
+ observed_at=observed_at,
268
+ content_sha256=openliga_digest,
269
+ media_type="text/plain",
270
+ )
271
+ statsbomb_evidence = EvidenceReference(
272
+ uri=STATSBOMB_ORIGIN,
273
+ observed_at=observed_at,
274
+ content_sha256=statsbomb_digest,
275
+ media_type="text/plain",
276
+ )
277
+ openliga_proposal = _proposal(
278
+ source_id="openligadb-matches",
279
+ proposal_id="proposal-openligadb",
280
+ display_name="OpenLigaDB current match results",
281
+ locator="https://api.openligadb.de/getmatchdata/bl1/2026",
282
+ observed_at=observed_at,
283
+ delay_seconds=120,
284
+ live_endpoint="live",
285
+ fitness_decision="selected",
286
+ evidence=openliga_evidence,
287
+ )
288
+ statsbomb_proposal = _proposal(
289
+ source_id="statsbomb-open-data",
290
+ proposal_id="proposal-statsbomb",
291
+ display_name="StatsBomb historical open data",
292
+ locator=STATSBOMB_ORIGIN,
293
+ observed_at=observed_at,
294
+ delay_seconds=86_400,
295
+ live_endpoint="delayed",
296
+ fitness_decision="rejected",
297
+ evidence=statsbomb_evidence,
298
+ )
299
+ openliga_observation = _observation(
300
+ source_id=openliga_proposal.source_id,
301
+ observed_at=observed_at,
302
+ live_status="live",
303
+ delay_seconds=120,
304
+ evidence=openliga_evidence,
305
+ )
306
+ statsbomb_observation = _observation(
307
+ source_id=statsbomb_proposal.source_id,
308
+ observed_at=observed_at,
309
+ live_status="delayed",
310
+ delay_seconds=86_400,
311
+ evidence=statsbomb_evidence,
312
+ )
313
+ openliga_report = build_fitness_report(
314
+ report_id="fitness.openligadb",
315
+ question_id="question-timely-match-results",
316
+ proposal=openliga_proposal,
317
+ observation=openliga_observation,
318
+ rights=openligadb_rights_evidence,
319
+ intended_use=intended_use,
320
+ attached_license_artifact_digests=openligadb_attached_artifact_digests,
321
+ signals=_passing_signals(openliga_evidence),
322
+ require_timely=True,
323
+ max_publication_delay_seconds=600,
324
+ )
325
+ statsbomb_report = build_fitness_report(
326
+ report_id="fitness.statsbomb",
327
+ question_id="question-timely-match-results",
328
+ proposal=statsbomb_proposal,
329
+ observation=statsbomb_observation,
330
+ rights=statsbomb_rights_evidence,
331
+ intended_use=intended_use,
332
+ attached_license_artifact_digests=statsbomb_attached_artifact_digests,
333
+ signals=_passing_signals(statsbomb_evidence),
334
+ require_timely=True,
335
+ max_publication_delay_seconds=600,
336
+ )
337
+ selected = select_one_source((openliga_report, statsbomb_report))
338
+ return SportsComparison(
339
+ openligadb_report_digest=openliga_report.digest,
340
+ statsbomb_report_digest=statsbomb_report.digest,
341
+ selected_source_id=selected.source_id,
342
+ selected_report_digest=selected.digest,
343
+ selected_observation_digest=selected.observation_digest,
344
+ selected_rights_digest=selected.rights_digest,
345
+ evidence_manifest_sha256=sha256_bytes(manifest_raw),
346
+ selected_sample_sha256=openliga_record["sample_sha256"],
347
+ )
348
+
349
+
350
+ def _recorded_evidence_manifest(
351
+ raw: bytes,
352
+ fixture_root: Path,
353
+ observed_at: str,
354
+ ) -> dict[str, Any]:
355
+ value = parse_json(raw)
356
+ if not isinstance(value, dict) or set(value) != {
357
+ "schema_version",
358
+ "captured_at",
359
+ "sources",
360
+ }:
361
+ raise SourceContractError(
362
+ "RECORDED_EVIDENCE_MANIFEST",
363
+ "manifest",
364
+ "recorded evidence manifest fields are invalid",
365
+ )
366
+ if (
367
+ value["schema_version"] != "recorded-source-evidence.v1"
368
+ or value["captured_at"] != observed_at
369
+ or not isinstance(value["sources"], list)
370
+ or len(value["sources"]) != 2
371
+ ):
372
+ raise SourceContractError(
373
+ "RECORDED_EVIDENCE_MANIFEST",
374
+ "manifest",
375
+ "recorded evidence manifest identity or capture time is invalid",
376
+ )
377
+ expected_fields = {
378
+ "source_id",
379
+ "documentation_uri",
380
+ "documentation_path",
381
+ "documentation_sha256",
382
+ "sample_uri",
383
+ "sample_path",
384
+ "sample_sha256",
385
+ "recorded_response_status",
386
+ "recorded_media_type",
387
+ "rights_status",
388
+ "license",
389
+ "permitted_uses",
390
+ "required_controls",
391
+ }
392
+ records: dict[str, dict[str, Any]] = {}
393
+ for item in value["sources"]:
394
+ if not isinstance(item, dict) or set(item) != expected_fields:
395
+ raise SourceContractError(
396
+ "RECORDED_EVIDENCE_MANIFEST",
397
+ "manifest.sources",
398
+ "recorded source evidence fields are invalid",
399
+ )
400
+ source_id = item["source_id"]
401
+ if source_id in records or source_id not in {
402
+ "openligadb-matches",
403
+ "statsbomb-open-data",
404
+ }:
405
+ raise SourceContractError(
406
+ "RECORDED_EVIDENCE_MANIFEST",
407
+ "manifest.sources.source_id",
408
+ "recorded source identity is duplicate or unexpected",
409
+ )
410
+ documentation_path = item["documentation_path"]
411
+ if (
412
+ not isinstance(documentation_path, str)
413
+ or Path(documentation_path).name != documentation_path
414
+ or sha256_bytes((fixture_root / documentation_path).read_bytes())
415
+ != item["documentation_sha256"]
416
+ ):
417
+ raise SourceContractError(
418
+ "RECORDED_EVIDENCE_DIGEST",
419
+ "manifest.sources.documentation_sha256",
420
+ "recorded documentation bytes do not match the manifest",
421
+ )
422
+ sample_path = item["sample_path"]
423
+ if sample_path is None:
424
+ if item["sample_sha256"] is not None:
425
+ raise SourceContractError(
426
+ "RECORDED_EVIDENCE_DIGEST",
427
+ "manifest.sources.sample_sha256",
428
+ "absent sample must not claim a digest",
429
+ )
430
+ elif (
431
+ not isinstance(sample_path, str)
432
+ or Path(sample_path).name != sample_path
433
+ or sha256_bytes((fixture_root / sample_path).read_bytes()) != item["sample_sha256"]
434
+ ):
435
+ raise SourceContractError(
436
+ "RECORDED_EVIDENCE_DIGEST",
437
+ "manifest.sources.sample_sha256",
438
+ "recorded sample bytes do not match the manifest",
439
+ )
440
+ records[source_id] = item
441
+ if set(records) != {"openligadb-matches", "statsbomb-open-data"}:
442
+ raise SourceContractError(
443
+ "RECORDED_EVIDENCE_MANIFEST",
444
+ "manifest.sources",
445
+ "both required sports evidence records are required",
446
+ )
447
+ return value
448
+
449
+
450
+ def _proposal(
451
+ *,
452
+ source_id: str,
453
+ proposal_id: str,
454
+ display_name: str,
455
+ locator: str,
456
+ observed_at: str,
457
+ delay_seconds: int,
458
+ live_endpoint: str,
459
+ fitness_decision: str,
460
+ evidence: EvidenceReference,
461
+ ) -> SourceProposal:
462
+ return SourceProposal(
463
+ proposal_id=proposal_id,
464
+ question_id="question-timely-match-results",
465
+ source_id=source_id,
466
+ source_class="external_adapter",
467
+ display_name=display_name,
468
+ locator_kind="https_url",
469
+ locator=locator,
470
+ data_format="json",
471
+ observed_at=observed_at,
472
+ historical_coverage=TimeRange(
473
+ start_inclusive="2018-08-01T00:00:00Z",
474
+ end_exclusive="2026-07-18T00:00:00Z",
475
+ ),
476
+ live_endpoint=live_endpoint,
477
+ publication_delay_seconds=delay_seconds,
478
+ rights_status="conditional",
479
+ fitness_decision=fitness_decision,
480
+ evidence=(
481
+ ProposalEvidence(
482
+ uri=evidence.uri,
483
+ observed_at=evidence.observed_at,
484
+ content_sha256=evidence.content_sha256,
485
+ ),
486
+ ),
487
+ )
488
+
489
+
490
+ def _observation(
491
+ *,
492
+ source_id: str,
493
+ observed_at: str,
494
+ live_status: str,
495
+ delay_seconds: int,
496
+ evidence: EvidenceReference,
497
+ ) -> SourceObservation:
498
+ return SourceObservation(
499
+ source_id=source_id,
500
+ observed_at=observed_at,
501
+ event_time_field="match_time",
502
+ available_at_field="available_at",
503
+ historical_start="2018-08-01T00:00:00Z",
504
+ historical_end="2026-07-17T00:00:00Z",
505
+ live_status=live_status,
506
+ publication_delay_seconds=delay_seconds,
507
+ update_frequency_seconds=300 if live_status == "live" else 86_400,
508
+ response_status=200,
509
+ evidence=(evidence,),
510
+ )
511
+
512
+
513
+ def _passing_signals(evidence: EvidenceReference) -> FitnessSignals:
514
+ return FitnessSignals(
515
+ outcomes=tuple((dimension, True) for dimension in sorted(FITNESS_DIMENSIONS)),
516
+ summaries=tuple(
517
+ (dimension, f"recorded evidence supports {dimension}")
518
+ for dimension in sorted(FITNESS_DIMENSIONS)
519
+ ),
520
+ evidence=(evidence,),
521
+ )