shareddata 6.85.17__tar.gz → 6.86.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.
Files changed (189) hide show
  1. {shareddata-6.85.17/src/shareddata.egg-info → shareddata-6.86.0}/PKG-INFO +7 -5
  2. {shareddata-6.85.17 → shareddata-6.86.0}/README.md +6 -4
  3. {shareddata-6.85.17 → shareddata-6.86.0}/setup.py +1 -1
  4. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/apidocs.yml +141 -12
  5. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/collections.py +111 -26
  6. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/tables.py +30 -17
  7. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/timeseries.py +26 -18
  8. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Auth/workers.py +1 -1
  9. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/CacheRedis.py +4 -2
  10. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/ClientAPI.py +38 -15
  11. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/LogHandlerAPI.py +14 -14
  12. shareddata-6.86.0/src/SharedData/IO/SaveTables.py +326 -0
  13. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/ServerSocket.py +2 -2
  14. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/ServerWebSocket.py +2 -2
  15. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/StreamsCache.py +4 -4
  16. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/StreamsPersist.py +2 -2
  17. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/SyncTable.py +1 -1
  18. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/MultiProc.py +3 -0
  19. shareddata-6.86.0/src/SharedData/Routines/RoutineCommand.py +90 -0
  20. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/Worker.py +1 -1
  21. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/WorkerLib.py +322 -66
  22. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/SharedData.py +149 -41
  23. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/SharedNumpy.py +75 -15
  24. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Table.py +44 -13
  25. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableDisk.py +28 -45
  26. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableIndex.py +6 -1
  27. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TimeSeriesDisk.py +30 -1
  28. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/auth.py +20 -0
  29. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/route_health.py +1 -1
  30. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/standalone.py +1 -1
  31. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/build_info.py +3 -1
  32. {shareddata-6.85.17 → shareddata-6.86.0/src/shareddata.egg-info}/PKG-INFO +7 -5
  33. shareddata-6.85.17/src/SharedData/IO/SaveTables.py +0 -144
  34. shareddata-6.85.17/src/SharedData/Routines/RoutineCommand.py +0 -36
  35. {shareddata-6.85.17 → shareddata-6.86.0}/LICENSE +0 -0
  36. {shareddata-6.85.17 → shareddata-6.86.0}/MANIFEST.in +0 -0
  37. {shareddata-6.85.17 → shareddata-6.86.0}/pyproject.toml +0 -0
  38. {shareddata-6.85.17 → shareddata-6.86.0}/setup.cfg +0 -0
  39. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/ServerGunicorn.py +0 -0
  40. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/ServerWaitress.py +0 -0
  41. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/__init__.py +0 -0
  42. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/apidocs-dark.css +0 -0
  43. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/auth.py +0 -0
  44. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/constants.py +0 -0
  45. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/download_paths.py +0 -0
  46. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/download_slots.py +0 -0
  47. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/install/worker.ps1 +0 -0
  48. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/install/worker.sh +0 -0
  49. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/listcache.py +0 -0
  50. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/__init__.py +0 -0
  51. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/cache.py +0 -0
  52. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/data.py +0 -0
  53. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/downloads.py +0 -0
  54. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/metadata.py +0 -0
  55. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/operations.py +0 -0
  56. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/schedules.py +0 -0
  57. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/sqlite.py +0 -0
  58. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/system.py +0 -0
  59. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/web_routes.py +0 -0
  60. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/routes/workers.py +0 -0
  61. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/API/utils.py +0 -0
  62. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Auth/__init__.py +0 -0
  63. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Auth/bootstrap.py +0 -0
  64. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Auth/migrate.py +0 -0
  65. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Auth/store.py +0 -0
  66. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/CollectionMongoDB.py +0 -0
  67. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/__init__.py +0 -0
  68. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/catalog.py +0 -0
  69. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/config.py +0 -0
  70. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/errors.py +0 -0
  71. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/indexes.py +0 -0
  72. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/readers.py +0 -0
  73. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/resolve.py +0 -0
  74. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/DataAPI/service.py +0 -0
  75. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Database.py +0 -0
  76. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Defaults.py +0 -0
  77. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/AWSEC2.py +0 -0
  78. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/AWSS3.py +0 -0
  79. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/AutoDocstrings.py +0 -0
  80. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/ClientSocket.py +0 -0
  81. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/ClientWebSocket.py +0 -0
  82. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/LogReader.py +0 -0
  83. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/MongoDBClient.py +0 -0
  84. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/TunnelWebSocket.py +0 -0
  85. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/IO/__init__.py +0 -0
  86. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Listing.py +0 -0
  87. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/LockWaiters.py +0 -0
  88. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Logger.py +0 -0
  89. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/MCP/__init__.py +0 -0
  90. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/MCP/__main__.py +0 -0
  91. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/MCP/client.py +0 -0
  92. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/MCP/server.py +0 -0
  93. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Metadata.py +0 -0
  94. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/OpenFIGI.py +0 -0
  95. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Redaction.py +0 -0
  96. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/ArchiveOldCollections.py +0 -0
  97. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/BatchJob.py +0 -0
  98. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/ContainerHealth.py +0 -0
  99. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/JobStore.py +0 -0
  100. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/RoutineOrigin.py +0 -0
  101. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/Schedule.py +0 -0
  102. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/ScheduleMirror.py +0 -0
  103. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/ScheduleMonitor.py +0 -0
  104. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/ScheduleStore.py +0 -0
  105. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/Scheduler.py +0 -0
  106. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/UpdateActiveJobs.py +0 -0
  107. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/WorkerBootstrap.py +0 -0
  108. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/WorkerPool.py +0 -0
  109. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Routines/__init__.py +0 -0
  110. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/SQLite.py +0 -0
  111. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/StreamKafka.py +0 -0
  112. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Symbol.py +0 -0
  113. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableIndexJitFunctions.py +0 -0
  114. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableIndexJitFunctionsManual.py +0 -0
  115. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableIndexJitGenerate.py +0 -0
  116. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableIndexJitHash.py +0 -0
  117. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableIndexJitLoc.py +0 -0
  118. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableProtocol.py +0 -0
  119. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableRead.py +0 -0
  120. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TableRewrite.py +0 -0
  121. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/TimeseriesContainer.py +0 -0
  122. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Users.py +0 -0
  123. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Utils.py +0 -0
  124. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/ADDING_A_PAGE.md +0 -0
  125. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/__init__.py +0 -0
  126. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/activity_logger.py +0 -0
  127. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/app.py +0 -0
  128. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/assets/dashAgGridComponentFunctions.js +0 -0
  129. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/assets/dashAgGridFunctions.js +0 -0
  130. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/assets/google_logo.png +0 -0
  131. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/assets/kafka_keys.js +0 -0
  132. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/assets/logs.css +0 -0
  133. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/assets/sched.css +0 -0
  134. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/assets/styles.css +0 -0
  135. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/backends.py +0 -0
  136. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/config.py +0 -0
  137. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/containers.py +0 -0
  138. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/identity.py +0 -0
  139. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/kafka_view.py +0 -0
  140. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/mongo_view.py +0 -0
  141. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/proc.py +0 -0
  142. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/redis_view.py +0 -0
  143. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/route_registry.py +0 -0
  144. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/sched.py +0 -0
  145. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/schedule_editor.py +0 -0
  146. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/server.py +0 -0
  147. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/services.py +0 -0
  148. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/shell.py +0 -0
  149. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/account.js +0 -0
  150. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/custom.css +0 -0
  151. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/favicon.ico +0 -0
  152. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/favicon.png +0 -0
  153. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/logo.png +0 -0
  154. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/logo_96x96.png +0 -0
  155. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/shareddata-mark.svg +0 -0
  156. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/shell.css +0 -0
  157. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/shell.js +0 -0
  158. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/standalone.css +0 -0
  159. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/static/standalone.js +0 -0
  160. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/suggest.py +0 -0
  161. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/account.html +0 -0
  162. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/base.html +0 -0
  163. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/claim.html +0 -0
  164. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/claim_done.html +0 -0
  165. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/dash.html +0 -0
  166. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/index.html +0 -0
  167. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/login.html +0 -0
  168. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/login_2fa.html +0 -0
  169. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/partials/navbar.html +0 -0
  170. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/partials/scripts.html +0 -0
  171. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/recovery_codes.html +0 -0
  172. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/shell.html +0 -0
  173. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/templates/standalone.html +0 -0
  174. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/twofactor.py +0 -0
  175. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/utils.py +0 -0
  176. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/Web/workerpool.py +0 -0
  177. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/__init__.py +0 -0
  178. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/fleet.py +0 -0
  179. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/preflight.py +0 -0
  180. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/private_files.py +0 -0
  181. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/release_backup.py +0 -0
  182. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/server.py +0 -0
  183. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/services.py +0 -0
  184. {shareddata-6.85.17 → shareddata-6.86.0}/src/SharedData/sharedmutexwin.pyd +0 -0
  185. {shareddata-6.85.17 → shareddata-6.86.0}/src/shareddata.egg-info/SOURCES.txt +0 -0
  186. {shareddata-6.85.17 → shareddata-6.86.0}/src/shareddata.egg-info/dependency_links.txt +0 -0
  187. {shareddata-6.85.17 → shareddata-6.86.0}/src/shareddata.egg-info/entry_points.txt +0 -0
  188. {shareddata-6.85.17 → shareddata-6.86.0}/src/shareddata.egg-info/requires.txt +0 -0
  189. {shareddata-6.85.17 → shareddata-6.86.0}/src/shareddata.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shareddata
