shareddata 6.86.0__tar.gz → 6.94.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shareddata might be problematic. Click here for more details.

Files changed (215) hide show
  1. shareddata-6.94.0/PKG-INFO +164 -0
  2. shareddata-6.94.0/README.md +89 -0
  3. {shareddata-6.86.0 → shareddata-6.94.0}/setup.py +4 -2
  4. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/__init__.py +8 -1
  5. shareddata-6.94.0/src/SharedData/API/access_log.py +306 -0
  6. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/apidocs.yml +1 -1
  7. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/auth.py +10 -2
  8. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/data.py +1 -1
  9. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/tables.py +24 -16
  10. shareddata-6.94.0/src/SharedData/Analytics/__init__.py +1 -0
  11. shareddata-6.94.0/src/SharedData/Analytics/cdie.py +283 -0
  12. shareddata-6.94.0/src/SharedData/Analytics/cdie_calendar.py +28 -0
  13. shareddata-6.94.0/src/SharedData/Analytics/cdie_store.py +74 -0
  14. shareddata-6.94.0/src/SharedData/Analytics/data/cdie/anbima_holidays.csv +235 -0
  15. shareddata-6.94.0/src/SharedData/Analytics/data/cdie/b3_session_overrides.csv +37 -0
  16. shareddata-6.94.0/src/SharedData/Analytics/data/cdie/copom.csv +33 -0
  17. shareddata-6.94.0/src/SharedData/Analytics/data/cdie/manifest.json +56 -0
  18. shareddata-6.94.0/src/SharedData/ConsensusGraph.py +869 -0
  19. shareddata-6.94.0/src/SharedData/DataAPI/cdie.py +72 -0
  20. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/service.py +6 -1
  21. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/ClientAPI.py +94 -51
  22. shareddata-6.94.0/src/SharedData/KnowledgeBase.py +1474 -0
  23. shareddata-6.94.0/src/SharedData/KnowledgeContract.py +978 -0
  24. shareddata-6.94.0/src/SharedData/KnowledgeEvaluation.py +232 -0
  25. shareddata-6.94.0/src/SharedData/KnowledgeExperiment.py +335 -0
  26. shareddata-6.94.0/src/SharedData/KnowledgeGraph.py +536 -0
  27. shareddata-6.94.0/src/SharedData/KnowledgeLookup.py +156 -0
  28. shareddata-6.94.0/src/SharedData/KnowledgeMarket.py +417 -0
  29. shareddata-6.94.0/src/SharedData/KnowledgeOntology.py +176 -0
  30. shareddata-6.94.0/src/SharedData/KnowledgeResearch.py +481 -0
  31. shareddata-6.94.0/src/SharedData/KnowledgeScenario.py +200 -0
  32. shareddata-6.94.0/src/SharedData/KnowledgeXBRL.py +273 -0
  33. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Logger.py +3 -0
  34. shareddata-6.94.0/src/SharedData/ResearchMCP.py +432 -0
  35. shareddata-6.94.0/src/SharedData/Routines/PackageRoutine.py +174 -0
  36. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/RoutineCommand.py +8 -1
  37. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/RoutineOrigin.py +6 -1
  38. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/WorkerLib.py +28 -3
  39. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/SharedData.py +21 -4
  40. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/SharedNumpy.py +124 -8
  41. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Table.py +120 -27
  42. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableDisk.py +5 -3
  43. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableIndex.py +96 -38
  44. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableIndexJitFunctions.py +1247 -48
  45. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableIndexJitGenerate.py +34 -14
  46. shareddata-6.94.0/src/SharedData/TableVector.py +140 -0
  47. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Utils.py +4 -2
  48. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/schedule_editor.py +9 -2
  49. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/workerpool.py +6 -1
  50. shareddata-6.94.0/src/shareddata.egg-info/PKG-INFO +164 -0
  51. {shareddata-6.86.0 → shareddata-6.94.0}/src/shareddata.egg-info/SOURCES.txt +25 -0
  52. {shareddata-6.86.0 → shareddata-6.94.0}/src/shareddata.egg-info/entry_points.txt +1 -0
  53. shareddata-6.86.0/PKG-INFO +0 -245
  54. shareddata-6.86.0/README.md +0 -170
  55. shareddata-6.86.0/src/shareddata.egg-info/PKG-INFO +0 -245
  56. {shareddata-6.86.0 → shareddata-6.94.0}/LICENSE +0 -0
  57. {shareddata-6.86.0 → shareddata-6.94.0}/MANIFEST.in +0 -0
  58. {shareddata-6.86.0 → shareddata-6.94.0}/pyproject.toml +0 -0
  59. {shareddata-6.86.0 → shareddata-6.94.0}/setup.cfg +0 -0
  60. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/ServerGunicorn.py +0 -0
  61. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/ServerWaitress.py +0 -0
  62. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/apidocs-dark.css +0 -0
  63. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/constants.py +0 -0
  64. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/download_paths.py +0 -0
  65. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/download_slots.py +0 -0
  66. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/install/worker.ps1 +0 -0
  67. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/install/worker.sh +0 -0
  68. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/listcache.py +0 -0
  69. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/__init__.py +0 -0
  70. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/cache.py +0 -0
  71. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/collections.py +0 -0
  72. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/downloads.py +0 -0
  73. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/metadata.py +0 -0
  74. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/operations.py +0 -0
  75. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/schedules.py +0 -0
  76. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/sqlite.py +0 -0
  77. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/system.py +0 -0
  78. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/timeseries.py +0 -0
  79. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/web_routes.py +0 -0
  80. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/routes/workers.py +0 -0
  81. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/API/utils.py +0 -0
  82. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Auth/__init__.py +0 -0
  83. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Auth/bootstrap.py +0 -0
  84. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Auth/migrate.py +0 -0
  85. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Auth/store.py +0 -0
  86. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Auth/workers.py +0 -0
  87. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/CacheRedis.py +0 -0
  88. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/CollectionMongoDB.py +0 -0
  89. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/__init__.py +0 -0
  90. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/catalog.py +0 -0
  91. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/config.py +0 -0
  92. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/errors.py +0 -0
  93. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/indexes.py +0 -0
  94. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/readers.py +0 -0
  95. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/DataAPI/resolve.py +0 -0
  96. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Database.py +0 -0
  97. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Defaults.py +0 -0
  98. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/AWSEC2.py +0 -0
  99. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/AWSS3.py +0 -0
  100. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/AutoDocstrings.py +0 -0
  101. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/ClientSocket.py +0 -0
  102. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/ClientWebSocket.py +0 -0
  103. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/LogHandlerAPI.py +0 -0
  104. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/LogReader.py +0 -0
  105. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/MongoDBClient.py +0 -0
  106. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/SaveTables.py +0 -0
  107. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/ServerSocket.py +0 -0
  108. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/ServerWebSocket.py +0 -0
  109. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/StreamsCache.py +0 -0
  110. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/StreamsPersist.py +0 -0
  111. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/SyncTable.py +0 -0
  112. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/TunnelWebSocket.py +0 -0
  113. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/IO/__init__.py +0 -0
  114. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Listing.py +0 -0
  115. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/LockWaiters.py +0 -0
  116. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/MCP/__init__.py +0 -0
  117. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/MCP/__main__.py +0 -0
  118. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/MCP/client.py +0 -0
  119. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/MCP/server.py +0 -0
  120. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Metadata.py +0 -0
  121. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/MultiProc.py +0 -0
  122. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/OpenFIGI.py +0 -0
  123. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Redaction.py +0 -0
  124. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/ArchiveOldCollections.py +0 -0
  125. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/BatchJob.py +0 -0
  126. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/ContainerHealth.py +0 -0
  127. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/JobStore.py +0 -0
  128. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/Schedule.py +0 -0
  129. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/ScheduleMirror.py +0 -0
  130. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/ScheduleMonitor.py +0 -0
  131. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/ScheduleStore.py +0 -0
  132. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/Scheduler.py +0 -0
  133. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/UpdateActiveJobs.py +0 -0
  134. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/Worker.py +0 -0
  135. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/WorkerBootstrap.py +0 -0
  136. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/WorkerPool.py +0 -0
  137. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Routines/__init__.py +0 -0
  138. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/SQLite.py +0 -0
  139. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/StreamKafka.py +0 -0
  140. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Symbol.py +0 -0
  141. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableIndexJitFunctionsManual.py +0 -0
  142. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableIndexJitHash.py +0 -0
  143. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableIndexJitLoc.py +0 -0
  144. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableProtocol.py +0 -0
  145. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableRead.py +0 -0
  146. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TableRewrite.py +0 -0
  147. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TimeSeriesDisk.py +0 -0
  148. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/TimeseriesContainer.py +0 -0
  149. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Users.py +0 -0
  150. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/ADDING_A_PAGE.md +0 -0
  151. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/__init__.py +0 -0
  152. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/activity_logger.py +0 -0
  153. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/app.py +0 -0
  154. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js +0 -0
  155. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/assets/dashAgGridFunctions.js +0 -0
  156. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/assets/google_logo.png +0 -0
  157. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/assets/kafka_keys.js +0 -0
  158. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/assets/logs.css +0 -0
  159. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/assets/sched.css +0 -0
  160. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/assets/styles.css +0 -0
  161. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/auth.py +0 -0
  162. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/backends.py +0 -0
  163. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/config.py +0 -0
  164. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/containers.py +0 -0
  165. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/identity.py +0 -0
  166. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/kafka_view.py +0 -0
  167. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/mongo_view.py +0 -0
  168. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/proc.py +0 -0
  169. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/redis_view.py +0 -0
  170. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/route_health.py +0 -0
  171. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/route_registry.py +0 -0
  172. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/sched.py +0 -0
  173. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/server.py +0 -0
  174. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/services.py +0 -0
  175. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/shell.py +0 -0
  176. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/standalone.py +0 -0
  177. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/account.js +0 -0
  178. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/custom.css +0 -0
  179. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/favicon.ico +0 -0
  180. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/favicon.png +0 -0
  181. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/logo.png +0 -0
  182. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/logo_96x96.png +0 -0
  183. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/shareddata-mark.svg +0 -0
  184. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/shell.css +0 -0
  185. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/shell.js +0 -0
  186. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/standalone.css +0 -0
  187. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/static/standalone.js +0 -0
  188. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/suggest.py +0 -0
  189. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/account.html +0 -0
  190. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/base.html +0 -0
  191. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/claim.html +0 -0
  192. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/claim_done.html +0 -0
  193. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/dash.html +0 -0
  194. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/index.html +0 -0
  195. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/login.html +0 -0
  196. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/login_2fa.html +0 -0
  197. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/partials/navbar.html +0 -0
  198. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/partials/scripts.html +0 -0
  199. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/recovery_codes.html +0 -0
  200. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/shell.html +0 -0
  201. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/templates/standalone.html +0 -0
  202. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/twofactor.py +0 -0
  203. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/Web/utils.py +0 -0
  204. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/__init__.py +0 -0
  205. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/build_info.py +0 -0
  206. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/fleet.py +0 -0
  207. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/preflight.py +0 -0
  208. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/private_files.py +0 -0
  209. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/release_backup.py +0 -0
  210. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/server.py +0 -0
  211. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/services.py +0 -0
  212. {shareddata-6.86.0 → shareddata-6.94.0}/src/SharedData/sharedmutexwin.pyd +0 -0
  213. {shareddata-6.86.0 → shareddata-6.94.0}/src/shareddata.egg-info/dependency_links.txt +0 -0
  214. {shareddata-6.86.0 → shareddata-6.94.0}/src/shareddata.egg-info/requires.txt +0 -0
  215. {shareddata-6.86.0 → shareddata-6.94.0}/src/shareddata.egg-info/top_level.txt +0 -0
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: shareddata
3
+ Version: 6.94.0
4
+ Summary: Memory-mapped record and time-series store with an HTTP API and a distributed scheduler
5
+ Home-page: https://shareddata.net
6
+ Author: shareddata
7
+ Author-email: admin@shareddata.net
8
+ License: MIT
9
+ Project-URL: Homepage, https://shareddata.net
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.11, <3.13
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: ipykernel<8,>=7.4
19
+ Requires-Dist: python-json-logger<5,>=4.2
20
+ Requires-Dist: python-dotenv<2,>=1.2.4
21
+ Requires-Dist: pandas<3,>=2.2
22
+ Requires-Dist: scipy<1.18,>=1.14
23
+ Requires-Dist: numpy<1.27,>=1.26.4
24
+ Requires-Dist: numba==0.59.1
25
+ Requires-Dist: XlsxWriter<4,>=3.2.9
26
+ Requires-Dist: openpyxl<4,>=3.1.5
27
+ Requires-Dist: tqdm<5,>=4.70.1
28
+ Requires-Dist: cffi<3,>=2.1
29
+ Requires-Dist: tzlocal<6,>=5.4.4
30
+ Requires-Dist: websockets<18,>=17.1
31
+ Requires-Dist: lz4<5,>=4.4.5
32
+ Requires-Dist: flask<4,>=3.1.3
33
+ Requires-Dist: waitress<4,>=3.0.2
34
+ Requires-Dist: requests<3,>=2.34.2
35
+ Requires-Dist: flasgger==0.9.7.1
36
+ Requires-Dist: pymongo<5,>=4.18.2
37
+ Requires-Dist: setuptools>=84
38
+ Requires-Dist: filelock<5,>=4.0.9
39
+ Requires-Dist: gunicorn<27,>=26.2
40
+ Requires-Dist: aiohttp<4,>=3.14.3
41
+ Requires-Dist: boto3<2,>=1.43
42
+ Requires-Dist: confluent-kafka<3,>=2.15.1
43
+ Requires-Dist: aiokafka<1,>=0.14
44
+ Requires-Dist: redis<9,>=8.1
45
+ Requires-Dist: Authlib<2,>=1.8
46
+ Requires-Dist: PyJWT<3,>=2.8
47
+ Requires-Dist: argon2-cffi<26,>=25.1
48
+ Requires-Dist: flask-jwt-extended<5,>=4.6
49
+ Requires-Dist: cryptography<51,>=50.0.2
50
+ Requires-Dist: segno<2,>=1.6
51
+ Requires-Dist: psutil<8,>=5.9
52
+ Requires-Dist: mcp>=1.27
53
+ Requires-Dist: httpx>=0.27
54
+ Provides-Extra: test
55
+ Requires-Dist: pytest<10,>=8.3.4; extra == "test"
56
+ Requires-Dist: pytest-asyncio<2,>=0.24; extra == "test"
57
+ Requires-Dist: playwright<2,>=1.63; extra == "test"
58
+ Provides-Extra: s3
59
+ Provides-Extra: kafka
60
+ Provides-Extra: redis
61
+ Provides-Extra: all
62
+ Dynamic: author
63
+ Dynamic: author-email
64
+ Dynamic: classifier
65
+ Dynamic: description
66
+ Dynamic: description-content-type
67
+ Dynamic: home-page
68
+ Dynamic: license
69
+ Dynamic: license-file
70
+ Dynamic: project-url
71
+ Dynamic: provides-extra
72
+ Dynamic: requires-dist
73
+ Dynamic: requires-python
74
+ Dynamic: summary
75
+
76
+ # SharedData
77
+
78
+ SharedData is an open-source memory-mapped store for records and time series —
79
+ in the workload category of tick stores such as kdb+ — plus the scheduler and
80
+ worker fleet that produce the data. Local processes map the same files through
81
+ the operating system's page cache; indexed writes coordinate through a
82
+ per-table process lock. Remote clients use an authenticated HTTP API; shared
83
+ memory does not span hosts.
84
+
85
+ Website and manual: <https://shareddata.net>
86
+
87
+ ## Install
88
+
89
+ Python 3.11 or 3.12:
90
+
91
+ ```sh
92
+ python -m pip install shareddata
93
+ ```
94
+
95
+ The [quick start](https://shareddata.net/docs/QUICK_START/) uses illustrative
96
+ records in isolated storage. See [configuration](https://shareddata.net/docs/CONFIGURATION/)
97
+ for public environment names and [Windows limitations](https://shareddata.net/docs/WINDOWS/)
98
+ for native requirements and rewrite behavior.
99
+
100
+ ## A record in one minute
101
+
102
+ Save as `example.py` after setting up isolated storage in the
103
+ [quick start](https://shareddata.net/docs/QUICK_START/), then run `python example.py`:
104
+
105
+ ```python
106
+ from SharedData.SharedData import SharedData
107
+ import pandas as pd
108
+
109
+ store = SharedData(__file__, user="example")
110
+ index = pd.MultiIndex.from_tuples(
111
+ [(pd.Timestamp("2026-01-02"), "EXAMPLE")],
112
+ names=["date", "symbol"],
113
+ )
114
+ rows = pd.DataFrame({"close": [100.0]}, index=index)
115
+ records = store.table(
116
+ "MarketData", "D1", "DEMO", "PRICES",
117
+ value=rows,
118
+ )
119
+ print(records.loc["2026-01-02", "EXAMPLE"])
120
+ ```
121
+
122
+ ## Containers and execution
123
+
124
+ - [Tables](https://shareddata.net/docs/core/table/): structured NumPy records and primary-key lookup.
125
+ - [Time series](https://shareddata.net/docs/core/timeseries/): timestamped float matrices.
126
+ - Collections, Redis caches, Kafka streams and optional S3 integration.
127
+ - [HTTP API](https://shareddata.net/docs/guide/swagger/): authenticated remote access.
128
+ - [Measured latency](https://shareddata.net/docs/performance/): what the containers cost in practice.
129
+
130
+ ## A store and the fleet that fills it
131
+
132
+ A routine is any program the Scheduler owns — a feed collector, a parser, a
133
+ consolidation job, an API server. A schedule lists the routines that should
134
+ stay alive or run at their times. Batch routines wait for their batch dependencies
135
+ to complete; realtime routines wait for their realtime dependencies to be running.
136
+ Dependencies between the two kinds are not enforced at runtime. Commands travel
137
+ through a per-host worker pool that validates the requested source revision
138
+ before every launch; routines report heartbeats and lifecycle messages through
139
+ the logging plane, and the Scheduler writes queryable state into the store.
140
+
141
+ - [Orchestration](https://shareddata.net/docs/orchestration/): the routine model, the Scheduler's tick and the worker pool.
142
+ - [Use cases](https://shareddata.net/docs/use-cases/): the workloads a production deployment runs, generalized.
143
+ - [Schedules](https://shareddata.net/docs/scheduler-store/) and [batch jobs](https://shareddata.net/docs/job-store/): where the state lives.
144
+
145
+ ## Data by symbol
146
+
147
+ The [semantic data API](https://shareddata.net/docs/data-api/) resolves a
148
+ symbol or entity to a dataset and includes provenance. It is a convenience,
149
+ not the fast path: producers keep using direct container interfaces for
150
+ performance-critical work.
151
+
152
+ ## Client integration
153
+
154
+ [Client integration](https://shareddata.net/docs/ecosystem/) covers
155
+ authenticated API access, the MCP server for compatible agent clients, and
156
+ worker access.
157
+
158
+ ## Development and license
159
+
160
+ Submit changes through the repository's review process. Read the
161
+ [development guide](https://shareddata.net/docs/DEVELOPMENT/) and run the
162
+ checks relevant to your change; [building and publishing](https://shareddata.net/docs/guide/building/)
163
+ explains how the public site is produced and audited. SharedData is released
164
+ under the MIT license; see `LICENSE` in the source distribution.
@@ -0,0 +1,89 @@
1
+ # SharedData
2
+
3
+ SharedData is an open-source memory-mapped store for records and time series —
4
+ in the workload category of tick stores such as kdb+ — plus the scheduler and
5
+ worker fleet that produce the data. Local processes map the same files through
6
+ the operating system's page cache; indexed writes coordinate through a
7
+ per-table process lock. Remote clients use an authenticated HTTP API; shared
8
+ memory does not span hosts.
9
+
10
+ Website and manual: <https://shareddata.net>
11
+
12
+ ## Install
13
+
14
+ Python 3.11 or 3.12:
15
+
16
+ ```sh
17
+ python -m pip install shareddata
18
+ ```
19
+
20
+ The [quick start](https://shareddata.net/docs/QUICK_START/) uses illustrative
21
+ records in isolated storage. See [configuration](https://shareddata.net/docs/CONFIGURATION/)
22
+ for public environment names and [Windows limitations](https://shareddata.net/docs/WINDOWS/)
23
+ for native requirements and rewrite behavior.
24
+
25
+ ## A record in one minute
26
+
27
+ Save as `example.py` after setting up isolated storage in the
28
+ [quick start](https://shareddata.net/docs/QUICK_START/), then run `python example.py`:
29
+
30
+ ```python
31
+ from SharedData.SharedData import SharedData
32
+ import pandas as pd
33
+
34
+ store = SharedData(__file__, user="example")
35
+ index = pd.MultiIndex.from_tuples(
36
+ [(pd.Timestamp("2026-01-02"), "EXAMPLE")],
37
+ names=["date", "symbol"],
38
+ )
39
+ rows = pd.DataFrame({"close": [100.0]}, index=index)
40
+ records = store.table(
41
+ "MarketData", "D1", "DEMO", "PRICES",
42
+ value=rows,
43
+ )
44
+ print(records.loc["2026-01-02", "EXAMPLE"])
45
+ ```
46
+
47
+ ## Containers and execution
48
+
49
+ - [Tables](https://shareddata.net/docs/core/table/): structured NumPy records and primary-key lookup.
50
+ - [Time series](https://shareddata.net/docs/core/timeseries/): timestamped float matrices.
51
+ - Collections, Redis caches, Kafka streams and optional S3 integration.
52
+ - [HTTP API](https://shareddata.net/docs/guide/swagger/): authenticated remote access.
53
+ - [Measured latency](https://shareddata.net/docs/performance/): what the containers cost in practice.
54
+
55
+ ## A store and the fleet that fills it
56
+
57
+ A routine is any program the Scheduler owns — a feed collector, a parser, a
58
+ consolidation job, an API server. A schedule lists the routines that should
59
+ stay alive or run at their times. Batch routines wait for their batch dependencies
60
+ to complete; realtime routines wait for their realtime dependencies to be running.
61
+ Dependencies between the two kinds are not enforced at runtime. Commands travel
62
+ through a per-host worker pool that validates the requested source revision
63
+ before every launch; routines report heartbeats and lifecycle messages through
64
+ the logging plane, and the Scheduler writes queryable state into the store.
65
+
66
+ - [Orchestration](https://shareddata.net/docs/orchestration/): the routine model, the Scheduler's tick and the worker pool.
67
+ - [Use cases](https://shareddata.net/docs/use-cases/): the workloads a production deployment runs, generalized.
68
+ - [Schedules](https://shareddata.net/docs/scheduler-store/) and [batch jobs](https://shareddata.net/docs/job-store/): where the state lives.
69
+
70
+ ## Data by symbol
71
+
72
+ The [semantic data API](https://shareddata.net/docs/data-api/) resolves a
73
+ symbol or entity to a dataset and includes provenance. It is a convenience,
74
+ not the fast path: producers keep using direct container interfaces for
75
+ performance-critical work.
76
+
77
+ ## Client integration
78
+
79
+ [Client integration](https://shareddata.net/docs/ecosystem/) covers
80
+ authenticated API access, the MCP server for compatible agent clients, and
81
+ worker access.
82
+
83
+ ## Development and license
84
+
85
+ Submit changes through the repository's review process. Read the
86
+ [development guide](https://shareddata.net/docs/DEVELOPMENT/) and run the
87
+ checks relevant to your change; [building and publishing](https://shareddata.net/docs/guide/building/)
88
+ explains how the public site is produced and audited. SharedData is released
89
+ under the MIT license; see `LICENSE` in the source distribution.
@@ -66,8 +66,8 @@ test_requires = [
66
66
 
67
67
  setup(
68
68
  name='shareddata',
69
- version='6.86.0',
70
- description='Memory Mapped / Shared Memory Database with S3 repository',
69
+ version='6.94.0',
70
+ description='Memory-mapped record and time-series store with an HTTP API and a distributed scheduler',
71
71
  author='shareddata',
72
72
  author_email='admin@shareddata.net',
73
73
  url='https://shareddata.net',
@@ -92,6 +92,7 @@ setup(
92
92
  extras_require={'test': test_requires, 's3': [], 'kafka': [], 'redis': [], 'all': []},
93
93
  include_package_data=True,
94
94
  package_data={
95
+ 'SharedData.Analytics': ['data/cdie/*.csv', 'data/cdie/*.json'],
95
96
  'SharedData.API': ['apidocs.yml', 'apidocs-dark.css', 'install/*'],
96
97
  'SharedData.Web': [
97
98
  'templates/*', 'templates/**/*',
@@ -105,6 +106,7 @@ setup(
105
106
  'shdata-web=SharedData.Web.server:main',
106
107
  'shdata-server=SharedData.server:main',
107
108
  'shareddata-mcp=SharedData.MCP.server:main',
109
+ 'shareddata-research-mcp=SharedData.ResearchMCP:main',
108
110
  ],
109
111
  },
110
112
  )
@@ -111,12 +111,16 @@ def create_app(shdata=None, quiet=True, app=None):
111
111
 
112
112
  @app.before_request
113
113
  def auth_availability():
114
+ from flask import g
114
115
  from SharedData.Auth.store import StoreUnavailable
115
116
  # Worker install scripts and enrollment take a one-time install code instead of a token.
116
117
  public = request.path in ('/api/health', '/api/heartbeat', '/api/workers/enroll') or \
117
118
  request.path.startswith(('/api/auth/', '/api/web/', '/api/workers/install/'))
118
119
  from SharedData.API.auth import local_log_token_ok
119
- if request.path.startswith('/api/') and not public and not local_log_token_ok(request):
120
+ if local_log_token_ok(request):
121
+ g.client_user = 'local-log'
122
+ return
123
+ if request.path.startswith('/api/') and not public:
120
124
  try:
121
125
  if not authenticate_fn(request, shdata):
122
126
  return jsonify(error='unauthorized'), 401
@@ -126,6 +130,9 @@ def create_app(shdata=None, quiet=True, app=None):
126
130
  # Register request lifecycle hooks
127
131
  register_request_hooks(app)
128
132
 
133
+ from SharedData.API import access_log
134
+ access_log.register(app, shdata)
135
+
129
136
  # Store shdata on app for access in routes
130
137
  app.shdata = shdata
131
138
 
@@ -0,0 +1,306 @@
1
+ """
2
+ access_log.py — per-client API access logging, two-tier, never on the request path.
3
+
4
+ Tier 1 (always): per-minute aggregate counters keyed by
5
+ (user, route, status_class) — requests, bytes_in, bytes_out, total_ms, max_ms.
6
+ Small docs; powers bandwidth / usage / route-preference analytics.
7
+
8
+ Tier 2 (events): one document per request, but only for security-relevant or
9
+ sampled traffic — status >= 300, failed authentication, unmatched paths, plus a
10
+ configurable sample of successful requests.
11
+
12
+ Flush: a daemon thread drains both tiers every FLUSH_INTERVAL_S seconds and
13
+ writes through the in-process SharedData handle — Requests/RT/API/ACCESS_AGG
14
+ and Requests/RT/API/ACCESS_EVT (hasindex=False collections → insert_many).
15
+ No HTTP loopback: a ClientAPI.post_collection call would itself be a request
16
+ and self-log. Each server worker process flushes its own buffer (worker_pid).
17
+
18
+ Overhead: the request path does one dict update under a short lock and, for
19
+ events, a put_nowait — no I/O, no body reads. A full event queue drops the
20
+ record (counted in dropped_events). The request path never blocks on Mongo.
21
+
22
+ Secrets: the token itself is never logged — only token_fp (first 8 chars of the
23
+ server-side token_hash, stashed on flask.g by API/auth.py). The raw query
24
+ string is never logged either (it can carry ?token=); only the allow-listed
25
+ data-path arguments are kept.
26
+
27
+ Client IP: X-Real-IP / X-Forwarded-For are trusted only when the direct peer
28
+ is a known proxy; a LAN client hitting the API port directly cannot spoof its
29
+ address. Built in: loopback only (a same-host proxy). A proxy on another
30
+ address, or one that reaches the API by its LAN address, is listed in env
31
+ SHAREDDATA_ACCESSLOG_TRUSTED (comma-separated, replaces the built-in list).
32
+ """
33
+
34
+ import os
35
+ import queue
36
+ import random
37
+ import socket
38
+ import threading
39
+ import time
40
+ from collections import defaultdict
41
+ from datetime import datetime, timezone
42
+
43
+ from flask import g, request
44
+
45
+ from SharedData.Logger import Logger
46
+
47
+ # ─── Configuration ──────────────────────────────────────────────────────────
48
+
49
+ FLUSH_INTERVAL_S = float(os.environ.get('SHAREDDATA_ACCESSLOG_FLUSH_S', 30))
50
+ MAX_QUEUE_SIZE = int(os.environ.get('SHAREDDATA_ACCESSLOG_QUEUE', 10_000))
51
+ EVENT_SAMPLE = float(os.environ.get('SHAREDDATA_ACCESSLOG_SAMPLE', 0.01))
52
+ DISABLED = os.environ.get('SHAREDDATA_ACCESSLOG', '1') in ('0', 'false', 'off')
53
+
54
+ DATABASE = 'Requests'
55
+ PERIOD = 'RT'
56
+ SOURCE = 'API'
57
+ AGG_TABLE = 'ACCESS_AGG'
58
+ EVT_TABLE = 'ACCESS_EVT'
59
+
60
+ # Peers allowed to supply client addresses in X-Real-IP / X-Forwarded-For:
61
+ # loopback (same-host proxy), unless the deployment lists its own proxies.
62
+ _TRUSTED_PROXIES = frozenset(
63
+ part.strip() for part in (os.environ.get('SHAREDDATA_ACCESSLOG_TRUSTED') or '127.0.0.1,::1').split(',')
64
+ if part.strip()
65
+ )
66
+
67
+ # Public/polled endpoints and noise that is not worth a counter.
68
+ _SKIP_PREFIXES = (
69
+ '/api/health',
70
+ '/api/heartbeat',
71
+ '/flasgger_static',
72
+ '/favicon.ico',
73
+ )
74
+
75
+ # Data-path arguments worth keeping on event records (the object a client
76
+ # read/wrote). Everything else — most of all 'token' — stays out.
77
+ _DATA_ARGS = ('database', 'period', 'source', 'tablename', 'tablesubfolder',
78
+ 'timeseries', 'symbol', 'user')
79
+
80
+ _PATH_MAX = 200
81
+ _UA_MAX = 160
82
+
83
+ # ─── Internal state ─────────────────────────────────────────────────────────
84
+
85
+ _evt_queue: 'queue.Queue[dict]' = queue.Queue(maxsize=MAX_QUEUE_SIZE)
86
+
87
+ # {(minute_epoch, user, route, status_class): [requests, bytes_in, bytes_out, total_ms, max_ms]}
88
+ _agg = defaultdict(lambda: [0, 0, 0, 0.0, 0.0])
89
+ _agg_lock = threading.Lock()
90
+
91
+ _pending_evts: list = [] # evt docs retained across a failed flush
92
+ _pending_lock = threading.Lock()
93
+ dropped_events = 0 # events lost to a full queue / retained-buffer cap
94
+
95
+ _shdata = None
96
+ _sink = None # fn(collection_name, docs); injectable for tests
97
+ _HOSTNAME = socket.gethostname()
98
+
99
+
100
+ def _client_ip() -> str:
101
+ """The real client address: the peer itself, unless the peer is a known
102
+ proxy that supplied X-Real-IP / X-Forwarded-For (original client first)."""
103
+ peer = request.remote_addr or ''
104
+ if peer in _TRUSTED_PROXIES:
105
+ real = request.headers.get('X-Real-IP')
106
+ if real:
107
+ return real.split(',')[0].strip()
108
+ xff = request.headers.get('X-Forwarded-For')
109
+ if xff:
110
+ return xff.split(',')[0].strip()
111
+ return peer or 'unknown'
112
+
113
+
114
+ def _user() -> str:
115
+ """Auth identity resolved by API/auth.authenticate (or 'local-log' set by
116
+ the auth_availability hook). 'invalid-token' when a token was presented
117
+ and rejected, 'anonymous' when none was needed/presented."""
118
+ user = getattr(g, 'client_user', None)
119
+ if user:
120
+ return user
121
+ if getattr(g, 'auth_ok', None) is False:
122
+ return 'invalid-token'
123
+ return 'anonymous'
124
+
125
+
126
+ def _route() -> str:
127
+ rule = request.url_rule
128
+ return rule.rule if rule is not None else '<unmatched>'
129
+
130
+
131
+ def _record_event(response, ms, bytes_in, bytes_out):
132
+ rec = {
133
+ 'kind': 'evt',
134
+ 'ts': datetime.now(timezone.utc).isoformat(),
135
+ 'host': _HOSTNAME,
136
+ 'worker_pid': os.getpid(),
137
+ 'remote_ip': _client_ip(),
138
+ 'user': _user(),
139
+ 'method': request.method,
140
+ 'route': _route(),
141
+ 'status': response.status_code,
142
+ 'ms': round(ms, 3),
143
+ 'bytes_in': bytes_in,
144
+ 'bytes_out': bytes_out,
145
+ }
146
+ token_fp = getattr(g, 'token_fp', None)
147
+ if token_fp:
148
+ rec['token_fp'] = token_fp
149
+ if request.url_rule is None or response.status_code >= 300:
150
+ rec['path'] = request.path[:_PATH_MAX] # raw path: probes/errors only
151
+ ua = request.headers.get('User-Agent')
152
+ if ua:
153
+ rec['user_agent'] = ua[:_UA_MAX]
154
+ args = {k: request.args.get(k) for k in _DATA_ARGS if request.args.get(k) is not None}
155
+ if args:
156
+ rec['args'] = args
157
+ global dropped_events
158
+ try:
159
+ _evt_queue.put_nowait(rec)
160
+ except queue.Full:
161
+ dropped_events += 1
162
+
163
+
164
+ # ─── Flask hooks ────────────────────────────────────────────────────────────
165
+
166
+ def before_request():
167
+ g._accesslog_start = time.perf_counter()
168
+
169
+
170
+ def after_request(response):
171
+ try:
172
+ path = request.path
173
+ if any(path.startswith(p) for p in _SKIP_PREFIXES):
174
+ return response
175
+
176
+ start = getattr(g, '_accesslog_start', None)
177
+ ms = (time.perf_counter() - start) * 1000.0 if start is not None else 0.0
178
+ status = response.status_code
179
+
180
+ content_length = response.calculate_content_length()
181
+ bytes_out = content_length if content_length is not None else 0
182
+ bytes_in = g.get('request_bytes', request.content_length or 0)
183
+
184
+ minute = int(time.time() // 60) * 60
185
+ key = (minute, _user(), _route(), f'{status // 100}xx')
186
+ with _agg_lock:
187
+ bucket = _agg[key]
188
+ bucket[0] += 1
189
+ bucket[1] += bytes_in
190
+ bucket[2] += bytes_out
191
+ bucket[3] += ms
192
+ if ms > bucket[4]:
193
+ bucket[4] = ms
194
+
195
+ if status >= 300 or getattr(g, 'auth_ok', None) is False \
196
+ or random.random() < EVENT_SAMPLE:
197
+ _record_event(response, ms, bytes_in, bytes_out)
198
+
199
+ except Exception as exc: # logging must never break the app
200
+ try:
201
+ Logger.log.warning(f'access_log after_request: {exc}')
202
+ except Exception:
203
+ pass
204
+
205
+ return response
206
+
207
+
208
+ # ─── Flush ──────────────────────────────────────────────────────────────────
209
+
210
+ def _agg_docs(snapshot):
211
+ docs = []
212
+ for (minute, user, route, status_class), (n, bi, bo, total_ms, max_ms) in snapshot.items():
213
+ docs.append({
214
+ 'kind': 'agg',
215
+ 'ts': datetime.fromtimestamp(minute, tz=timezone.utc).isoformat(),
216
+ 'host': _HOSTNAME,
217
+ 'worker_pid': os.getpid(),
218
+ 'user': user,
219
+ 'route': route,
220
+ 'status_class': status_class,
221
+ 'requests': n,
222
+ 'bytes_in': bi,
223
+ 'bytes_out': bo,
224
+ 'total_ms': round(total_ms, 3),
225
+ 'max_ms': round(max_ms, 3),
226
+ })
227
+ return docs
228
+
229
+
230
+ def _flush_once(sink):
231
+ """Drain both tiers and hand them to sink(name, docs). Returns doc counts.
232
+ On sink failure event docs are retained for the next cycle (capped);
233
+ aggregate buckets are minute-resolution and simply lost on failure."""
234
+ global dropped_events, _agg
235
+ with _agg_lock:
236
+ snapshot = _agg
237
+ _agg = defaultdict(lambda: [0, 0, 0, 0.0, 0.0])
238
+ with _pending_lock:
239
+ evts = list(_pending_evts)
240
+ _pending_evts.clear()
241
+ while True:
242
+ try:
243
+ evts.append(_evt_queue.get_nowait())
244
+ except queue.Empty:
245
+ break
246
+
247
+ agg_docs = _agg_docs(snapshot)
248
+ if not agg_docs and not evts:
249
+ return 0, 0
250
+ try:
251
+ if agg_docs:
252
+ sink(AGG_TABLE, agg_docs)
253
+ if evts:
254
+ sink(EVT_TABLE, evts)
255
+ return len(agg_docs), len(evts)
256
+ except Exception as exc:
257
+ Logger.log.error(f'access_log flush error: {exc}')
258
+ with _pending_lock:
259
+ keep = _pending_evts + evts
260
+ if len(keep) > MAX_QUEUE_SIZE:
261
+ dropped_events += len(keep) - MAX_QUEUE_SIZE
262
+ keep = keep[-MAX_QUEUE_SIZE:]
263
+ _pending_evts[:] = keep
264
+ # aggs are minute-buckets; on failure they are simply lost (bounded)
265
+ return 0, 0
266
+
267
+
268
+ def _default_sink(name, docs):
269
+ coll = _shdata.collection(DATABASE, PERIOD, SOURCE, name,
270
+ hasindex=False, user='master')
271
+ coll.extend(docs)
272
+
273
+
274
+ def _flush_worker():
275
+ while True:
276
+ time.sleep(FLUSH_INTERVAL_S)
277
+ _flush_once(_sink or _default_sink)
278
+
279
+
280
+ # ─── Registration ───────────────────────────────────────────────────────────
281
+
282
+ def register(flask_app, shdata_instance, sink=None):
283
+ """Attach access logging to a Flask app; starts the flush thread once.
284
+
285
+ Parameters
286
+ ----------
287
+ flask_app : Flask application instance
288
+ shdata_instance : SharedData handle for the direct collection write
289
+ sink : optional fn(name, docs) override (tests)
290
+ """
291
+ global _shdata, _sink
292
+ if DISABLED:
293
+ Logger.log.info('access_log: disabled (SHAREDDATA_ACCESSLOG=0)')
294
+ return
295
+ if flask_app.extensions.get('shareddata_access_log'):
296
+ return
297
+ _shdata = shdata_instance
298
+ if sink is not None:
299
+ _sink = sink
300
+ flask_app.before_request(before_request)
301
+ flask_app.after_request(after_request)
302
+ flask_app.extensions['shareddata_access_log'] = True
303
+ threading.Thread(target=_flush_worker, daemon=True,
304
+ name='api-access-log-flush').start()
305
+ Logger.log.info('access_log: registered (flush every %ds, event sample %.3f)',
306
+ FLUSH_INTERVAL_S, EVENT_SAMPLE)
@@ -2,7 +2,7 @@
2
2
  openapi: 3.0.3
3
3
  info:
4
4
  title: SharedData API
5
- version: 6.86.0
5
+ version: 6.86.1
6
6
  description: |
7
7
  HTTP API of a SharedData server. The same operations are available from Python
8
8
  through `SharedData.IO.ClientAPI` (for example `ClientAPI.get_table(...)`).
@@ -120,17 +120,21 @@ def check_permissions(reqpath: list, permissions: dict, method: str, identity=No
120
120
  def authenticate(request, shdata) -> bool:
121
121
  """
122
122
  Authenticate an incoming HTTP request by validating a token and verifying permissions.
123
-
123
+
124
124
  Parameters:
125
125
  request (flask.Request): The incoming HTTP request object.
126
126
  shdata: SharedData instance for accessing user table.
127
-
127
+
128
128
  Returns:
129
129
  bool: True if authenticated and authorized; False otherwise.
130
130
  """
131
+ from flask import g
131
132
  from SharedData.Auth.store import get_store, StoreUnavailable, token_hash
132
133
  token = get_token_from_request(request)
134
+ if token:
135
+ g.token_fp = token_hash(token)[:8]
133
136
  if not token:
137
+ g.auth_ok = False
134
138
  time.sleep(AUTH_FAILURE_SLEEP_SECONDS)
135
139
  return False
136
140
  try:
@@ -138,8 +142,12 @@ def authenticate(request, shdata) -> bool:
138
142
  except Exception as exc:
139
143
  raise StoreUnavailable('auth store lookup failed') from exc
140
144
  if not userdata:
145
+ g.auth_ok = False
146
+ g.client_user = 'invalid-token'
141
147
  time.sleep(AUTH_FAILURE_SLEEP_SECONDS)
142
148
  return False
149
+ g.auth_ok = True
150
+ g.client_user = userdata.get('email') or userdata.get('symbol') or 'unknown'
143
151
  with user_by_token_lock:
144
152
  if len(user_by_token) >= 4096:
145
153
  user_by_token.clear()
@@ -14,7 +14,7 @@ from SharedData.Auth.store import StoreUnavailable
14
14
  from SharedData.DataAPI import service
15
15
  from SharedData.DataAPI.errors import DataAPIError
16
16
 
17
- KEYED = ('symbol', 'marketdata', 'signals', 'portfolios', 'riskfactors', 'vol')
17
+ KEYED = ('symbol', 'marketdata', 'signals', 'portfolios', 'riskfactors', 'vol', 'cdie')
18
18
 
19
19
 
20
20
  def token_permissions(request):