ingestr 0.13.63__tar.gz → 0.13.64__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 ingestr might be problematic. Click here for more details.

Files changed (313) hide show
  1. {ingestr-0.13.63 → ingestr-0.13.64}/PKG-INFO +1 -1
  2. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/mssql.md +18 -0
  3. ingestr-0.13.64/docs/supported-sources/zoom.md +47 -0
  4. ingestr-0.13.64/ingestr/src/buildinfo.py +1 -0
  5. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/destinations.py +93 -1
  6. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/factory.py +1 -0
  7. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/sources.py +47 -2
  8. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zoom/__init__.py +45 -1
  9. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zoom/helpers.py +26 -0
  10. {ingestr-0.13.63 → ingestr-0.13.64}/styles/config/vocabularies/bruin/accept.txt +1 -0
  11. ingestr-0.13.63/docs/supported-sources/zoom.md +0 -34
  12. ingestr-0.13.63/ingestr/src/buildinfo.py +0 -1
  13. {ingestr-0.13.63 → ingestr-0.13.64}/.dockerignore +0 -0
  14. {ingestr-0.13.63 → ingestr-0.13.64}/.githooks/pre-commit-hook.sh +0 -0
  15. {ingestr-0.13.63 → ingestr-0.13.64}/.github/workflows/deploy-docs.yml +0 -0
  16. {ingestr-0.13.63 → ingestr-0.13.64}/.github/workflows/release.yml +0 -0
  17. {ingestr-0.13.63 → ingestr-0.13.64}/.github/workflows/secrets-scan.yml +0 -0
  18. {ingestr-0.13.63 → ingestr-0.13.64}/.github/workflows/tests.yml +0 -0
  19. {ingestr-0.13.63 → ingestr-0.13.64}/.gitignore +0 -0
  20. {ingestr-0.13.63 → ingestr-0.13.64}/.gitleaksignore +0 -0
  21. {ingestr-0.13.63 → ingestr-0.13.64}/.python-version +0 -0
  22. {ingestr-0.13.63 → ingestr-0.13.64}/.vale.ini +0 -0
  23. {ingestr-0.13.63 → ingestr-0.13.64}/Dockerfile +0 -0
  24. {ingestr-0.13.63 → ingestr-0.13.64}/LICENSE.md +0 -0
  25. {ingestr-0.13.63 → ingestr-0.13.64}/Makefile +0 -0
  26. {ingestr-0.13.63 → ingestr-0.13.64}/README.md +0 -0
  27. {ingestr-0.13.63 → ingestr-0.13.64}/docs/.vitepress/config.mjs +0 -0
  28. {ingestr-0.13.63 → ingestr-0.13.64}/docs/.vitepress/theme/custom.css +0 -0
  29. {ingestr-0.13.63 → ingestr-0.13.64}/docs/.vitepress/theme/index.js +0 -0
  30. {ingestr-0.13.63 → ingestr-0.13.64}/docs/commands/example-uris.md +0 -0
  31. {ingestr-0.13.63 → ingestr-0.13.64}/docs/commands/ingest.md +0 -0
  32. {ingestr-0.13.63 → ingestr-0.13.64}/docs/getting-started/core-concepts.md +0 -0
  33. {ingestr-0.13.63 → ingestr-0.13.64}/docs/getting-started/incremental-loading.md +0 -0
  34. {ingestr-0.13.63 → ingestr-0.13.64}/docs/getting-started/quickstart.md +0 -0
  35. {ingestr-0.13.63 → ingestr-0.13.64}/docs/getting-started/telemetry.md +0 -0
  36. {ingestr-0.13.63 → ingestr-0.13.64}/docs/index.md +0 -0
  37. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/applovin_max.png +0 -0
  38. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/athena.png +0 -0
  39. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/clickhouse_img.png +0 -0
  40. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/cratedb-destination.png +0 -0
  41. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/cratedb-source.png +0 -0
  42. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/freshdesk_ingestion.png +0 -0
  43. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/gcp_spanner_ingestion.png +0 -0
  44. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/github.png +0 -0
  45. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/google_analytics_realtime_report.png +0 -0
  46. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/googleanalytics.png +0 -0
  47. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/ingestion_elasticsearch_img.png +0 -0
  48. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/kinesis.bigquery.png +0 -0
  49. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/linear.png +0 -0
  50. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/linkedin_ads.png +0 -0
  51. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/mixpanel_ingestion.png +0 -0
  52. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/personio.png +0 -0
  53. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/personio_duckdb.png +0 -0
  54. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/phantombuster.png +0 -0
  55. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/pipedrive.png +0 -0
  56. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/quickbook_ingestion.png +0 -0
  57. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/sftp.png +0 -0
  58. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/stripe_postgres.png +0 -0
  59. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/tiktok.png +0 -0
  60. {ingestr-0.13.63 → ingestr-0.13.64}/docs/media/zoom_ingestion.png +0 -0
  61. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/adjust.md +0 -0
  62. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/airtable.md +0 -0
  63. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/applovin.md +0 -0
  64. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/applovin_max.md +0 -0
  65. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/appsflyer.md +0 -0
  66. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/appstore.md +0 -0
  67. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/asana.md +0 -0
  68. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/athena.md +0 -0
  69. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/attio.md +0 -0
  70. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/bigquery.md +0 -0
  71. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/chess.md +0 -0
  72. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/clickhouse.md +0 -0
  73. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/cratedb.md +0 -0
  74. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/csv.md +0 -0
  75. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/custom_queries.md +0 -0
  76. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/databricks.md +0 -0
  77. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/db2.md +0 -0
  78. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/duckdb.md +0 -0
  79. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/dynamodb.md +0 -0
  80. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/elasticsearch.md +0 -0
  81. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/facebook-ads.md +0 -0
  82. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/frankfurter.md +0 -0
  83. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/freshdesk.md +0 -0
  84. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/gcs.md +0 -0
  85. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/github.md +0 -0
  86. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/google-ads.md +0 -0
  87. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/google_analytics.md +0 -0
  88. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/gorgias.md +0 -0
  89. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/gsheets.md +0 -0
  90. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/hubspot.md +0 -0
  91. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/isoc-pulse.md +0 -0
  92. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/kafka.md +0 -0
  93. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/kinesis.md +0 -0
  94. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/klaviyo.md +0 -0
  95. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/linear.md +0 -0
  96. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/linkedin_ads.md +0 -0
  97. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/mixpanel.md +0 -0
  98. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/mongodb.md +0 -0
  99. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/mysql.md +0 -0
  100. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/notion.md +0 -0
  101. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/oracle.md +0 -0
  102. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/personio.md +0 -0
  103. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/phantombuster.md +0 -0
  104. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/pinterest.md +0 -0
  105. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/pipedrive.md +0 -0
  106. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/postgres.md +0 -0
  107. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/quickbooks.md +0 -0
  108. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/redshift.md +0 -0
  109. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/s3.md +0 -0
  110. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/salesforce.md +0 -0
  111. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/sap-hana.md +0 -0
  112. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/sftp.md +0 -0
  113. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/shopify.md +0 -0
  114. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/slack.md +0 -0
  115. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/smartsheets.md +0 -0
  116. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/snowflake.md +0 -0
  117. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/solidgate.md +0 -0
  118. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/spanner.md +0 -0
  119. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/sqlite.md +0 -0
  120. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/stripe.md +0 -0
  121. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/tiktok-ads.md +0 -0
  122. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/trustpilot.md +0 -0
  123. {ingestr-0.13.63 → ingestr-0.13.64}/docs/supported-sources/zendesk.md +0 -0
  124. {ingestr-0.13.63 → ingestr-0.13.64}/docs/tutorials/load-kinesis-bigquery.md +0 -0
  125. {ingestr-0.13.63 → ingestr-0.13.64}/docs/tutorials/load-personio-duckdb.md +0 -0
  126. {ingestr-0.13.63 → ingestr-0.13.64}/docs/tutorials/load-stripe-postgres.md +0 -0
  127. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/conftest.py +0 -0
  128. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/main.py +0 -0
  129. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/.gitignore +0 -0
  130. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/adjust/__init__.py +0 -0
  131. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/adjust/adjust_helpers.py +0 -0
  132. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/airtable/__init__.py +0 -0
  133. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/applovin/__init__.py +0 -0
  134. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/applovin_max/__init__.py +0 -0
  135. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/appsflyer/__init__.py +0 -0
  136. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/appsflyer/client.py +0 -0
  137. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/appstore/__init__.py +0 -0
  138. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/appstore/client.py +0 -0
  139. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/appstore/errors.py +0 -0
  140. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/appstore/models.py +0 -0
  141. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/appstore/resources.py +0 -0
  142. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/arrow/__init__.py +0 -0
  143. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/asana_source/__init__.py +0 -0
  144. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/asana_source/helpers.py +0 -0
  145. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/asana_source/settings.py +0 -0
  146. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/attio/__init__.py +0 -0
  147. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/attio/helpers.py +0 -0
  148. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/blob.py +0 -0
  149. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/chess/__init__.py +0 -0
  150. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/chess/helpers.py +0 -0
  151. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/chess/settings.py +0 -0
  152. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/collector/spinner.py +0 -0
  153. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/dynamodb/__init__.py +0 -0
  154. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/elasticsearch/__init__.py +0 -0
  155. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/errors.py +0 -0
  156. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/facebook_ads/__init__.py +0 -0
  157. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/facebook_ads/exceptions.py +0 -0
  158. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/facebook_ads/helpers.py +0 -0
  159. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/facebook_ads/settings.py +0 -0
  160. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/facebook_ads/utils.py +0 -0
  161. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/filesystem/__init__.py +0 -0
  162. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/filesystem/helpers.py +0 -0
  163. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/filesystem/readers.py +0 -0
  164. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/filters.py +0 -0
  165. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/frankfurter/__init__.py +0 -0
  166. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/frankfurter/helpers.py +0 -0
  167. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/freshdesk/__init__.py +0 -0
  168. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/freshdesk/freshdesk_client.py +0 -0
  169. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/freshdesk/settings.py +0 -0
  170. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/github/__init__.py +0 -0
  171. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/github/helpers.py +0 -0
  172. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/github/queries.py +0 -0
  173. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/github/settings.py +0 -0
  174. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_ads/__init__.py +0 -0
  175. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_ads/field.py +0 -0
  176. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_ads/metrics.py +0 -0
  177. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_ads/predicates.py +0 -0
  178. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_ads/reports.py +0 -0
  179. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_analytics/__init__.py +0 -0
  180. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_analytics/helpers.py +0 -0
  181. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_sheets/README.md +0 -0
  182. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_sheets/__init__.py +0 -0
  183. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_sheets/helpers/__init__.py +0 -0
  184. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_sheets/helpers/api_calls.py +0 -0
  185. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/google_sheets/helpers/data_processing.py +0 -0
  186. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/gorgias/__init__.py +0 -0
  187. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/gorgias/helpers.py +0 -0
  188. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/http_client.py +0 -0
  189. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/hubspot/__init__.py +0 -0
  190. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/hubspot/helpers.py +0 -0
  191. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/hubspot/settings.py +0 -0
  192. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/isoc_pulse/__init__.py +0 -0
  193. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/kafka/__init__.py +0 -0
  194. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/kafka/helpers.py +0 -0
  195. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/kinesis/__init__.py +0 -0
  196. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/kinesis/helpers.py +0 -0
  197. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/klaviyo/__init__.py +0 -0
  198. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/klaviyo/client.py +0 -0
  199. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/klaviyo/helpers.py +0 -0
  200. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/linear/__init__.py +0 -0
  201. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/linkedin_ads/__init__.py +0 -0
  202. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/linkedin_ads/dimension_time_enum.py +0 -0
  203. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/linkedin_ads/helpers.py +0 -0
  204. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/loader.py +0 -0
  205. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/mixpanel/__init__.py +0 -0
  206. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/mixpanel/client.py +0 -0
  207. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/mongodb/__init__.py +0 -0
  208. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/mongodb/helpers.py +0 -0
  209. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/notion/__init__.py +0 -0
  210. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/notion/helpers/__init__.py +0 -0
  211. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/notion/helpers/client.py +0 -0
  212. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/notion/helpers/database.py +0 -0
  213. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/notion/settings.py +0 -0
  214. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/partition.py +0 -0
  215. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/personio/__init__.py +0 -0
  216. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/personio/helpers.py +0 -0
  217. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/phantombuster/__init__.py +0 -0
  218. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/phantombuster/client.py +0 -0
  219. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/pinterest/__init__.py +0 -0
  220. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/pipedrive/__init__.py +0 -0
  221. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/pipedrive/helpers/__init__.py +0 -0
  222. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/pipedrive/helpers/custom_fields_munger.py +0 -0
  223. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/pipedrive/helpers/pages.py +0 -0
  224. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/pipedrive/settings.py +0 -0
  225. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/pipedrive/typing.py +0 -0
  226. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/quickbooks/__init__.py +0 -0
  227. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/resource.py +0 -0
  228. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/salesforce/__init__.py +0 -0
  229. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/salesforce/helpers.py +0 -0
  230. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/shopify/__init__.py +0 -0
  231. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/shopify/exceptions.py +0 -0
  232. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/shopify/helpers.py +0 -0
  233. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/shopify/settings.py +0 -0
  234. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/slack/__init__.py +0 -0
  235. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/slack/helpers.py +0 -0
  236. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/slack/settings.py +0 -0
  237. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/smartsheets/__init__.py +0 -0
  238. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/solidgate/__init__.py +0 -0
  239. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/solidgate/helpers.py +0 -0
  240. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/sql_database/__init__.py +0 -0
  241. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/sql_database/callbacks.py +0 -0
  242. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/stripe_analytics/__init__.py +0 -0
  243. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/stripe_analytics/helpers.py +0 -0
  244. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/stripe_analytics/settings.py +0 -0
  245. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/table_definition.py +0 -0
  246. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/telemetry/event.py +0 -0
  247. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/testdata/fakebqcredentials.json +0 -0
  248. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/tiktok_ads/__init__.py +0 -0
  249. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/tiktok_ads/tiktok_helpers.py +0 -0
  250. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/time.py +0 -0
  251. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/trustpilot/__init__.py +0 -0
  252. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/trustpilot/client.py +0 -0
  253. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/version.py +0 -0
  254. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zendesk/__init__.py +0 -0
  255. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zendesk/helpers/__init__.py +0 -0
  256. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zendesk/helpers/api_helpers.py +0 -0
  257. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zendesk/helpers/credentials.py +0 -0
  258. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zendesk/helpers/talk_api.py +0 -0
  259. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/src/zendesk/settings.py +0 -0
  260. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/.gitignore +0 -0
  261. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/create_replace.csv +0 -0
  262. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/delete_insert_expected.csv +0 -0
  263. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/delete_insert_part1.csv +0 -0
  264. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/delete_insert_part2.csv +0 -0
  265. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/merge_expected.csv +0 -0
  266. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/merge_part1.csv +0 -0
  267. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/testdata/merge_part2.csv +0 -0
  268. {ingestr-0.13.63 → ingestr-0.13.64}/ingestr/tests/unit/test_smartsheets.py +0 -0
  269. {ingestr-0.13.63 → ingestr-0.13.64}/package-lock.json +0 -0
  270. {ingestr-0.13.63 → ingestr-0.13.64}/package.json +0 -0
  271. {ingestr-0.13.63 → ingestr-0.13.64}/pyproject.toml +0 -0
  272. {ingestr-0.13.63 → ingestr-0.13.64}/requirements-dev.txt +0 -0
  273. {ingestr-0.13.63 → ingestr-0.13.64}/requirements.in +0 -0
  274. {ingestr-0.13.63 → ingestr-0.13.64}/requirements.txt +0 -0
  275. {ingestr-0.13.63 → ingestr-0.13.64}/requirements_arm64.txt +0 -0
  276. {ingestr-0.13.63 → ingestr-0.13.64}/resources/demo.gif +0 -0
  277. {ingestr-0.13.63 → ingestr-0.13.64}/resources/demo.tape +0 -0
  278. {ingestr-0.13.63 → ingestr-0.13.64}/resources/ingestr.svg +0 -0
  279. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/AMPM.yml +0 -0
  280. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Acronyms.yml +0 -0
  281. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Colons.yml +0 -0
  282. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Contractions.yml +0 -0
  283. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/DateFormat.yml +0 -0
  284. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Ellipses.yml +0 -0
  285. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/EmDash.yml +0 -0
  286. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Exclamation.yml +0 -0
  287. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/FirstPerson.yml +0 -0
  288. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Gender.yml +0 -0
  289. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/GenderBias.yml +0 -0
  290. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/HeadingPunctuation.yml +0 -0
  291. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Headings.yml +0 -0
  292. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Latin.yml +0 -0
  293. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/LyHyphens.yml +0 -0
  294. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/OptionalPlurals.yml +0 -0
  295. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Ordinal.yml +0 -0
  296. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/OxfordComma.yml +0 -0
  297. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Parens.yml +0 -0
  298. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Passive.yml +0 -0
  299. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Periods.yml +0 -0
  300. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Quotes.yml +0 -0
  301. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Ranges.yml +0 -0
  302. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Semicolons.yml +0 -0
  303. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Slang.yml +0 -0
  304. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Spacing.yml +0 -0
  305. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Spelling.yml +0 -0
  306. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Units.yml +0 -0
  307. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/We.yml +0 -0
  308. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/Will.yml +0 -0
  309. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/WordList.yml +0 -0
  310. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/meta.json +0 -0
  311. {ingestr-0.13.63 → ingestr-0.13.64}/styles/Google/vocab.txt +0 -0
  312. {ingestr-0.13.63 → ingestr-0.13.64}/styles/bruin/Ingestr.yml +0 -0
  313. {ingestr-0.13.63 → ingestr-0.13.64}/test.env.template +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.63
