ingestr 0.13.36__tar.gz → 0.13.38__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 (270) hide show
  1. {ingestr-0.13.36 → ingestr-0.13.38}/PKG-INFO +2 -2
  2. {ingestr-0.13.36 → ingestr-0.13.38}/docs/.vitepress/config.mjs +2 -0
  3. ingestr-0.13.38/docs/media/phantombuster.png +0 -0
  4. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/frankfurter.md +21 -17
  5. ingestr-0.13.38/docs/supported-sources/phantombuster.md +38 -0
  6. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/main.py +3 -0
  7. ingestr-0.13.38/ingestr/src/buildinfo.py +1 -0
  8. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/factory.py +2 -0
  9. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/frankfurter/__init__.py +25 -12
  10. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/frankfurter/helpers.py +16 -0
  11. ingestr-0.13.38/ingestr/src/phantombuster/__init__.py +60 -0
  12. ingestr-0.13.38/ingestr/src/phantombuster/client.py +70 -0
  13. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/sources.py +52 -5
  14. {ingestr-0.13.36 → ingestr-0.13.38}/requirements.txt +1 -1
  15. ingestr-0.13.36/ingestr/src/buildinfo.py +0 -1
  16. {ingestr-0.13.36 → ingestr-0.13.38}/.dockerignore +0 -0
  17. {ingestr-0.13.36 → ingestr-0.13.38}/.githooks/pre-commit-hook.sh +0 -0
  18. {ingestr-0.13.36 → ingestr-0.13.38}/.github/workflows/deploy-docs.yml +0 -0
  19. {ingestr-0.13.36 → ingestr-0.13.38}/.github/workflows/release.yml +0 -0
  20. {ingestr-0.13.36 → ingestr-0.13.38}/.github/workflows/secrets-scan.yml +0 -0
  21. {ingestr-0.13.36 → ingestr-0.13.38}/.github/workflows/tests.yml +0 -0
  22. {ingestr-0.13.36 → ingestr-0.13.38}/.gitignore +0 -0
  23. {ingestr-0.13.36 → ingestr-0.13.38}/.gitleaksignore +0 -0
  24. {ingestr-0.13.36 → ingestr-0.13.38}/.python-version +0 -0
  25. {ingestr-0.13.36 → ingestr-0.13.38}/.vale.ini +0 -0
  26. {ingestr-0.13.36 → ingestr-0.13.38}/Dockerfile +0 -0
  27. {ingestr-0.13.36 → ingestr-0.13.38}/LICENSE.md +0 -0
  28. {ingestr-0.13.36 → ingestr-0.13.38}/Makefile +0 -0
  29. {ingestr-0.13.36 → ingestr-0.13.38}/README.md +0 -0
  30. {ingestr-0.13.36 → ingestr-0.13.38}/docs/.vitepress/theme/custom.css +0 -0
  31. {ingestr-0.13.36 → ingestr-0.13.38}/docs/.vitepress/theme/index.js +0 -0
  32. {ingestr-0.13.36 → ingestr-0.13.38}/docs/commands/example-uris.md +0 -0
  33. {ingestr-0.13.36 → ingestr-0.13.38}/docs/commands/ingest.md +0 -0
  34. {ingestr-0.13.36 → ingestr-0.13.38}/docs/getting-started/core-concepts.md +0 -0
  35. {ingestr-0.13.36 → ingestr-0.13.38}/docs/getting-started/incremental-loading.md +0 -0
  36. {ingestr-0.13.36 → ingestr-0.13.38}/docs/getting-started/quickstart.md +0 -0
  37. {ingestr-0.13.36 → ingestr-0.13.38}/docs/getting-started/telemetry.md +0 -0
  38. {ingestr-0.13.36 → ingestr-0.13.38}/docs/index.md +0 -0
  39. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/applovin_max.png +0 -0
  40. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/athena.png +0 -0
  41. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/clickhouse_img.png +0 -0
  42. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/freshdesk_ingestion.png +0 -0
  43. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/github.png +0 -0
  44. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/google_analytics_realtime_report.png +0 -0
  45. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/googleanalytics.png +0 -0
  46. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/kinesis.bigquery.png +0 -0
  47. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/linkedin_ads.png +0 -0
  48. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/personio.png +0 -0
  49. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/personio_duckdb.png +0 -0
  50. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/pipedrive.png +0 -0
  51. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/stripe_postgres.png +0 -0
  52. {ingestr-0.13.36 → ingestr-0.13.38}/docs/media/tiktok.png +0 -0
  53. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/adjust.md +0 -0
  54. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/airtable.md +0 -0
  55. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/applovin.md +0 -0
  56. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/applovin_max.md +0 -0
  57. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/appsflyer.md +0 -0
  58. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/appstore.md +0 -0
  59. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/asana.md +0 -0
  60. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/athena.md +0 -0
  61. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/bigquery.md +0 -0
  62. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/chess.md +0 -0
  63. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/clickhouse.md +0 -0
  64. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/csv.md +0 -0
  65. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/custom_queries.md +0 -0
  66. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/databricks.md +0 -0
  67. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/db2.md +0 -0
  68. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/duckdb.md +0 -0
  69. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/dynamodb.md +0 -0
  70. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/facebook-ads.md +0 -0
  71. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/freshdesk.md +0 -0
  72. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/gcs.md +0 -0
  73. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/github.md +0 -0
  74. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/google-ads.md +0 -0
  75. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/google_analytics.md +0 -0
  76. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/gorgias.md +0 -0
  77. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/gsheets.md +0 -0
  78. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/hubspot.md +0 -0
  79. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/kafka.md +0 -0
  80. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/kinesis.md +0 -0
  81. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/klaviyo.md +0 -0
  82. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/linkedin_ads.md +0 -0
  83. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/mongodb.md +0 -0
  84. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/mssql.md +0 -0
  85. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/mysql.md +0 -0
  86. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/notion.md +0 -0
  87. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/oracle.md +0 -0
  88. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/personio.md +0 -0
  89. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/pipedrive.md +0 -0
  90. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/postgres.md +0 -0
  91. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/redshift.md +0 -0
  92. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/s3.md +0 -0
  93. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/salesforce.md +0 -0
  94. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/sap-hana.md +0 -0
  95. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/shopify.md +0 -0
  96. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/slack.md +0 -0
  97. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/snowflake.md +0 -0
  98. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/sqlite.md +0 -0
  99. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/stripe.md +0 -0
  100. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/tiktok-ads.md +0 -0
  101. {ingestr-0.13.36 → ingestr-0.13.38}/docs/supported-sources/zendesk.md +0 -0
  102. {ingestr-0.13.36 → ingestr-0.13.38}/docs/tutorials/load-kinesis-bigquery.md +0 -0
  103. {ingestr-0.13.36 → ingestr-0.13.38}/docs/tutorials/load-personio-duckdb.md +0 -0
  104. {ingestr-0.13.36 → ingestr-0.13.38}/docs/tutorials/load-stripe-postgres.md +0 -0
  105. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/conftest.py +0 -0
  106. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/.gitignore +0 -0
  107. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/adjust/__init__.py +0 -0
  108. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/adjust/adjust_helpers.py +0 -0
  109. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/airtable/__init__.py +0 -0
  110. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/applovin/__init__.py +0 -0
  111. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/applovin_max/__init__.py +0 -0
  112. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/appsflyer/__init__.py +0 -0
  113. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/appsflyer/client.py +0 -0
  114. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/appstore/__init__.py +0 -0
  115. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/appstore/client.py +0 -0
  116. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/appstore/errors.py +0 -0
  117. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/appstore/models.py +0 -0
  118. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/appstore/resources.py +0 -0
  119. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/arrow/__init__.py +0 -0
  120. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/asana_source/__init__.py +0 -0
  121. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/asana_source/helpers.py +0 -0
  122. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/asana_source/settings.py +0 -0
  123. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/blob.py +0 -0
  124. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/chess/__init__.py +0 -0
  125. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/chess/helpers.py +0 -0
  126. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/chess/settings.py +0 -0
  127. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/collector/spinner.py +0 -0
  128. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/destinations.py +0 -0
  129. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/dynamodb/__init__.py +0 -0
  130. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/errors.py +0 -0
  131. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/facebook_ads/__init__.py +0 -0
  132. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/facebook_ads/exceptions.py +0 -0
  133. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/facebook_ads/helpers.py +0 -0
  134. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/facebook_ads/settings.py +0 -0
  135. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/filesystem/__init__.py +0 -0
  136. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/filesystem/helpers.py +0 -0
  137. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/filesystem/readers.py +0 -0
  138. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/filters.py +0 -0
  139. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/freshdesk/__init__.py +0 -0
  140. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/freshdesk/freshdesk_client.py +0 -0
  141. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/freshdesk/settings.py +0 -0
  142. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/github/__init__.py +0 -0
  143. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/github/helpers.py +0 -0
  144. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/github/queries.py +0 -0
  145. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/github/settings.py +0 -0
  146. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_ads/__init__.py +0 -0
  147. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_ads/field.py +0 -0
  148. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_ads/metrics.py +0 -0
  149. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_ads/predicates.py +0 -0
  150. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_ads/reports.py +0 -0
  151. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_analytics/__init__.py +0 -0
  152. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_analytics/helpers.py +0 -0
  153. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_sheets/README.md +0 -0
  154. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_sheets/__init__.py +0 -0
  155. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_sheets/helpers/__init__.py +0 -0
  156. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_sheets/helpers/api_calls.py +0 -0
  157. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/google_sheets/helpers/data_processing.py +0 -0
  158. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/gorgias/__init__.py +0 -0
  159. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/gorgias/helpers.py +0 -0
  160. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/hubspot/__init__.py +0 -0
  161. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/hubspot/helpers.py +0 -0
  162. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/hubspot/settings.py +0 -0
  163. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/kafka/__init__.py +0 -0
  164. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/kafka/helpers.py +0 -0
  165. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/kinesis/__init__.py +0 -0
  166. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/kinesis/helpers.py +0 -0
  167. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/klaviyo/__init__.py +0 -0
  168. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/klaviyo/client.py +0 -0
  169. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/klaviyo/helpers.py +0 -0
  170. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/linkedin_ads/__init__.py +0 -0
  171. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/linkedin_ads/dimension_time_enum.py +0 -0
  172. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/linkedin_ads/helpers.py +0 -0
  173. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/loader.py +0 -0
  174. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/mongodb/__init__.py +0 -0
  175. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/mongodb/helpers.py +0 -0
  176. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/notion/__init__.py +0 -0
  177. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/notion/helpers/__init__.py +0 -0
  178. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/notion/helpers/client.py +0 -0
  179. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/notion/helpers/database.py +0 -0
  180. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/notion/settings.py +0 -0
  181. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/partition.py +0 -0
  182. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/personio/__init__.py +0 -0
  183. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/personio/helpers.py +0 -0
  184. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/pipedrive/__init__.py +0 -0
  185. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/pipedrive/helpers/__init__.py +0 -0
  186. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/pipedrive/helpers/custom_fields_munger.py +0 -0
  187. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/pipedrive/helpers/pages.py +0 -0
  188. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/pipedrive/settings.py +0 -0
  189. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/pipedrive/typing.py +0 -0
  190. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/resource.py +0 -0
  191. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/salesforce/__init__.py +0 -0
  192. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/salesforce/helpers.py +0 -0
  193. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/shopify/__init__.py +0 -0
  194. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/shopify/exceptions.py +0 -0
  195. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/shopify/helpers.py +0 -0
  196. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/shopify/settings.py +0 -0
  197. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/slack/__init__.py +0 -0
  198. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/slack/helpers.py +0 -0
  199. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/slack/settings.py +0 -0
  200. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/sql_database/__init__.py +0 -0
  201. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/sql_database/callbacks.py +0 -0
  202. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/stripe_analytics/__init__.py +0 -0
  203. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/stripe_analytics/helpers.py +0 -0
  204. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/stripe_analytics/settings.py +0 -0
  205. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/table_definition.py +0 -0
  206. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/telemetry/event.py +0 -0
  207. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/testdata/fakebqcredentials.json +0 -0
  208. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/tiktok_ads/__init__.py +0 -0
  209. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/tiktok_ads/tiktok_helpers.py +0 -0
  210. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/time.py +0 -0
  211. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/version.py +0 -0
  212. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/zendesk/__init__.py +0 -0
  213. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/zendesk/helpers/__init__.py +0 -0
  214. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/zendesk/helpers/api_helpers.py +0 -0
  215. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/zendesk/helpers/credentials.py +0 -0
  216. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/zendesk/helpers/talk_api.py +0 -0
  217. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/src/zendesk/settings.py +0 -0
  218. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/.gitignore +0 -0
  219. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/create_replace.csv +0 -0
  220. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/delete_insert_expected.csv +0 -0
  221. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/delete_insert_part1.csv +0 -0
  222. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/delete_insert_part2.csv +0 -0
  223. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/merge_expected.csv +0 -0
  224. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/merge_part1.csv +0 -0
  225. {ingestr-0.13.36 → ingestr-0.13.38}/ingestr/testdata/merge_part2.csv +0 -0
  226. {ingestr-0.13.36 → ingestr-0.13.38}/package-lock.json +0 -0
  227. {ingestr-0.13.36 → ingestr-0.13.38}/package.json +0 -0
  228. {ingestr-0.13.36 → ingestr-0.13.38}/pyproject.toml +0 -0
  229. {ingestr-0.13.36 → ingestr-0.13.38}/requirements-dev.txt +0 -0
  230. {ingestr-0.13.36 → ingestr-0.13.38}/requirements.in +0 -0
  231. {ingestr-0.13.36 → ingestr-0.13.38}/requirements_arm64.txt +0 -0
  232. {ingestr-0.13.36 → ingestr-0.13.38}/resources/demo.gif +0 -0
  233. {ingestr-0.13.36 → ingestr-0.13.38}/resources/demo.tape +0 -0
  234. {ingestr-0.13.36 → ingestr-0.13.38}/resources/ingestr.svg +0 -0
  235. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/AMPM.yml +0 -0
  236. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Acronyms.yml +0 -0
  237. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Colons.yml +0 -0
  238. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Contractions.yml +0 -0
  239. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/DateFormat.yml +0 -0
  240. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Ellipses.yml +0 -0
  241. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/EmDash.yml +0 -0
  242. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Exclamation.yml +0 -0
  243. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/FirstPerson.yml +0 -0
  244. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Gender.yml +0 -0
  245. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/GenderBias.yml +0 -0
  246. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/HeadingPunctuation.yml +0 -0
  247. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Headings.yml +0 -0
  248. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Latin.yml +0 -0
  249. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/LyHyphens.yml +0 -0
  250. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/OptionalPlurals.yml +0 -0
  251. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Ordinal.yml +0 -0
  252. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/OxfordComma.yml +0 -0
  253. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Parens.yml +0 -0
  254. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Passive.yml +0 -0
  255. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Periods.yml +0 -0
  256. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Quotes.yml +0 -0
  257. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Ranges.yml +0 -0
  258. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Semicolons.yml +0 -0
  259. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Slang.yml +0 -0
  260. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Spacing.yml +0 -0
  261. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Spelling.yml +0 -0
  262. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Units.yml +0 -0
  263. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/We.yml +0 -0
  264. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/Will.yml +0 -0
  265. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/WordList.yml +0 -0
  266. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/meta.json +0 -0
  267. {ingestr-0.13.36 → ingestr-0.13.38}/styles/Google/vocab.txt +0 -0
  268. {ingestr-0.13.36 → ingestr-0.13.38}/styles/bruin/Ingestr.yml +0 -0
  269. {ingestr-0.13.36 → ingestr-0.13.38}/styles/config/vocabularies/bruin/accept.txt +0 -0
  270. {ingestr-0.13.36 → ingestr-0.13.38}/test.env.template +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.36
3
+ Version: 0.13.38
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
@@ -74,7 +74,7 @@ Requires-Dist: google-cloud-storage==3.1.0
74
74
  Requires-Dist: google-crc32c==1.6.0
75
75
  Requires-Dist: google-resumable-media==2.7.2
76
76
  Requires-Dist: googleapis-common-protos==1.69.0
77
- Requires-Dist: greenlet==3.2.1
77
+ Requires-Dist: greenlet==3.2.2
78
78
  Requires-Dist: grpcio-status==1.62.3
79
79
  Requires-Dist: grpcio==1.70.0
80
80
  Requires-Dist: hdbcli==2.23.27
@@ -119,6 +119,7 @@ export default defineConfig({
119
119
  link: "/supported-sources/facebook-ads.md",
120
120
  },
121
121
  { text: "Frankfurter", link: "/supported-sources/frankfurter.md" },
122
+ { text: "Freshdesk", link: "/supported-sources/freshdesk.md" },
122
123
  { text: "Google Cloud Storage (GCS)", link: "/supported-sources/gcs.md" },
123
124
  { text: "Google Analytics", link: "/supported-sources/google_analytics.md" },
124
125
  { text: "Google Ads", link: "/supported-sources/google-ads.md" },
@@ -130,6 +131,7 @@ export default defineConfig({
130
131
  { text: "LinkedIn Ads", link: "/supported-sources/linkedin_ads.md" },
131
132
  { text: "Notion", link: "/supported-sources/notion.md" },
132
133
  { text: "Personio", link: "/supported-sources/personio.md" },
134
+ { text: "PhantomBuster", link: "/supported-sources/phantombuster.md" },
133
135
  { text: "Pipedrive", link: "/supported-sources/pipedrive.md" },
134
136
  { text: "S3", link: "/supported-sources/s3.md" },
135
137
  { text: "Salesforce", link: "/supported-sources/salesforce.md" },
@@ -10,7 +10,7 @@ The `ingestr` command to use the `frankfurter` source is as follows:
10
10
 
11
11
  ```bash
12
12
  ingestr ingest \
13
- --source-uri 'frankfurter://' \
13
+ --source-uri 'frankfurter://?base=IDR' \
14
14
  --interval-start '2025-03-27' \ # Optional. See 'exchange_rates'.
15
15
  --interval-end '2025-03-28' \ # Optional.
16
16
  --source-table '<table_name>' \ # E.g 'currencies', 'latest', 'exchange_rates'. See below.
@@ -25,7 +25,9 @@ ingestr ingest \
25
25
  ### **`--source-uri`**
26
26
  - **Description**: Specifies the source URI for the Frankfurter API.
27
27
  - **Value**: `'frankfurter://'`
28
- - **Purpose**: Indicates that the data will be fetched from the Frankfurter API.
28
+ - **Purpose**: Indicates that the data will be fetched from the Frankfurter API.
29
+ - An optional base currency can be added `?base={base_currency}`.
30
+ - If no base currency is included, base currency defaults to USD.
29
31
 
30
32
  ---
31
33
 
@@ -89,9 +91,10 @@ ingestr ingest \
89
91
  - **Description**: Fetches the latest exchange rates.
90
92
  - **Columns**:
91
93
  - `date`: The date of the exchange rates.
92
- - `currency_name`: The ISO 4217 currency code (e.g., `USD`, `EUR`).
94
+ - `currency_code`: The ISO 4217 currency code (e.g., `USD`, `EUR`).
93
95
  - `rate`: The exchange rate relative to the base currency.
94
- - **Primary Key**: Composite key of `date` and `currency_name`.
96
+ - `base_currency`: The base currency used to calculate the exchange rate.
97
+ - **Primary Key**: Composite key of `date`, `currency_code` and `base_currency`.
95
98
  - **Notes**:
96
99
  - The base currency (e.g., `EUR`) is included with a rate of `1.0`.
97
100
 
@@ -101,9 +104,10 @@ ingestr ingest \
101
104
  - **Description**: Fetches historical exchange rates for a specified date range.
102
105
  - **Columns**:
103
106
  - `date`: The date of the exchange rates.
104
- - `currency_name`: The ISO 4217 currency code (e.g., `USD`, `EUR`).
107
+ - `currency_code`: The ISO 4217 currency code (e.g., `USD`, `EUR`).
105
108
  - `rate`: The exchange rate relative to the base currency.
106
- - **Primary Key**: Composite key of `date` and `currency_name`.
109
+ - `base_currency`: The base currency used to calculate the exchange rate.
110
+ - **Primary Key**: Composite key of `date`, `currency_code` and `base_currency`.
107
111
  - **Notes**:
108
112
  - An optional start and end date can be added via the arguments `--interval-start` and optionally `--interval-end` to define the date range (see examples below). If no start date is specified, the date will default today's date (and thus return the latest exchange rates).
109
113
  - If a start date but no end date is specified, then the end date will default to today's date and ingestr will retrieve data up until the latest published data.
@@ -114,24 +118,24 @@ Here `--interval-start` is set to a weekend date (e.g., `2025-03-29` -- a Saturd
114
118
 
115
119
  `--interval-start` defaults to the previous Friday (`2025-03-28`) and the next data is from the following Monday (for simplicity, only a subset of currencies is shown below):
116
120
 
117
- | **date** | **currency_name** | **rate** |
118
- |--------------|-------------------|----------|
119
- | 2025-03-28 | EUR | 1.0 |
120
- | 2025-03-28 | USD | 1.0783 |
121
- | 2025-03-28 | GBP | 0.8571 |
122
- | 2025-03-31 | EUR | 1.0 |
123
- | 2025-03-31 | USD | 1.0783 |
124
- | 2025-03-31 | GBP | 0.8571 |
121
+ | **date** | **currency_code** | **rate** | **base_currency** |
122
+ |--------------|-------------------|----------|-------------------|
123
+ | 2025-03-28 | EUR | 1.0 | EUR |
124
+ | 2025-03-28 | USD | 1.0783 | EUR |
125
+ | 2025-03-28 | GBP | 0.8571 | EUR |
126
+ | 2025-03-31 | EUR | 1.0 | EUR |
127
+ | 2025-03-31 | USD | 1.0783 | EUR |
128
+ | 2025-03-31 | GBP | 0.8571 | EUR |
125
129
 
126
130
 
127
131
  ---
128
132
 
129
133
  ## **Examples**
130
134
 
131
- ### **1. Fetch the Latest Exchange Rates**
135
+ ### **1. Fetch the Latest Exchange Rates with GBP as Base Currency**
132
136
  ```bash
133
137
  ingestr ingest \
134
- --source-uri 'frankfurter://' \
138
+ --source-uri 'frankfurter://?base=GBP' \
135
139
  --source-table 'latest' \
136
140
  --dest-uri 'duckdb.db' \
137
141
  --dest-table 'schema.latest_new_scheme'
@@ -139,7 +143,7 @@ ingestr ingest \
139
143
 
140
144
  ---
141
145
 
142
- ### **2. Fetch Historical Exchange Rates**
146
+ ### **2. Fetch Historical Exchange Rates with USD as Default Base Currency**
143
147
  ```bash
144
148
  ingestr ingest \
145
149
  --source-uri 'frankfurter://' \
@@ -0,0 +1,38 @@
1
+ # PhantomBuster
2
+ [PhantomBuster](https://phantombuster.com/) is a cloud-based data automation and web scraping platform that allows users to extract data from websites, automate actions.
3
+
4
+ ingestr supports PhantomBuster as a source.
5
+
6
+ ## URI format
7
+
8
+ The URI format for PhantomBuster is as follows:
9
+
10
+ ```plaintext
11
+ PhantomBuster://?api_key=<api_key>
12
+ ```
13
+
14
+ URI parameters:
15
+ - `api_key`: the API key used for authentication with the PhantomBuster API
16
+
17
+ ## Setting up a PhantomBuster Integration
18
+
19
+ You can find your PhantomBuster API key by following the guide [here](https://hub.phantombuster.com/docs/api#how-to-find-my-api-key).
20
+
21
+ Let's say your `api_key` is key_123, here's a sample command that will copy the data from PhantomBuster into a DuckDB database:
22
+
23
+
24
+ ```bash
25
+ ingestr ingest \
26
+ --source-uri 'PhantomBuster://?api_key=key_123' \
27
+ --source-table 'completed_phantoms:<agent_id>' \
28
+ --dest-uri duckdb:///PhantomBuster.duckdb \
29
+ --dest-table 'dest.result'
30
+ ```
31
+
32
+
33
+ <img alt="PhantomBuster_img" src="../media/phantombuster.png"/>
34
+
35
+
36
+ For now, we only support `completed_phantoms` table followed by an `agent_id`. For example: `completed_phantoms:<agent_id>` Where agent id is a unique identifier for a specific Phantom which can be found in URI of a specific phantom.
37
+
38
+ Use this as `--source-table` parameter in the `ingestr ingest` command.
@@ -485,6 +485,9 @@ def ingest(
485
485
  print(
486
486
  f"[bold yellow] Primary Key:[/bold yellow] {primary_key if primary_key else 'None'}"
487
487
  )
488
+ print(
489
+ f"[bold yellow] Pipeline ID:[/bold yellow] {m.hexdigest()}"
490
+ )
488
491
  print()
489
492
 
490
493
  if not yes:
@@ -0,0 +1 @@
1
+ version = "v0.13.38"
@@ -54,6 +54,7 @@ from ingestr.src.sources import (
54
54
  TikTokSource,
55
55
  ZendeskSource,
56
56
  FreshdeskSource,
57
+ PhantombusterSource,
57
58
  )
58
59
 
59
60
  SQL_SOURCE_SCHEMES = [
@@ -150,6 +151,7 @@ class SourceDestinationFactory:
150
151
  "pipedrive": PipedriveSource,
151
152
  "frankfurter": FrankfurterSource,
152
153
  "freshdesk": FreshdeskSource,
154
+ "phantombuster": PhantombusterSource,
153
155
  }
154
156
  destinations: Dict[str, Type[DestinationProtocol]] = {
155
157
  "bigquery": BigQueryDestination,
@@ -1,4 +1,4 @@
1
- from typing import Any, Iterator
1
+ from typing import Any, Iterator, Optional
2
2
 
3
3
  import dlt
4
4
  from dlt.common.pendulum import pendulum
@@ -15,13 +15,13 @@ from ingestr.src.frankfurter.helpers import get_path_with_retry
15
15
  def frankfurter_source(
16
16
  start_date: TAnyDateTime,
17
17
  end_date: TAnyDateTime,
18
+ base_currency: str,
18
19
  ) -> Any:
19
20
  """
20
21
  A dlt source for the frankfurter.dev API. It groups several resources (in this case frankfurter.dev API endpoints) containing
21
22
  various types of data: currencies, latest rates, historical rates.
22
23
  """
23
24
  date_time = dlt.sources.incremental(
24
-
25
25
  "date",
26
26
  initial_value=start_date,
27
27
  end_value=end_date,
@@ -31,9 +31,10 @@ def frankfurter_source(
31
31
 
32
32
  return (
33
33
  currencies(),
34
- latest(),
35
- exchange_rates(start_date=date_time, end_date=end_date),
36
-
34
+ latest(base_currency=base_currency),
35
+ exchange_rates(
36
+ start_date=date_time, end_date=end_date, base_currency=base_currency
37
+ ),
37
38
  )
38
39
 
39
40
 
@@ -61,29 +62,33 @@ def currencies() -> Iterator[dict]:
61
62
  "date": {"data_type": "text"},
62
63
  "currency_code": {"data_type": "text"},
63
64
  "rate": {"data_type": "double"},
65
+ "base_currency": {"data_type": "text"},
64
66
  },
65
- primary_key=["date", "currency_code"], # Composite primary key
67
+ primary_key=["date", "currency_code", "base_currency"],
66
68
  )
67
- def latest() -> Iterator[dict]:
69
+ def latest(base_currency: Optional[str] = "") -> Iterator[dict]:
68
70
  """
69
71
  Fetches the latest exchange rates and yields them as rows.
70
72
  """
71
73
  # Base URL
72
74
  url = "latest?"
73
75
 
76
+ if base_currency:
77
+ url += f"base={base_currency}"
78
+
74
79
  # Fetch data
75
80
  data = get_path_with_retry(url)
76
81
 
77
82
  # Extract rates and base currency
78
83
  rates = data["rates"]
79
-
80
84
  date = pendulum.parse(data["date"])
81
85
 
82
- # Add the base currency (EUR) with a rate of 1.0
86
+ # Add the base currency with a rate of 1.0
83
87
  yield {
84
88
  "date": date,
85
- "currency_code": "EUR",
89
+ "currency_code": base_currency,
86
90
  "rate": 1.0,
91
+ "base_currency": base_currency,
87
92
  }
88
93
 
89
94
  # Add all currencies and their rates
@@ -92,6 +97,7 @@ def latest() -> Iterator[dict]:
92
97
  "date": date,
93
98
  "currency_code": currency_code,
94
99
  "rate": rate,
100
+ "base_currency": base_currency,
95
101
  }
96
102
 
97
103
 
@@ -101,12 +107,14 @@ def latest() -> Iterator[dict]:
101
107
  "date": {"data_type": "text"},
102
108
  "currency_code": {"data_type": "text"},
103
109
  "rate": {"data_type": "double"},
110
+ "base_currency": {"data_type": "text"},
104
111
  },
105
- primary_key=("date", "currency_code"), # Composite primary key
112
+ primary_key=("date", "currency_code", "base_currency"),
106
113
  )
107
114
  def exchange_rates(
108
115
  end_date: TAnyDateTime,
109
116
  start_date: dlt.sources.incremental[TAnyDateTime] = dlt.sources.incremental("date"),
117
+ base_currency: Optional[str] = "",
110
118
  ) -> Iterator[dict]:
111
119
  """
112
120
  Fetches exchange rates for a specified date range.
@@ -124,6 +132,9 @@ def exchange_rates(
124
132
  # Compose the URL
125
133
  url = f"{start_date_str}..{end_date_str}?"
126
134
 
135
+ if base_currency:
136
+ url += f"base={base_currency}"
137
+
127
138
  # Fetch data from the API
128
139
  data = get_path_with_retry(url)
129
140
 
@@ -137,8 +148,9 @@ def exchange_rates(
137
148
  # Add the base currency with a rate of 1.0
138
149
  yield {
139
150
  "date": formatted_date,
140
- "currency_code": "EUR",
151
+ "currency_code": base_currency,
141
152
  "rate": 1.0,
153
+ "base_currency": base_currency,
142
154
  }
143
155
 
144
156
  # Add all other currencies and their rates
@@ -147,4 +159,5 @@ def exchange_rates(
147
159
  "date": formatted_date,
148
160
  "currency_code": currency_code,
149
161
  "rate": rate,
162
+ "base_currency": base_currency,
150
163
  }
@@ -30,3 +30,19 @@ def validate_dates(start_date: datetime, end_date: datetime) -> None:
30
30
  # Check if start_date is before end_date
31
31
  if start_date > end_date:
32
32
  raise ValueError("Interval-end cannot be before interval-start.")
33
+
34
+
35
+ def validate_currency(currency_code: str) -> bool:
36
+ url = "https://api.frankfurter.dev/v1/currencies"
37
+
38
+ response = requests.get(url, timeout=5)
39
+ currencies = response.json()
40
+
41
+ if currency_code.upper() in currencies:
42
+ return True
43
+ else:
44
+ supported_currencies = list(currencies.keys())
45
+ print(
46
+ f"Invalid base currency '{currency_code}'. Supported currencies are: {supported_currencies}"
47
+ )
48
+ return False
@@ -0,0 +1,60 @@
1
+ from typing import Iterable, Optional
2
+
3
+ import dlt
4
+ import pendulum
5
+ import requests
6
+ from dlt.common.typing import TDataItem, TAnyDateTime
7
+ from dlt.sources import DltResource
8
+ from dlt.sources.helpers.requests import Client
9
+
10
+
11
+ from ingestr.src.phantombuster.client import PhantombusterClient
12
+
13
+
14
+ def retry_on_limit(
15
+ response: Optional[requests.Response], exception: Optional[BaseException]
16
+ ) -> bool:
17
+ if response is not None and response.status_code == 429:
18
+ return True
19
+ return False
20
+
21
+
22
+ def create_client() -> requests.Session:
23
+ return Client(
24
+ raise_for_status=False,
25
+ retry_condition=retry_on_limit,
26
+ request_max_attempts=12,
27
+ request_backoff_factor=2,
28
+ ).session
29
+
30
+ @dlt.source(max_table_nesting=0)
31
+ def phantombuster_source(api_key: str, agent_id: str, start_date: TAnyDateTime, end_date: TAnyDateTime | None) -> Iterable[DltResource]:
32
+ client = PhantombusterClient(api_key)
33
+ @dlt.resource(
34
+ write_disposition="merge",
35
+ primary_key="container_id",
36
+ columns={
37
+ "partition_dt": {"data_type": "date", "partition": True},
38
+ },
39
+ )
40
+ def completed_phantoms(
41
+ dateTime=(
42
+ dlt.sources.incremental(
43
+ "ended_at",
44
+ initial_value=start_date,
45
+ end_value=end_date,
46
+ range_start="closed",
47
+ range_end="closed",
48
+ )
49
+ ),
50
+ ) -> Iterable[TDataItem]:
51
+ if dateTime.end_value is None:
52
+ end_dt = pendulum.now(tz="UTC")
53
+ else:
54
+ end_dt = dateTime.end_value
55
+
56
+ start_dt = dateTime.last_value
57
+
58
+ yield client.fetch_containers_result(create_client(), agent_id, start_date=start_dt, end_date=end_dt)
59
+
60
+ return completed_phantoms
@@ -0,0 +1,70 @@
1
+ from typing import Union
2
+
3
+ import pendulum
4
+ import requests
5
+
6
+
7
+ class PhantombusterClient:
8
+ def __init__(self, api_key: str):
9
+ self.api_key = api_key
10
+
11
+ def _get_headers(self):
12
+ return {
13
+ "X-Phantombuster-Key-1": self.api_key,
14
+ "accept": "application/json",
15
+ }
16
+
17
+ def fetch_containers_result(self, session: requests.Session, agent_id: str, start_date: pendulum.DateTime, end_date: pendulum.DateTime):
18
+ url = "https://api.phantombuster.com/api/v2/containers/fetch-all/"
19
+ before_ended_at = None
20
+ limit = 100
21
+
22
+ started_at = start_date.int_timestamp * 1000 + int(start_date.microsecond / 1000)
23
+ ended_at = end_date.int_timestamp * 1000 + int(end_date.microsecond / 1000)
24
+
25
+ while True:
26
+ params: dict[str, Union[str, int, float, bytes, None]] = {
27
+ "agentId": agent_id,
28
+ "limit": limit,
29
+ "mode": "finalized",
30
+ }
31
+
32
+ if before_ended_at:
33
+ params["beforeEndedAt"] = before_ended_at
34
+
35
+ response = session.get(url=url, headers=self._get_headers(), params=params)
36
+ data = response.json()
37
+ containers = data.get("containers", [])
38
+
39
+ for container in containers:
40
+ container_ended_at = container.get("endedAt")
41
+
42
+ if before_ended_at is None or before_ended_at > container_ended_at:
43
+ before_ended_at = container_ended_at
44
+
45
+ if container_ended_at < started_at or container_ended_at > ended_at:
46
+ continue
47
+
48
+ try:
49
+ result = self.fetch_result_object(session, container["id"])
50
+ partition_dt = pendulum.from_timestamp(container_ended_at / 1000, tz="UTC").date()
51
+ container_ended_at_datetime = pendulum.from_timestamp(container_ended_at / 1000, tz="UTC")
52
+ row = {"container_id": container["id"],"container": container, "result": result, "partition_dt": partition_dt, "ended_at": container_ended_at_datetime}
53
+ yield row
54
+
55
+ except requests.RequestException as e:
56
+ print(f"Error fetching result for container {container['id']}: {e}")
57
+
58
+ if data["maxLimitReached"] is False:
59
+ break
60
+
61
+
62
+ def fetch_result_object(self, session: requests.Session, container_id: str):
63
+ result_url = (
64
+ "https://api.phantombuster.com/api/v2/containers/fetch-result-object"
65
+ )
66
+ params = {"id": container_id}
67
+ response = session.get(result_url, headers=self._get_headers(), params=params)
68
+ response.raise_for_status()
69
+
70
+ return response.json()
@@ -2180,6 +2180,18 @@ class FrankfurterSource:
2180
2180
  "Frankfurter takes care of incrementality on its own, you should not provide incremental_key"
2181
2181
  )
2182
2182
 
2183
+ from ingestr.src.frankfurter import frankfurter_source
2184
+ from ingestr.src.frankfurter.helpers import validate_currency, validate_dates
2185
+
2186
+ parsed_uri = urlparse(uri)
2187
+ source_params = parse_qs(parsed_uri.query)
2188
+ base_currency = source_params.get("base", [None])[0]
2189
+
2190
+ if not base_currency:
2191
+ base_currency = "USD"
2192
+
2193
+ validate_currency(base_currency)
2194
+
2183
2195
  if kwargs.get("interval_start"):
2184
2196
  start_date = ensure_pendulum_datetime(str(kwargs.get("interval_start")))
2185
2197
  if kwargs.get("interval_end"):
@@ -2190,21 +2202,20 @@ class FrankfurterSource:
2190
2202
  start_date = pendulum.now()
2191
2203
  end_date = pendulum.now()
2192
2204
 
2193
- from ingestr.src.frankfurter import frankfurter_source
2194
- from ingestr.src.frankfurter.helpers import validate_dates
2195
-
2196
2205
  validate_dates(start_date=start_date, end_date=end_date)
2197
2206
 
2198
2207
  src = frankfurter_source(
2199
2208
  start_date=start_date,
2200
2209
  end_date=end_date,
2210
+ base_currency=base_currency,
2201
2211
  )
2202
2212
 
2203
2213
  if table not in src.resources:
2204
2214
  raise UnsupportedResourceError(table, "Frankfurter")
2205
2215
 
2206
2216
  return src.with_resources(table)
2207
-
2217
+
2218
+
2208
2219
  class FreshdeskSource:
2209
2220
  # freshdesk://domain?api_key=<api_key>
2210
2221
  def handles_incrementality(self) -> bool:
@@ -2231,4 +2242,40 @@ class FreshdeskSource:
2231
2242
 
2232
2243
  from ingestr.src.freshdesk import freshdesk_source
2233
2244
  return freshdesk_source(api_secret_key=api_key[0], domain=domain).with_resources(table)
2234
-
2245
+
2246
+ class PhantombusterSource:
2247
+ def handles_incrementality(self) -> bool:
2248
+ return True
2249
+
2250
+ def dlt_source(self, uri: str, table: str, **kwargs):
2251
+ #phantombuster://?api_key=<api_key>
2252
+ #source table = phantom_results:agent_id
2253
+ parsed_uri = urlparse(uri)
2254
+ params = parse_qs(parsed_uri.query)
2255
+ api_key = params.get("api_key")
2256
+ if api_key is None:
2257
+ raise MissingValueError("api_key", "Phantombuster")
2258
+
2259
+ table_fields = table.replace(" ", "").split(":")
2260
+ table_name = table_fields[0]
2261
+
2262
+ agent_id = table_fields[1] if len(table_fields) > 1 else None
2263
+
2264
+ if table_name not in ["completed_phantoms"]:
2265
+ raise UnsupportedResourceError(table_name, "Phantombuster")
2266
+
2267
+ if not agent_id:
2268
+ raise MissingValueError("agent_id", "Phantombuster")
2269
+
2270
+ start_date = kwargs.get("interval_start")
2271
+ if start_date is None:
2272
+ start_date = ensure_pendulum_datetime("2018-01-01").in_tz("UTC")
2273
+ else:
2274
+ start_date = ensure_pendulum_datetime(start_date).in_tz("UTC")
2275
+
2276
+ end_date = kwargs.get("interval_end")
2277
+ if end_date is not None:
2278
+ end_date = ensure_pendulum_datetime(end_date).in_tz("UTC")
2279
+
2280
+ from ingestr.src.phantombuster import phantombuster_source
2281
+ return phantombuster_source(api_key=api_key[0], agent_id=agent_id, start_date=start_date, end_date=end_date).with_resources(table_name)
@@ -187,7 +187,7 @@ googleapis-common-protos==1.69.0
187
187
  # google-ads
188
188
  # google-api-core
189
189
  # grpcio-status
190
- greenlet==3.2.1
190
+ greenlet==3.2.2
191
191
  # via sqlalchemy
192
192
  grpcio==1.70.0
193
193
  # via
@@ -1 +0,0 @@
1
- version = "v0.13.36"
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