3
- Version: 6.85.17
3
+ Version: 6.86.0
4
4
  Summary: Memory Mapped / Shared Memory Database with S3 repository
5
5
  Home-page: https://shareddata.net
6
6
  Author: shareddata
@@ -100,7 +100,7 @@ SharedData provides a comprehensive set of features for high-performance financi
100
100
  - **📋 Comprehensive Logging System** - Enterprise-grade logging with multiple destinations
101
101
  - **🌐 Remote API Client** - REST API for remote data access and operations
102
102
 
103
- **📚 [Read the complete Core Features guide →](https://shareddata.net/docs/CORE_FEATURES.md)**
103
+ **[Read the manual](docs/index.md)** — [Table](docs/core/table.md) and [Time series](docs/core/timeseries.md) are the core. The HTTP spec is [Swagger](docs/guide/swagger.md).
104
104
 
105
105
  ## ⚡ Quick Start
106
106
 
@@ -169,7 +169,7 @@ For power users and complex scenarios:
169
169
  - **Production Deployment** - High availability and monitoring
170
170
  - **Monitoring & Debugging** - Performance profiling and health checks
171
171
 
172
- **📚 [Read the complete Advanced Usage guide →](https://shareddata.net/docs/ADVANCED_USAGE.md)**
172
+ **[Logging templates](docs/patterns/logging.md)** — log at the scheduler boundary; do not log inside the row loop.
173
173
 
174
174
  ## 🧭 Data by Symbol
175
175
 
@@ -232,10 +232,12 @@ MIT: free to use, change and distribute, commercially too. See the [LICENSE](htt
232
232
 
233
233
  ## 📚 Documentation Index
234
234
 
235
- - **[🏗️ Core Features](https://shareddata.net/docs/CORE_FEATURES.md)** - Comprehensive overview of all SharedData capabilities
235
+ - **[Manual](docs/index.md)** — current Read the Docs home
236
+ - **[Table](docs/core/table.md)** and **[Time series](docs/core/timeseries.md)**
237
+ - **[Swagger](docs/guide/swagger.md)** — `src/SharedData/API/apidocs.yml`, version 6.86.0
236
238
  - **[⚡ Quick Start](https://shareddata.net/docs/QUICK_START.md)** - Get started with SharedData in minutes
237
239
  - **[🔧 Configuration](https://shareddata.net/docs/CONFIGURATION.md)** - Complete configuration reference
238
- - **[🚀 Advanced Usage](https://shareddata.net/docs/ADVANCED_USAGE.md)** - Advanced patterns and optimization
240
+ - **[Logging templates](docs/patterns/logging.md)** — scheduler heartbeat vs a quiet data loop
239
241
  - **[🔄 Development](https://shareddata.net/docs/DEVELOPMENT.md)** - Contributing and development guidelines
240
242
 
241
243
  ---
@@ -25,7 +25,7 @@ SharedData provides a comprehensive set of features for high-performance financi
25
25
  - **📋 Comprehensive Logging System** - Enterprise-grade logging with multiple destinations
26
26
  - **🌐 Remote API Client** - REST API for remote data access and operations
27
27
 
28
- **📚 [Read the complete Core Features guide →](https://shareddata.net/docs/CORE_FEATURES.md)**
28
+ **[Read the manual](docs/index.md)** — [Table](docs/core/table.md) and [Time series](docs/core/timeseries.md) are the core. The HTTP spec is [Swagger](docs/guide/swagger.md).
29
29
 
30
30
  ## ⚡ Quick Start
31
31
 
@@ -94,7 +94,7 @@ For power users and complex scenarios:
94
94
  - **Production Deployment** - High availability and monitoring
95
95
  - **Monitoring & Debugging** - Performance profiling and health checks
96
96
 
97
- **📚 [Read the complete Advanced Usage guide →](https://shareddata.net/docs/ADVANCED_USAGE.md)**
97
+ **[Logging templates](docs/patterns/logging.md)** — log at the scheduler boundary; do not log inside the row loop.
98
98
 
99
99
  ## 🧭 Data by Symbol
100
100
 
@@ -157,10 +157,12 @@ MIT: free to use, change and distribute, commercially too. See the [LICENSE](htt
157
157
 
158
158
  ## 📚 Documentation Index
159
159
 
160
- - **[🏗️ Core Features](https://shareddata.net/docs/CORE_FEATURES.md)** - Comprehensive overview of all SharedData capabilities
160
+ - **[Manual](docs/index.md)** — current Read the Docs home
161
+ - **[Table](docs/core/table.md)** and **[Time series](docs/core/timeseries.md)**
162
+ - **[Swagger](docs/guide/swagger.md)** — `src/SharedData/API/apidocs.yml`, version 6.86.0
161
163
  - **[⚡ Quick Start](https://shareddata.net/docs/QUICK_START.md)** - Get started with SharedData in minutes
162
164
  - **[🔧 Configuration](https://shareddata.net/docs/CONFIGURATION.md)** - Complete configuration reference
163
- - **[🚀 Advanced Usage](https://shareddata.net/docs/ADVANCED_USAGE.md)** - Advanced patterns and optimization
165
+ - **[Logging templates](docs/patterns/logging.md)** — scheduler heartbeat vs a quiet data loop
164
166
  - **[🔄 Development](https://shareddata.net/docs/DEVELOPMENT.md)** - Contributing and development guidelines
165
167
 
166
168
  ---
@@ -66,7 +66,7 @@ test_requires = [
66
66
 
67
67
  setup(
68
68
  name='shareddata',
69
- version='6.85.17',
69
+ version='6.86.0',
70
70
  description='Memory Mapped / Shared Memory Database with S3 repository',
71
71
  author='shareddata',
72
72
  author_email='admin@shareddata.net',
@@ -2,7 +2,7 @@
2
2
  openapi: 3.0.3
3
3
  info:
4
4
  title: SharedData API
5
- version: 6.85.1
5
+ version: 6.86.0
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(...)`).
@@ -811,6 +811,8 @@ paths:
811
811
  format: binary
812
812
  '204':
813
813
  description: No data found
814
+ '404':
815
+ description: JSON TagNotFound with type and message; the tag does not exist.
814
816
  '401':
815
817
  $ref: '#/components/responses/Unauthorized'
816
818
  '500':
@@ -854,7 +856,8 @@ paths:
854
856
  format: binary
855
857
  responses:
856
858
  '201':
857
- description: Data inserted
859
+ description: 'Data inserted. JSON with status, tag, message and `appended_columns`: the columns added
860
+ to an existing tag (empty for a new tag).'
858
861
  '401':
859
862
  $ref: '#/components/responses/Unauthorized'
860
863
  '500':
@@ -1111,7 +1114,8 @@ paths:
1111
1114
  '500':
1112
1115
  $ref: '#/components/responses/ServerError'
1113
1116
  description: 'Upserts rows sent as BSON+LZ4 by primary key; creates the table on first write when `names`,
1114
- `formats` and `size` are given. Python: `ClientAPI.post_table(...)`.'
1117
+ `formats` and `size` are given. A `size` larger than the capacity of an existing table grows it. Python:
1118
+ `ClientAPI.post_table(...)`.'
1115
1119
  delete:
1116
1120
  tags:
1117
1121
  - Tables
@@ -1290,7 +1294,7 @@ paths:
1290
1294
  post:
1291
1295
  tags:
1292
1296
  - Collections
1293
- summary: Insert documents (lz4+BSON or JSON)
1297
+ summary: Insert or upsert documents (lz4+BSON or JSON)
1294
1298
  parameters:
1295
1299
  - in: path
1296
1300
  name: database
@@ -1327,20 +1331,66 @@ paths:
1327
1331
  format: binary
1328
1332
  application/json:
1329
1333
  schema:
1330
- type: array
1331
- items:
1332
- type: object
1334
+ oneOf:
1335
+ - type: array
1336
+ nullable: true
1337
+ items:
1338
+ type: object
1339
+ - type: object
1333
1340
  responses:
1334
1341
  '201':
1335
- description: Created
1342
+ description: Batch accepted. An empty batch succeeds with documents equal to zero.
1343
+ content:
1344
+ application/json:
1345
+ schema:
1346
+ type: object
1347
+ required: [status, documents]
1348
+ properties:
1349
+ status:
1350
+ type: string
1351
+ enum: [success]
1352
+ documents:
1353
+ type: integer
1354
+ minimum: 0
1355
+ description: Documents accepted in this request (not the number of newly inserted rows).
1356
+ example: {status: success, documents: 0}
1336
1357
  '401':
1337
1358
  $ref: '#/components/responses/Unauthorized'
1338
1359
  '400':
1339
- description: Invalid data
1360
+ description: >-
1361
+ Empty or undecodable body, missing BSON data field, invalid document shape,
1362
+ missing primary-key fields, or an incompatible append/upsert mode.
1363
+ The entire request is rejected; none of its documents are written.
1364
+ headers:
1365
+ Error-Message:
1366
+ description: Reason the request was rejected, also returned in the JSON message.
1367
+ schema:
1368
+ type: string
1369
+ content:
1370
+ application/json:
1371
+ schema:
1372
+ type: object
1373
+ required: [type, message]
1374
+ properties:
1375
+ type:
1376
+ type: string
1377
+ enum: [BadRequest]
1378
+ message:
1379
+ type: string
1380
+ example: {type: BadRequest, message: "data must be a list of documents, not int"}
1340
1381
  '500':
1341
1382
  $ref: '#/components/responses/ServerError'
1342
- description: Inserts documents (lz4+BSON or JSON). With `hasindex` (default) documents are upserted by the
1343
- database's primary key; without it they are appended.
1383
+ description: >-
1384
+ Accepts LZ4-compressed BSON {"data": [documents]} with Content-Encoding lz4,
1385
+ or a plain JSON list. A single document object is also accepted. BSON data
1386
+ equal to null or [] (and JSON null or []) is an empty batch: returns 201 with
1387
+ documents equal to zero without opening or changing the collection.
1388
+ With hasindex=true (default), every document must contain every field of
1389
+ the database's primary key and documents are upserted; with hasindex=false,
1390
+ they are appended and the collection must have no primary-key index.
1391
+ Missing keys reject the whole request, including otherwise valid documents.
1392
+ A real storage/server failure returns 500 and can leave partial writes;
1393
+ only validation errors guarantee that nothing was written.
1344
1394
  patch:
1345
1395
  tags:
1346
1396
  - Collections
@@ -1835,7 +1885,9 @@ paths:
1835
1885
  '403':
1836
1886
  description: Master token required
1837
1887
  description: 'Each route: `id`, `command` (its name on the index), `path`, `target`, `group`, `description`,
1838
- `enabled`, `revision`. Master token.'
1888
+ `enabled`, `revision`. The body also has `activation`: `applied_generation`, `desired_generation`,
1889
+ and `state` (`unpublished` before any activation status has been recorded; later states include
1890
+ `pending`, `applied`, `unconfirmed`, and `failed`). Master token.'
1839
1891
  post:
1840
1892
  tags:
1841
1893
  - Routes
@@ -1880,6 +1932,21 @@ paths:
1880
1932
  description: 'Adds a service behind nginx on this deploy''s port: requests to `path` are proxied to `target`
1881
1933
  (a private or local http(s) URL), behind the SharedData sign-in. nginx is checked and reloaded before the
1882
1934
  answer, so the route works when it returns; `reachable` says whether the target answered. Master token.'
1935
+ /api/routes/activation/verify:
1936
+ post:
1937
+ tags:
1938
+ - Routes
1939
+ summary: Check that every configured proxy is serving this generation (master token)
1940
+ responses:
1941
+ '200':
1942
+ description: 'activation: applied_generation, desired_generation, state, and the probe results'
1943
+ '400':
1944
+ description: SHAREDDATA_WEB_PROXY_PROBES is missing or a probe URL is not http(s) without credentials
1945
+ '403':
1946
+ description: Master token required
1947
+ description: 'Reads `SHAREDDATA_WEB_PROXY_PROBES` (a JSON object of proxy name to marker URL) and GETs each
1948
+ URL with a 3 second timeout and no redirects. A probe passes when the status is 200 and the stripped body equals
1949
+ `{generation}:{sha256 of the catalog}`. Master token.'
1883
1950
  /api/routes/{route_id}:
1884
1951
  parameters:
1885
1952
  - name: route_id
@@ -2952,6 +3019,68 @@ paths:
2952
3019
  description: Unknown command token
2953
3020
  '403':
2954
3021
  description: Not a master
3022
+ /api/workers/shared-tokens:
3023
+ get:
3024
+ tags:
3025
+ - Workers
3026
+ summary: List shared worker tokens (master)
3027
+ description: One token shared by a group of interchangeable workers. The list is the label, the name pattern,
3028
+ the creator and the last use. The secret is not included. Tokens that workers already presented are listed
3029
+ after the ones created here.
3030
+ responses:
3031
+ '200':
3032
+ description: Shared worker tokens without their secret
3033
+ '403':
3034
+ description: Not a master
3035
+ post:
3036
+ tags:
3037
+ - Workers
3038
+ summary: Create a shared worker token (master)
3039
+ description: Shown once. `names` is the worker-name pattern, with `*` for the part that changes, for example
3040
+ `*@SCALE-*`. At least three characters must be fixed. The label is letters, digits, dot, dash or underscore,
3041
+ up to 40 characters. Revoke the existing label before creating it again.
3042
+ requestBody:
3043
+ required: true
3044
+ content:
3045
+ application/json:
3046
+ schema:
3047
+ type: object
3048
+ required:
3049
+ - label
3050
+ - names
3051
+ properties:
3052
+ label:
3053
+ type: string
3054
+ example: scale
3055
+ names:
3056
+ type: string
3057
+ example: '*@SCALE-*'
3058
+ responses:
3059
+ '201':
3060
+ description: '`{token, label, names, enabled, created_at, created_by, last_seen_at}` — `token` only on this response'
3061
+ '400':
3062
+ description: Invalid label or names, or a token with that label already exists
3063
+ '403':
3064
+ description: Not a master
3065
+ /api/workers/shared-tokens/{label}:
3066
+ delete:
3067
+ tags:
3068
+ - Workers
3069
+ summary: Revoke a shared worker token (master)
3070
+ parameters:
3071
+ - name: label
3072
+ in: path
3073
+ required: true
3074
+ schema:
3075
+ type: string
3076
+ description: The token stops working (API processes cache tokens up to 5 minutes).
3077
+ responses:
3078
+ '200':
3079
+ description: Revoked
3080
+ '404':
3081
+ description: Unknown shared worker token
3082
+ '403':
3083
+ description: Not a master
2955
3084
  /api/data/sources:
2956
3085
  get:
2957
3086
  tags:
@@ -6,6 +6,7 @@ import datetime
6
6
  import gzip
7
7
  import json
8
8
  import time
9
+ import traceback
9
10
 
10
11
  import bson
11
12
  from bson import json_util
@@ -17,6 +18,7 @@ from flask import Blueprint, Response, jsonify, request
17
18
 
18
19
  from SharedData.CollectionMongoDB import CollectionMongoDB
19
20
  from SharedData.Database import DATABASE_PKEYS
21
+ from SharedData.Logger import Logger, redact_credentials
20
22
 
21
23
  from SharedData.API.constants import (
22
24
  DEFAULT_USER,
@@ -36,6 +38,73 @@ from SharedData.API.utils import (
36
38
  # Blueprint definition
37
39
  collections_bp = Blueprint('collections', __name__)
38
40
 
41
+
42
+ class InvalidPayload(ValueError):
43
+ """The posted body is not something that can be written: answered 400 with this message."""
44
+
45
+
46
+ def payload_error(error, status=400):
47
+ """A 4xx whose reason is in the Error-Message header (what ClientAPI reports) and the JSON body."""
48
+ message = redact_credentials(error).replace('\r', ' ').replace('\n', ' ')
49
+ response = make_error_response(message, status=status, error_type='BadRequest')
50
+ response.headers['Error-Message'] = message
51
+ return response
52
+
53
+
54
+ def decode_posted_documents(request):
55
+ """The documents of a collection POST, as a list of dicts.
56
+
57
+ None (what ClientAPI sent for an empty list before 6.86, issue #112) is an empty batch; a single
58
+ document is a batch of one. Anything else that is not a list of documents raises InvalidPayload."""
59
+ if not request.data:
60
+ raise InvalidPayload('empty request body: send {"data": [documents]} (lz4 BSON) or a JSON list')
61
+ lz4 = request.headers.get('Content-Encoding', '').lower() == 'lz4'
62
+ try:
63
+ if lz4:
64
+ payload = bson.decode(lz4f.decompress(request.data))
65
+ else:
66
+ payload = json.loads(request.data)
67
+ except Exception as e:
68
+ raise InvalidPayload(f'request body could not be decoded as {"lz4 BSON" if lz4 else "JSON"}: '
69
+ f'{type(e).__name__}: {e}')
70
+ if lz4:
71
+ if 'data' not in payload:
72
+ raise InvalidPayload("lz4 BSON body has no 'data' field")
73
+ documents = payload['data']
74
+ else:
75
+ documents = payload
76
+ if documents is None:
77
+ return []
78
+ if isinstance(documents, dict):
79
+ return [documents]
80
+ if not isinstance(documents, list):
81
+ raise InvalidPayload(f'data must be a list of documents, not {type(documents).__name__}')
82
+ for i, document in enumerate(documents):
83
+ if not isinstance(document, dict):
84
+ raise InvalidPayload(f'document {i} is {type(document).__name__}, not an object '
85
+ f'(data must be a list of documents)')
86
+ return documents
87
+
88
+
89
+ def missing_pkey_report(documents, pkey_columns):
90
+ """Why these documents cannot be upserted, or '' when every one has every primary-key field."""
91
+ missing = {}
92
+ incomplete = 0
93
+ first = None
94
+ for i, document in enumerate(documents):
95
+ absent = [field for field in pkey_columns if field not in document]
96
+ if absent:
97
+ incomplete += 1
98
+ first = i if first is None else first
99
+ for field in absent:
100
+ missing[field] = missing.get(field, 0) + 1
101
+ if not incomplete:
102
+ return ''
103
+ fields = ', '.join(f'{field} ({count})' for field, count in missing.items())
104
+ return (f'{incomplete}/{len(documents)} documents miss primary-key fields: {fields}; '
105
+ f'the primary key is {", ".join(pkey_columns)} (first incomplete document: {first}); '
106
+ 'nothing was written')
107
+
39
108
  def parse_json_query(query: dict) -> dict:
40
109
  """
41
110
  Recursively parses a MongoDB-like query dictionary, converting string representations
@@ -145,11 +214,15 @@ def init_collections_routes(shdata, authenticate_fn):
145
214
  return jsonify({'error': 'method not allowed'}), 405
146
215
 
147
216
  except Exception as e:
217
+ # A server-side failure: keep its stack in the server log, the client only gets the message.
218
+ Logger.log.error(redact_credentials(f'collection {request.method} {database}/{period}/{source}/{tablename}'
219
+ f'{"/" + request.args["tablesubfolder"] if request.args.get("tablesubfolder") else ""}: '
220
+ f'{type(e).__name__}: {e}\n{traceback.format_exc()}'))
148
221
  time.sleep(ERROR_SLEEP_SECONDS)
149
- response = Response(status=500)
150
- response.headers['Error-Message'] = str(e).replace('\n', ' ')
222
+ response = Response(status=500)
223
+ response.headers['Error-Message'] = redact_credentials(e).replace('\r', ' ').replace('\n', ' ')
151
224
  return response
152
-
225
+
153
226
  def head_collection(database: str, period: str, source: str, tablename: str, request):
154
227
  """Handle HTTP HEAD requests by returning metadata headers for a specified MongoDB collection."""
155
228
  user = request.args.get('user', DEFAULT_USER)
@@ -302,37 +375,49 @@ def init_collections_routes(shdata, authenticate_fn):
302
375
  response.headers['Content-Length'] = len(response_json)
303
376
  return with_cursor(response)
304
377
 
305
- def post_collection(database, period, source, tablename, request):
306
- """Handle a POST request to insert or upsert a collection of documents."""
378
+ def post_collection(database, period, source, tablename, request):
379
+ """Handle a POST request to insert or upsert a collection of documents.
380
+
381
+ Body: lz4-compressed BSON {"data": [documents]} (what ClientAPI sends) or a plain JSON list.
382
+ A payload that is not a list of documents, or (upsert) documents missing a primary-key
383
+ field, is a 400 saying why and nothing is written. An empty batch is nothing to write."""
307
384
  tablesubfolder = request.args.get('tablesubfolder')
308
385
  user = request.args.get('user', DEFAULT_USER)
309
386
  hasindex = parse_bool_param(request.args.get('hasindex'), True)
310
387
 
311
388
  tablepath = apply_subfolder(tablename, tablesubfolder)
312
389
 
313
- collection = shdata.collection(database, period, source, tablepath, user=user, hasindex=hasindex)
314
-
315
- if not request.data:
316
- Response({'message': 'No data'}, status=400)
317
-
318
- if request.headers.get('Content-Encoding', '').lower() == 'lz4':
319
- decompressed = lz4f.decompress(request.data)
320
- documents = bson.decode(decompressed)['data']
321
- else:
322
- documents = json.loads(request.data)
323
-
324
- if not isinstance(documents, list):
325
- Response({'message': 'Data must be a list'}, status=400)
326
-
327
- if hasindex:
328
- collection.upsert(documents)
329
- else:
330
- collection.extend(documents)
331
-
332
- response_data = json.dumps({'status': 'success'}).encode('utf-8')
390
+ try:
391
+ documents = decode_posted_documents(request)
392
+ except InvalidPayload as e:
393
+ return payload_error(e)
394
+
395
+ if documents:
396
+ # Opening a collection can create it or upgrade its indexes. An empty upload
397
+ # must not do either (nor fail building a unique index on append-only data).
398
+ collection = shdata.collection(database, period, source, tablepath, user=user, hasindex=hasindex)
399
+ if hasindex:
400
+ if not collection.hasindex:
401
+ return payload_error(
402
+ f'{collection.relpath} has no unique index on its primary key '
403
+ f'({", ".join(collection.pkey_columns)}), so it cannot be upserted; '
404
+ 'post with hasindex=False to append')
405
+ missing = missing_pkey_report(documents, collection.pkey_columns)
406
+ if missing:
407
+ return payload_error(missing)
408
+ collection.upsert(documents)
409
+ else:
410
+ if collection.hasindex:
411
+ return payload_error(
412
+ f'{collection.relpath} has a unique index on its primary key '
413
+ f'({", ".join(collection.pkey_columns)}), so documents cannot be appended; '
414
+ 'post with hasindex=True to upsert')
415
+ collection.extend(documents)
416
+
417
+ response_data = json.dumps({'status': 'success', 'documents': len(documents)}).encode('utf-8')
333
418
  response = Response(response_data, status=201, mimetype='application/json')
334
419
  response.headers['Content-Length'] = str(len(response_data))
335
- return response
420
+ return response
336
421
 
337
422
  def patch_collection(database, period, source, tablename, request):
338
423
  """Update a single document in a specified collection within the database."""
@@ -15,6 +15,7 @@ from flask import Blueprint, Response, jsonify, request
15
15
  from SharedData.CollectionMongoDB import CollectionMongoDB
16
16
  from SharedData.Database import DATABASE_PKEYS
17
17
  from SharedData.Table import TableNotFound
18
+ from SharedData.SharedNumpy import IndexNotAvailable
18
19
  from SharedData.TableRead import generation_read
19
20
  from SharedData.Listing import ListingBudget, ListingTimeout, ListingTooLarge
20
21
 
@@ -40,6 +41,15 @@ TABLE_LIST_SECONDS = 5.0
40
41
  TABLE_LIST_ENTRIES = 10000
41
42
 
42
43
 
44
+ def _require_filter_indexes(tbl, startdate, enddate, symbols, portfolios, tags):
45
+ """Validate filters before an empty response, without traversing their indexes."""
46
+ for kind, requested in (('date', startdate is not None or enddate is not None),
47
+ ('symbol', symbols is not None),
48
+ ('portfolio', portfolios is not None), ('tag', tags is not None)):
49
+ if requested:
50
+ tbl.require_index_for(kind)
51
+
52
+
43
53
  def init_tables_routes(shdata, authenticate_fn):
44
54
  global tables_bp
45
55
  tables_bp = Blueprint('tables', __name__)
@@ -125,6 +135,11 @@ def init_tables_routes(shdata, authenticate_fn):
125
135
  response.status_code = 404
126
136
  response.headers['Error-Message'] = str(e).replace('\n', ' ')
127
137
  return response
138
+ except IndexNotAvailable as e:
139
+ # An index filter (startdate/enddate/symbols/portfolios/tags) this table's key has no index for.
140
+ response = make_error_response(e, status=400, error_type='IndexNotAvailable')
141
+ response.headers['Error-Message'] = str(e).replace('\n', ' ')
142
+ return response
128
143
  except Exception as e:
129
144
  busy = table_busy_response(e)
130
145
  if busy is not None:
@@ -179,6 +194,14 @@ def init_tables_routes(shdata, authenticate_fn):
179
194
  tablepath = apply_subfolder(tablename, tablesubfolder)
180
195
  if last < 0:
181
196
  return jsonify({'error': 'last is a number of rows'}), 400
197
+ try:
198
+ startdate = pd.Timestamp(startdate) if startdate is not None else None
199
+ enddate = pd.Timestamp(enddate) if enddate is not None else None
200
+ except (ValueError, TypeError):
201
+ return jsonify({'error': 'startdate and enddate are dates, as 2026-10-07'}), 400
202
+ if (startdate is not None and pd.isna(startdate)) or (enddate is not None and pd.isna(enddate)):
203
+ return jsonify({'error': 'startdate and enddate are dates, as 2026-10-07'}), 400
204
+
182
205
  has_mtime = 'mtime' in (tbl.dtype.names or ())
183
206
  if by_since:
184
207
  try:
@@ -190,6 +213,7 @@ def init_tables_routes(shdata, authenticate_fn):
190
213
  since = since.tz_convert('UTC').tz_localize(None) if since.tzinfo is not None else since
191
214
  # Nothing was written to the table since then: answered without reading a row.
192
215
  if float(tbl.mtime) <= since.timestamp():
216
+ _require_filter_indexes(tbl, startdate, enddate, symbols, portfolios, tags)
193
217
  return make_empty_response(204)
194
218
  since = np.datetime64(since.value, 'ns')
195
219
  plain = not (startdate or enddate or symbols or portfolios or tags or query)
@@ -197,15 +221,11 @@ def init_tables_routes(shdata, authenticate_fn):
197
221
  loc = None
198
222
 
199
223
  if startdate is not None and enddate is not None:
200
- startdate = pd.Timestamp(startdate)
201
- enddate = pd.Timestamp(enddate)
202
224
  loc = tbl.get_date_loc_gte_lte(startdate, enddate)
203
225
  elif startdate is not None:
204
- startdate = pd.Timestamp(startdate)
205
- loc = tbl.get_date_loc_gte(startdate)
226
+ loc = tbl.get_date_loc_gte(startdate)
206
227
  elif enddate is not None:
207
- enddate = pd.Timestamp(enddate)
208
- loc = tbl.get_date_loc_lte(enddate)
228
+ loc = tbl.get_date_loc_lte(enddate)
209
229
  elif plain and by_since:
210
230
  loc = np.flatnonzero(np.asarray(tbl[:]['mtime']) > since) # one pass over one column
211
231
  since = None
@@ -215,8 +235,9 @@ def init_tables_routes(shdata, authenticate_fn):
215
235
  loc = np.arange(tbl.count)
216
236
 
217
237
  if len(loc) == 0:
238
+ _require_filter_indexes(tbl, startdate, enddate, symbols, portfolios, tags)
218
239
  return make_empty_response(204)
219
-
240
+
220
241
  # filter data by symbols
221
242
  if symbols is not None:
222
243
  symbols = symbols.split(',')
@@ -470,12 +491,12 @@ def init_tables_routes(shdata, authenticate_fn):
470
491
 
471
492
  if names is None or formats is None:
472
493
  tbl = shdata.table(database, period, source, tablepath,
473
- names=names, formats=formats, size=size,
494
+ names=names, formats=formats, size=size, grow=size is not None,
474
495
  overwrite=overwrite, user=user, value=value,
475
496
  hasindex=hasindex, is_schemaless=is_schemaless)
476
497
  else:
477
498
  tbl = shdata.table(database, period, source, tablepath,
478
- names=names, formats=formats, size=size,
499
+ names=names, formats=formats, size=size, grow=size is not None,
479
500
  overwrite=overwrite, user=user, value=None,
480
501
  hasindex=hasindex, is_schemaless=is_schemaless)
481
502
  if value is not None:
@@ -484,14 +505,6 @@ def init_tables_routes(shdata, authenticate_fn):
484
505
  else:
485
506
  tbl = tbl.extend(value)
486
507
 
487
- if size is not None:
488
- if tbl.size < size:
489
- tbl.free()
490
- tbl = shdata.table(database, period, source, tablepath,
491
- names=names, formats=formats, size=size,
492
- overwrite=overwrite, user=user,
493
- hasindex=hasindex, is_schemaless=is_schemaless)
494
-
495
508
  response = Response(status=201)
496
509
  response.headers['Content-Length'] = 0
497
510
  return response