3
+ Version: 0.13.64
4
4
  Summary: ingestr is a command-line application that ingests data from various sources and stores them in any database.
5
5
  Project-URL: Homepage, https://github.com/bruin-data/ingestr
6
6
  Project-URL: Issues, https://github.com/bruin-data/ingestr/issues
@@ -28,3 +28,21 @@ URI parameters:
28
28
  - `TrustServerCertificate`: whether to trust the server certificate
29
29
 
30
30
  The same URI structure can be used both for sources and destinations. You can read more about SQLAlchemy's SQL Server dialect [here](https://docs.sqlalchemy.org/en/20/core/engines.html#microsoft-sql-server).
31
+
32
+ ## Tips & Tricks
33
+
34
+ If you're using Azure SQL Server, you can use `az cli` to generate access tokens to connect to SQL server.
35
+
36
+ Set the password to your token and the `Authentication` parameter to `ActiveDirectoryAccessToken`
37
+ ::: code-group
38
+
39
+ ```sh [token-auth-example.sh]
40
+ USER=$(az account show --query user.name -o tsv)
41
+ TOKEN=$(az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv)
42
+ ingestr ingest \
43
+ --source-uri "mssql://$USER:$TOKEN@<server>.database.windows.net/<database>?Authentication=ActiveDirectoryAccessToken" \
44
+ --source-table "dbo.example" \
45
+ --dest-uri "duckdb:///example.db" \
46
+ --dest-table "dbo.example" \
47
+ ```
48
+ :::
@@ -0,0 +1,47 @@
1
+ # Zoom
2
+
3
+ [Zoom](https://www.zoom.com/) is a video conferencing and collaboration platform.
4
+
5
+ ingestr supports Zoom as a source.
6
+
7
+ ## Prerequisites
8
+ - A [Zoom Server-to-Server OAuth App](https://developers.zoom.us/docs/internal-apps/s2s-oauth/)
9
+ - Appropriate permissions related to meetings, users and participants must be added in the app's scopes
10
+ `user:read,user:write,user:read:admin,user:write:admin`
11
+ - Obtain the `client_id`, `client_secret` and `account_id` credentials from the app
12
+
13
+ ## URI format
14
+ ```plaintext
15
+ zoom://?client_id=<client_id>&client_secret=<client_secret>&account_id=<account_id>
16
+ ```
17
+
18
+ This command copies meetings data from the Zoom source to DuckDB.
19
+
20
+ ```sh
21
+ ingestr ingest \
22
+ --source-uri 'zoom://?client_id=abc&client_secret=xyz&account_id=123' \
23
+ --source-table 'meetings' \
24
+ --dest-uri duckdb:///zoom.duckdb \
25
+ --dest-table 'dest.meetings'
26
+ ```
27
+
28
+ <img alt="zoom" src="../media/zoom_ingestion.png"/>
29
+ ## Tables
30
+
31
+ Zoom source allows ingesting the following tables:
32
+ - `meetings`: Retrieve all valid previous meetings, live meetings, and upcoming scheduled meetings for all users in the given Zoom account.
33
+ - Permissions required: meeting:read:admin,meeting:read
34
+ - Granular permissions: meeting:read:list_meetings,meeting:read:list_meetings:admin
35
+
36
+ - `users`: Retrieve a list of users in your account.
37
+ - Permissions required: user:read, user:write, user:read:admin, user:write:admin.
38
+ - Granular permissions: user:read:list_users:admin.
39
+ - Prerequisites: A Pro or higher plan.
40
+
41
+ - `participants`: Return a report of a past meeting that had participants, including the host.
42
+ It only returns data for meetings within the last 6 months.
43
+ - Permissions required: report:read:admin.
44
+ - Granular permissions: report:read:list_meeting_participants:admin.
45
+ - Prerequisites: A Pro or higher plan.
46
+
47
+ Use these as the `--source-table` parameter in the `ingestr ingest` command.
@@ -0,0 +1 @@
1
+ version = "v0.13.64"
@@ -1,19 +1,31 @@
1
1
  import abc
2
2
  import base64
3
3
  import csv
4
+ import datetime
4
5
  import json
5
6
  import os
6
7
  import shutil
8
+ import struct
7
9
  import tempfile
8
10
  from urllib.parse import parse_qs, quote, urlparse
9
11
 
10
12
  import dlt
11
13
  import dlt.destinations.impl.filesystem.filesystem
12
14
  from dlt.common.configuration.specs import AwsCredentials
15
+ from dlt.common.destination.capabilities import DestinationCapabilitiesContext
16
+ from dlt.common.schema import Schema
13
17
  from dlt.common.storages.configuration import FileSystemCredentials
14
18
  from dlt.destinations.impl.clickhouse.configuration import (
15
19
  ClickHouseCredentials,
16
20
  )
21
+ from dlt.destinations.impl.mssql.configuration import MsSqlClientConfiguration
22
+ from dlt.destinations.impl.mssql.mssql import (
23
+ HINT_TO_MSSQL_ATTR,
24
+ MsSqlJobClient,
25
+ )
26
+ from dlt.destinations.impl.mssql.sql_client import (
27
+ PyOdbcMsSqlClient,
28
+ )
17
29
 
18
30
  from ingestr.src.errors import MissingValueError
19
31
  from ingestr.src.loader import load_dlt_file
@@ -143,9 +155,89 @@ class DuckDBDestination(GenericSqlDestination):
143
155
  return dlt.destinations.duckdb(uri, **kwargs)
144
156
 
145
157
 
158
+ def handle_datetimeoffset(dto_value: bytes) -> datetime.datetime:
159
+ # ref: https://github.com/mkleehammer/pyodbc/issues/134#issuecomment-281739794
160
+ tup = struct.unpack(
161
+ "<6hI2h", dto_value
162
+ ) # e.g., (2017, 3, 16, 10, 35, 18, 500000000, -6, 0)
163
+ return datetime.datetime(
164
+ tup[0],
165
+ tup[1],
166
+ tup[2],
167
+ tup[3],
168
+ tup[4],
169
+ tup[5],
170
+ tup[6] // 1000,
171
+ datetime.timezone(datetime.timedelta(hours=tup[7], minutes=tup[8])),
172
+ )
173
+
174
+
175
+ class OdbcMsSqlClient(PyOdbcMsSqlClient):
176
+ SQL_COPT_SS_ACCESS_TOKEN = 1256
177
+ SKIP_CREDENTIALS = {"PWD", "AUTHENTICATION", "UID"}
178
+
179
+ def open_connection(self):
180
+ cfg = self.credentials._get_odbc_dsn_dict()
181
+ if (
182
+ cfg.get("AUTHENTICATION", "").strip().lower()
183
+ != "activedirectoryaccesstoken"
184
+ ):
185
+ return super().open_connection()
186
+
187
+ import pyodbc # type: ignore
188
+
189
+ dsn = ";".join(
190
+ [f"{k}={v}" for k, v in cfg.items() if k not in self.SKIP_CREDENTIALS]
191
+ )
192
+
193
+ self._conn = pyodbc.connect(
194
+ dsn,
195
+ timeout=self.credentials.connect_timeout,
196
+ attrs_before={
197
+ self.SQL_COPT_SS_ACCESS_TOKEN: self.serialize_token(cfg["PWD"]),
198
+ },
199
+ )
200
+
201
+ # https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
202
+ self._conn.add_output_converter(-155, handle_datetimeoffset)
203
+ self._conn.autocommit = True
204
+ return self._conn
205
+
206
+ def serialize_token(self, token):
207
+ # https://github.com/mkleehammer/pyodbc/issues/228#issuecomment-494773723
208
+ encoded = token.encode("utf_16_le")
209
+ return struct.pack("<i", len(encoded)) + encoded
210
+
211
+
212
+ class MsSqlClient(MsSqlJobClient):
213
+ def __init__(
214
+ self,
215
+ schema: Schema,
216
+ config: MsSqlClientConfiguration,
217
+ capabilities: DestinationCapabilitiesContext,
218
+ ) -> None:
219
+ sql_client = OdbcMsSqlClient(
220
+ config.normalize_dataset_name(schema),
221
+ config.normalize_staging_dataset_name(schema),
222
+ config.credentials,
223
+ capabilities,
224
+ )
225
+ super(MsSqlJobClient, self).__init__(schema, config, sql_client)
226
+ self.config: MsSqlClientConfiguration = config
227
+ self.sql_client = sql_client
228
+ self.active_hints = HINT_TO_MSSQL_ATTR if self.config.create_indexes else {}
229
+ self.type_mapper = capabilities.get_type_mapper()
230
+
231
+
232
+ class MsSqlDestImpl(dlt.destinations.mssql):
233
+ @property
234
+ def client_class(self):
235
+ return MsSqlClient
236
+
237
+
146
238
  class MsSQLDestination(GenericSqlDestination):
147
239
  def dlt_dest(self, uri: str, **kwargs):
148
- return dlt.destinations.mssql(credentials=uri, **kwargs)
240
+ return MsSqlDestImpl(credentials=uri, **kwargs)
149
241
 
150
242
 
151
243
  class DatabricksDestination(GenericSqlDestination):
@@ -79,6 +79,7 @@ SQL_SOURCE_SCHEMES = [
79
79
  "crate",
80
80
  "duckdb",
81
81
  "mssql",
82
+ "mssql+pyodbc",
82
83
  "mysql",
83
84
  "mysql+pymysql",
84
85
  "mysql+mysqlconnector",
@@ -258,8 +258,53 @@ class SqlSource:
258
258
  # override the query adapters, the only one we want is the one here in the case of custom queries
259
259
  query_adapters = [custom_query_variable_subsitution(query_value, kwargs)]
260
260
 
261
+ credentials = ConnectionStringCredentials(uri)
262
+ if uri.startswith("mssql://"):
263
+ parsed_uri = urlparse(uri)
264
+ params = parse_qs(parsed_uri.query)
265
+ params = {k.lower(): v for k, v in params.items()}
266
+ if params.get("authentication") == ["ActiveDirectoryAccessToken"]:
267
+ import pyodbc # type: ignore
268
+ from sqlalchemy import create_engine
269
+
270
+ from ingestr.src.destinations import (
271
+ OdbcMsSqlClient,
272
+ handle_datetimeoffset,
273
+ )
274
+
275
+ cfg = {
276
+ "DRIVER": params.get("driver", ["ODBC Driver 18 for SQL Server"])[
277
+ 0
278
+ ],
279
+ "SERVER": f"{parsed_uri.hostname},{parsed_uri.port or 1433}",
280
+ "DATABASE": parsed_uri.path.lstrip("/"),
281
+ }
282
+ for k, v in params.items():
283
+ if k.lower() not in ["driver", "authentication", "connect_timeout"]:
284
+ cfg[k.upper()] = v[0]
285
+
286
+ token = OdbcMsSqlClient.serialize_token(None, parsed_uri.password) # type: ignore[arg-type]
287
+ dsn = ";".join([f"{k}={v}" for k, v in cfg.items()])
288
+
289
+ def creator():
290
+ connection = pyodbc.connect(
291
+ dsn,
292
+ autocommit=True,
293
+ timeout=kwargs.get("connect_timeout", 30),
294
+ attrs_before={
295
+ OdbcMsSqlClient.SQL_COPT_SS_ACCESS_TOKEN: token,
296
+ },
297
+ )
298
+ connection.add_output_converter(-155, handle_datetimeoffset)
299
+ return connection
300
+
301
+ credentials = create_engine(
302
+ "mssql+pyodbc://",
303
+ creator=creator,
304
+ )
305
+
261
306
  builder_res = self.table_builder(
262
- credentials=ConnectionStringCredentials(uri),
307
+ credentials=credentials,
263
308
  schema=table_fields.dataset,
264
309
  table=table_fields.table,
265
310
  incremental=incremental,
@@ -2915,7 +2960,7 @@ class ZoomSource:
2915
2960
 
2916
2961
  from ingestr.src.zoom import zoom_source
2917
2962
 
2918
- if table not in {"meetings"}:
2963
+ if table not in {"meetings", "users", "participants"}:
2919
2964
  raise UnsupportedResourceError(table, "Zoom")
2920
2965
 
2921
2966
  return zoom_source(
@@ -42,14 +42,58 @@ def zoom_source(
42
42
  end_dt = pendulum.now("UTC")
43
43
  else:
44
44
  end_dt = pendulum.parse(datetime.end_value)
45
+
45
46
  base_params: Dict[str, Any] = {
46
47
  "type": "scheduled",
47
48
  "page_size": 300,
48
49
  "from": start_dt.to_date_string(),
49
50
  "to": end_dt.to_date_string(),
50
51
  }
52
+
51
53
  for user in client.get_users():
52
54
  user_id = user["id"]
53
55
  yield from client.get_meetings(user_id, base_params)
54
56
 
55
- return meetings
57
+ @dlt.resource(write_disposition="merge", primary_key="id")
58
+ def users() -> Iterable[TDataItem]:
59
+ yield from client.get_users()
60
+
61
+ @dlt.resource(write_disposition="merge", primary_key="id")
62
+ def participants(
63
+ datetime: dlt.sources.incremental[TAnyDateTime] = dlt.sources.incremental(
64
+ "join_time",
65
+ initial_value=start_date.isoformat(),
66
+ end_value=end_date.isoformat() if end_date is not None else None,
67
+ range_start="closed",
68
+ range_end="closed",
69
+ ),
70
+ ) -> Iterable[TDataItem]:
71
+ if datetime.last_value:
72
+ start_dt = pendulum.parse(datetime.last_value)
73
+ else:
74
+ start_dt = pendulum.parse(start_date)
75
+
76
+ if end_date is None:
77
+ end_dt = pendulum.now("UTC")
78
+ else:
79
+ end_dt = pendulum.parse(datetime.end_value)
80
+
81
+ participant_params: Dict[str, Any] = {
82
+ "page_size": 300,
83
+ }
84
+ meeting_params = {
85
+ "type": "previous_meetings",
86
+ "page_size": 300,
87
+ }
88
+ for user in client.get_users():
89
+ user_id = user["id"]
90
+ for meeting in client.get_meetings(user_id=user_id, params=meeting_params):
91
+ meeting_id = meeting["id"]
92
+ yield from client.get_participants(
93
+ meeting_id=meeting_id,
94
+ params=participant_params,
95
+ start_date=start_dt,
96
+ end_date=end_dt,
97
+ )
98
+
99
+ return meetings, users, participants
@@ -1,6 +1,8 @@
1
1
  import time
2
2
  from typing import Any, Dict, Iterator, Optional
3
3
 
4
+ import pendulum
5
+
4
6
  from ingestr.src.http_client import create_client
5
7
 
6
8
 
@@ -47,6 +49,7 @@ class ZoomClient:
47
49
 
48
50
  def get_users(self) -> Iterator[Dict[str, Any]]:
49
51
  url = f"{self.base_url}/users"
52
+
50
53
  params = {"page_size": 1000}
51
54
  while True:
52
55
  response = self.session.get(url, headers=self._headers(), params=params)
@@ -59,6 +62,7 @@ class ZoomClient:
59
62
  break
60
63
  params["next_page_token"] = token
61
64
 
65
+ #https://developers.zoom.us/docs/api/rest/reference/zoom-api/methods/#operation/meetings
62
66
  def get_meetings(
63
67
  self, user_id: str, params: Dict[str, Any]
64
68
  ) -> Iterator[Dict[str, Any]]:
@@ -74,3 +78,25 @@ class ZoomClient:
74
78
  if not token:
75
79
  break
76
80
  params["next_page_token"] = token
81
+
82
+ # https://developers.zoom.us/docs/api/rest/reference/zoom-api/methods/#operation/reportMeetingParticipants
83
+ def get_participants(
84
+ self,
85
+ meeting_id: str,
86
+ params: Dict[str, Any],
87
+ start_date: pendulum.DateTime,
88
+ end_date: pendulum.DateTime,
89
+ ) -> Iterator[Dict[str, Any]]:
90
+ url = f"{self.base_url}/report/meetings/{meeting_id}/participants"
91
+ while True:
92
+ response = self.session.get(url, headers=self._headers(), params=params)
93
+ response.raise_for_status()
94
+ data = response.json()
95
+ for item in data.get("participants", []):
96
+ join_time = pendulum.parse(item["join_time"])
97
+ if join_time >= start_date and join_time <= end_date:
98
+ yield item
99
+ token = data.get("next_page_token")
100
+ if not token:
101
+ break
102
+ params["next_page_token"] = token
@@ -1,3 +1,4 @@
1
+ CrateDB
1
2
  ingestr
2
3
  URIs
3
4
  PostgreSQL
@@ -1,34 +0,0 @@
1
- # Zoom
2
-
3
- [Zoom](https://www.zoom.com/) is a video conferencing and collaboration platform.
4
-
5
- ingestr supports Zoom as a source.
6
-
7
- ## Prerequisites
8
- - A [Zoom Server-to-Server OAuth App](https://developers.zoom.us/docs/internal-apps/s2s-oauth/)
9
- - Appropriate permissions related to meetings and users must be added in the app's scopes
10
- - Obtain the `client_id`, `client_secret` and `account_id` credentials from the app
11
-
12
- ## URI format
13
- ```plaintext
14
- zoom://?client_id=<client_id>&client_secret=<client_secret>&account_id=<account_id>
15
- ```
16
-
17
- This command copies meetings data from the Zoom source to DuckDB.
18
-
19
- ```sh
20
- ingestr ingest \
21
- --source-uri 'zoom://?client_id=abc&client_secret=xyz&account_id=123' \
22
- --source-table 'meetings' \
23
- --dest-uri duckdb:///zoom.duckdb \
24
- --dest-table 'dest.meetings'
25
- ```
26
-
27
- <img alt="zoom" src="../media/zoom_ingestion.png"/>
28
- ## Tables
29
-
30
- Zoom source allows ingesting the following tables:
31
-
32
- - `meetings`: Retrieves all valid previous meetings, live meetings, and upcoming scheduled meetings for all users of the given Zoom account.
33
-
34
- Use these as the `--source-table` parameter in the `ingestr ingest` command.
@@ -1 +0,0 @@
1
- version = "v0.13.63"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes