wbfdm 2.2.1__py2.py3-none-any.whl

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

Potentially problematic release.


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

Files changed (337) hide show
  1. wbfdm/__init__.py +2 -0
  2. wbfdm/admin/__init__.py +42 -0
  3. wbfdm/admin/classifications.py +39 -0
  4. wbfdm/admin/esg.py +23 -0
  5. wbfdm/admin/exchanges.py +53 -0
  6. wbfdm/admin/instrument_lists.py +23 -0
  7. wbfdm/admin/instrument_prices.py +62 -0
  8. wbfdm/admin/instrument_requests.py +33 -0
  9. wbfdm/admin/instruments.py +117 -0
  10. wbfdm/admin/instruments_relationships.py +25 -0
  11. wbfdm/admin/options.py +101 -0
  12. wbfdm/analysis/__init__.py +2 -0
  13. wbfdm/analysis/esg/__init__.py +0 -0
  14. wbfdm/analysis/esg/enums.py +82 -0
  15. wbfdm/analysis/esg/esg_analysis.py +217 -0
  16. wbfdm/analysis/esg/utils.py +13 -0
  17. wbfdm/analysis/financial_analysis/__init__.py +1 -0
  18. wbfdm/analysis/financial_analysis/financial_metric_analysis.py +88 -0
  19. wbfdm/analysis/financial_analysis/financial_ratio_analysis.py +125 -0
  20. wbfdm/analysis/financial_analysis/financial_statistics_analysis.py +271 -0
  21. wbfdm/analysis/financial_analysis/statement_with_estimates.py +558 -0
  22. wbfdm/analysis/financial_analysis/utils.py +316 -0
  23. wbfdm/analysis/technical_analysis/__init__.py +1 -0
  24. wbfdm/analysis/technical_analysis/technical_analysis.py +138 -0
  25. wbfdm/analysis/technical_analysis/traces.py +165 -0
  26. wbfdm/analysis/utils.py +32 -0
  27. wbfdm/apps.py +14 -0
  28. wbfdm/contrib/__init__.py +0 -0
  29. wbfdm/contrib/dsws/__init__.py +0 -0
  30. wbfdm/contrib/dsws/client.py +285 -0
  31. wbfdm/contrib/internal/__init__.py +0 -0
  32. wbfdm/contrib/internal/dataloaders/__init__.py +0 -0
  33. wbfdm/contrib/internal/dataloaders/market_data.py +87 -0
  34. wbfdm/contrib/metric/__init__.py +0 -0
  35. wbfdm/contrib/metric/admin/__init__.py +2 -0
  36. wbfdm/contrib/metric/admin/instruments.py +12 -0
  37. wbfdm/contrib/metric/admin/metrics.py +43 -0
  38. wbfdm/contrib/metric/apps.py +10 -0
  39. wbfdm/contrib/metric/backends/__init__.py +2 -0
  40. wbfdm/contrib/metric/backends/base.py +159 -0
  41. wbfdm/contrib/metric/backends/performances.py +265 -0
  42. wbfdm/contrib/metric/backends/statistics.py +182 -0
  43. wbfdm/contrib/metric/decorators.py +14 -0
  44. wbfdm/contrib/metric/dispatch.py +23 -0
  45. wbfdm/contrib/metric/dto.py +88 -0
  46. wbfdm/contrib/metric/exceptions.py +6 -0
  47. wbfdm/contrib/metric/factories.py +33 -0
  48. wbfdm/contrib/metric/filters.py +28 -0
  49. wbfdm/contrib/metric/migrations/0001_initial.py +88 -0
  50. wbfdm/contrib/metric/migrations/0002_remove_instrumentmetric_unique_instrument_metric_and_more.py +26 -0
  51. wbfdm/contrib/metric/migrations/__init__.py +0 -0
  52. wbfdm/contrib/metric/models.py +180 -0
  53. wbfdm/contrib/metric/orchestrators.py +94 -0
  54. wbfdm/contrib/metric/registry.py +80 -0
  55. wbfdm/contrib/metric/serializers.py +44 -0
  56. wbfdm/contrib/metric/tasks.py +27 -0
  57. wbfdm/contrib/metric/tests/__init__.py +0 -0
  58. wbfdm/contrib/metric/tests/backends/__init__.py +0 -0
  59. wbfdm/contrib/metric/tests/backends/test_performances.py +152 -0
  60. wbfdm/contrib/metric/tests/backends/test_statistics.py +48 -0
  61. wbfdm/contrib/metric/tests/conftest.py +92 -0
  62. wbfdm/contrib/metric/tests/test_dto.py +73 -0
  63. wbfdm/contrib/metric/tests/test_models.py +72 -0
  64. wbfdm/contrib/metric/tests/test_tasks.py +24 -0
  65. wbfdm/contrib/metric/tests/test_viewsets.py +79 -0
  66. wbfdm/contrib/metric/urls.py +19 -0
  67. wbfdm/contrib/metric/viewsets/__init__.py +1 -0
  68. wbfdm/contrib/metric/viewsets/configs/__init__.py +1 -0
  69. wbfdm/contrib/metric/viewsets/configs/display.py +92 -0
  70. wbfdm/contrib/metric/viewsets/configs/menus.py +11 -0
  71. wbfdm/contrib/metric/viewsets/configs/utils.py +137 -0
  72. wbfdm/contrib/metric/viewsets/mixins.py +245 -0
  73. wbfdm/contrib/metric/viewsets/viewsets.py +40 -0
  74. wbfdm/contrib/msci/__init__.py +0 -0
  75. wbfdm/contrib/msci/client.py +92 -0
  76. wbfdm/contrib/msci/dataloaders/__init__.py +0 -0
  77. wbfdm/contrib/msci/dataloaders/esg.py +87 -0
  78. wbfdm/contrib/msci/dataloaders/esg_controversies.py +81 -0
  79. wbfdm/contrib/msci/sync.py +58 -0
  80. wbfdm/contrib/msci/tests/__init__.py +0 -0
  81. wbfdm/contrib/msci/tests/conftest.py +1 -0
  82. wbfdm/contrib/msci/tests/test_client.py +70 -0
  83. wbfdm/contrib/qa/__init__.py +0 -0
  84. wbfdm/contrib/qa/apps.py +22 -0
  85. wbfdm/contrib/qa/database_routers.py +25 -0
  86. wbfdm/contrib/qa/dataloaders/__init__.py +0 -0
  87. wbfdm/contrib/qa/dataloaders/adjustments.py +56 -0
  88. wbfdm/contrib/qa/dataloaders/corporate_actions.py +59 -0
  89. wbfdm/contrib/qa/dataloaders/financials.py +83 -0
  90. wbfdm/contrib/qa/dataloaders/market_data.py +117 -0
  91. wbfdm/contrib/qa/dataloaders/officers.py +59 -0
  92. wbfdm/contrib/qa/dataloaders/reporting_dates.py +67 -0
  93. wbfdm/contrib/qa/dataloaders/statements.py +267 -0
  94. wbfdm/contrib/qa/tasks.py +0 -0
  95. wbfdm/dataloaders/__init__.py +0 -0
  96. wbfdm/dataloaders/cache.py +129 -0
  97. wbfdm/dataloaders/protocols.py +112 -0
  98. wbfdm/dataloaders/proxies.py +201 -0
  99. wbfdm/dataloaders/types.py +209 -0
  100. wbfdm/dynamic_preferences_registry.py +45 -0
  101. wbfdm/enums.py +657 -0
  102. wbfdm/factories/__init__.py +13 -0
  103. wbfdm/factories/classifications.py +56 -0
  104. wbfdm/factories/controversies.py +27 -0
  105. wbfdm/factories/exchanges.py +21 -0
  106. wbfdm/factories/instrument_list.py +22 -0
  107. wbfdm/factories/instrument_prices.py +79 -0
  108. wbfdm/factories/instruments.py +63 -0
  109. wbfdm/factories/instruments_relationships.py +31 -0
  110. wbfdm/factories/options.py +66 -0
  111. wbfdm/figures/__init__.py +1 -0
  112. wbfdm/figures/financials/__init__.py +1 -0
  113. wbfdm/figures/financials/financial_analysis_charts.py +469 -0
  114. wbfdm/figures/financials/financials_charts.py +711 -0
  115. wbfdm/filters/__init__.py +31 -0
  116. wbfdm/filters/classifications.py +100 -0
  117. wbfdm/filters/exchanges.py +22 -0
  118. wbfdm/filters/financials.py +95 -0
  119. wbfdm/filters/financials_analysis.py +119 -0
  120. wbfdm/filters/instrument_prices.py +112 -0
  121. wbfdm/filters/instruments.py +198 -0
  122. wbfdm/filters/utils.py +44 -0
  123. wbfdm/import_export/__init__.py +0 -0
  124. wbfdm/import_export/backends/__init__.py +0 -0
  125. wbfdm/import_export/backends/cbinsights/__init__.py +2 -0
  126. wbfdm/import_export/backends/cbinsights/deals.py +44 -0
  127. wbfdm/import_export/backends/cbinsights/equities.py +41 -0
  128. wbfdm/import_export/backends/cbinsights/mixin.py +15 -0
  129. wbfdm/import_export/backends/cbinsights/utils/__init__.py +0 -0
  130. wbfdm/import_export/backends/cbinsights/utils/classifications.py +4150 -0
  131. wbfdm/import_export/backends/cbinsights/utils/client.py +217 -0
  132. wbfdm/import_export/backends/refinitiv/__init__.py +5 -0
  133. wbfdm/import_export/backends/refinitiv/daily_fundamental.py +36 -0
  134. wbfdm/import_export/backends/refinitiv/fiscal_period.py +63 -0
  135. wbfdm/import_export/backends/refinitiv/forecast.py +178 -0
  136. wbfdm/import_export/backends/refinitiv/fundamental.py +103 -0
  137. wbfdm/import_export/backends/refinitiv/geographic_segment.py +32 -0
  138. wbfdm/import_export/backends/refinitiv/instrument.py +55 -0
  139. wbfdm/import_export/backends/refinitiv/instrument_price.py +77 -0
  140. wbfdm/import_export/backends/refinitiv/mixin.py +29 -0
  141. wbfdm/import_export/backends/refinitiv/utils/__init__.py +1 -0
  142. wbfdm/import_export/backends/refinitiv/utils/controller.py +182 -0
  143. wbfdm/import_export/handlers/__init__.py +0 -0
  144. wbfdm/import_export/handlers/instrument.py +253 -0
  145. wbfdm/import_export/handlers/instrument_list.py +101 -0
  146. wbfdm/import_export/handlers/instrument_price.py +71 -0
  147. wbfdm/import_export/handlers/option.py +54 -0
  148. wbfdm/import_export/handlers/private_equities.py +49 -0
  149. wbfdm/import_export/parsers/__init__.py +0 -0
  150. wbfdm/import_export/parsers/cbinsights/__init__.py +0 -0
  151. wbfdm/import_export/parsers/cbinsights/deals.py +39 -0
  152. wbfdm/import_export/parsers/cbinsights/equities.py +56 -0
  153. wbfdm/import_export/parsers/cbinsights/fundamentals.py +45 -0
  154. wbfdm/import_export/parsers/refinitiv/__init__.py +0 -0
  155. wbfdm/import_export/parsers/refinitiv/daily_fundamental.py +7 -0
  156. wbfdm/import_export/parsers/refinitiv/forecast.py +7 -0
  157. wbfdm/import_export/parsers/refinitiv/fundamental.py +9 -0
  158. wbfdm/import_export/parsers/refinitiv/geographic_segment.py +7 -0
  159. wbfdm/import_export/parsers/refinitiv/instrument.py +75 -0
  160. wbfdm/import_export/parsers/refinitiv/instrument_price.py +26 -0
  161. wbfdm/import_export/parsers/refinitiv/utils.py +96 -0
  162. wbfdm/import_export/resources/__init__.py +0 -0
  163. wbfdm/import_export/resources/classification.py +23 -0
  164. wbfdm/import_export/resources/instrument_prices.py +33 -0
  165. wbfdm/import_export/resources/instruments.py +176 -0
  166. wbfdm/jinja2.py +7 -0
  167. wbfdm/management/__init__.py +30 -0
  168. wbfdm/menu.py +11 -0
  169. wbfdm/migrations/0001_initial.py +71 -0
  170. wbfdm/migrations/0002_rename_statements_instrumentlookup_financials_and_more.py +144 -0
  171. wbfdm/migrations/0003_instrument_estimate_backend_and_more.py +34 -0
  172. wbfdm/migrations/0004_rename_financials_instrumentlookup_statements_and_more.py +86 -0
  173. wbfdm/migrations/0005_instrument_corporate_action_backend.py +29 -0
  174. wbfdm/migrations/0006_instrument_officer_backend.py +29 -0
  175. wbfdm/migrations/0007_instrument_country_instrument_currency_and_more.py +117 -0
  176. wbfdm/migrations/0008_controversy.py +75 -0
  177. wbfdm/migrations/0009_alter_controversy_flag_alter_controversy_initiated_and_more.py +85 -0
  178. wbfdm/migrations/0010_classification_classificationgroup_deal_exchange_and_more.py +1299 -0
  179. wbfdm/migrations/0011_delete_instrumentlookup_instrument_corporate_actions_and_more.py +169 -0
  180. wbfdm/migrations/0012_instrumentprice_created_instrumentprice_modified.py +564 -0
  181. wbfdm/migrations/0013_instrument_is_investable_universe_and_more.py +199 -0
  182. wbfdm/migrations/0014_alter_controversy_instrument.py +22 -0
  183. wbfdm/migrations/0015_instrument_instrument_investible_index.py +16 -0
  184. wbfdm/migrations/0016_instrumenttype_name_repr.py +18 -0
  185. wbfdm/migrations/0017_instrument_instrument_security_index.py +16 -0
  186. wbfdm/migrations/0018_instrument_instrument_level_index.py +20 -0
  187. wbfdm/migrations/0019_alter_controversy_source.py +17 -0
  188. wbfdm/migrations/0020_optionaggregate_option_and_more.py +249 -0
  189. wbfdm/migrations/0021_delete_instrumentdailystatistics.py +15 -0
  190. wbfdm/migrations/0022_instrument_cusip_option_open_interest_20d_and_more.py +91 -0
  191. wbfdm/migrations/0023_instrument_unique_ric_instrument_unique_rmc_and_more.py +53 -0
  192. wbfdm/migrations/0024_option_open_interest_10d_option_volume_10d_and_more.py +36 -0
  193. wbfdm/migrations/0025_instrument_is_primary_and_more.py +29 -0
  194. wbfdm/migrations/0026_instrument_is_cash_equivalent.py +30 -0
  195. wbfdm/migrations/0027_remove_instrument_unique_ric_and_more.py +100 -0
  196. wbfdm/migrations/__init__.py +0 -0
  197. wbfdm/models/__init__.py +4 -0
  198. wbfdm/models/esg/__init__.py +1 -0
  199. wbfdm/models/esg/controversies.py +81 -0
  200. wbfdm/models/exchanges/__init__.py +1 -0
  201. wbfdm/models/exchanges/exchanges.py +223 -0
  202. wbfdm/models/fields.py +117 -0
  203. wbfdm/models/fk_fields.py +403 -0
  204. wbfdm/models/indicators.py +0 -0
  205. wbfdm/models/instruments/__init__.py +19 -0
  206. wbfdm/models/instruments/classifications.py +265 -0
  207. wbfdm/models/instruments/instrument_lists.py +120 -0
  208. wbfdm/models/instruments/instrument_prices.py +540 -0
  209. wbfdm/models/instruments/instrument_relationships.py +251 -0
  210. wbfdm/models/instruments/instrument_requests.py +196 -0
  211. wbfdm/models/instruments/instruments.py +991 -0
  212. wbfdm/models/instruments/llm/__init__.py +1 -0
  213. wbfdm/models/instruments/llm/create_instrument_news_relationships.py +78 -0
  214. wbfdm/models/instruments/mixin/__init__.py +0 -0
  215. wbfdm/models/instruments/mixin/financials_computed.py +804 -0
  216. wbfdm/models/instruments/mixin/financials_serializer_fields.py +1407 -0
  217. wbfdm/models/instruments/mixin/instruments.py +294 -0
  218. wbfdm/models/instruments/options.py +225 -0
  219. wbfdm/models/instruments/private_equities.py +59 -0
  220. wbfdm/models/instruments/querysets.py +73 -0
  221. wbfdm/models/instruments/utils.py +41 -0
  222. wbfdm/preferences.py +21 -0
  223. wbfdm/serializers/__init__.py +4 -0
  224. wbfdm/serializers/esg.py +36 -0
  225. wbfdm/serializers/exchanges.py +39 -0
  226. wbfdm/serializers/instruments/__init__.py +37 -0
  227. wbfdm/serializers/instruments/classifications.py +139 -0
  228. wbfdm/serializers/instruments/instrument_lists.py +61 -0
  229. wbfdm/serializers/instruments/instrument_prices.py +73 -0
  230. wbfdm/serializers/instruments/instrument_relationships.py +170 -0
  231. wbfdm/serializers/instruments/instrument_requests.py +61 -0
  232. wbfdm/serializers/instruments/instruments.py +274 -0
  233. wbfdm/serializers/instruments/mixins.py +104 -0
  234. wbfdm/serializers/officers.py +20 -0
  235. wbfdm/signals.py +7 -0
  236. wbfdm/sync/__init__.py +0 -0
  237. wbfdm/sync/abstract.py +31 -0
  238. wbfdm/sync/runner.py +22 -0
  239. wbfdm/tasks.py +69 -0
  240. wbfdm/tests/__init__.py +0 -0
  241. wbfdm/tests/analysis/__init__.py +0 -0
  242. wbfdm/tests/analysis/financial_analysis/__init__.py +0 -0
  243. wbfdm/tests/analysis/financial_analysis/test_statement_with_estimates.py +392 -0
  244. wbfdm/tests/analysis/financial_analysis/test_utils.py +322 -0
  245. wbfdm/tests/analysis/test_esg.py +159 -0
  246. wbfdm/tests/conftest.py +92 -0
  247. wbfdm/tests/dataloaders/__init__.py +0 -0
  248. wbfdm/tests/dataloaders/test_cache.py +73 -0
  249. wbfdm/tests/models/__init__.py +0 -0
  250. wbfdm/tests/models/test_classifications.py +99 -0
  251. wbfdm/tests/models/test_exchanges.py +7 -0
  252. wbfdm/tests/models/test_instrument_list.py +117 -0
  253. wbfdm/tests/models/test_instrument_prices.py +306 -0
  254. wbfdm/tests/models/test_instruments.py +202 -0
  255. wbfdm/tests/models/test_merge.py +99 -0
  256. wbfdm/tests/models/test_options.py +69 -0
  257. wbfdm/tests/test_tasks.py +6 -0
  258. wbfdm/tests/tests.py +10 -0
  259. wbfdm/urls.py +222 -0
  260. wbfdm/utils.py +54 -0
  261. wbfdm/viewsets/__init__.py +10 -0
  262. wbfdm/viewsets/configs/__init__.py +5 -0
  263. wbfdm/viewsets/configs/buttons/__init__.py +8 -0
  264. wbfdm/viewsets/configs/buttons/classifications.py +23 -0
  265. wbfdm/viewsets/configs/buttons/exchanges.py +9 -0
  266. wbfdm/viewsets/configs/buttons/instrument_prices.py +49 -0
  267. wbfdm/viewsets/configs/buttons/instruments.py +283 -0
  268. wbfdm/viewsets/configs/display/__init__.py +22 -0
  269. wbfdm/viewsets/configs/display/classifications.py +138 -0
  270. wbfdm/viewsets/configs/display/esg.py +75 -0
  271. wbfdm/viewsets/configs/display/exchanges.py +42 -0
  272. wbfdm/viewsets/configs/display/instrument_lists.py +137 -0
  273. wbfdm/viewsets/configs/display/instrument_prices.py +199 -0
  274. wbfdm/viewsets/configs/display/instrument_requests.py +116 -0
  275. wbfdm/viewsets/configs/display/instruments.py +618 -0
  276. wbfdm/viewsets/configs/display/instruments_relationships.py +65 -0
  277. wbfdm/viewsets/configs/display/monthly_performances.py +72 -0
  278. wbfdm/viewsets/configs/display/officers.py +16 -0
  279. wbfdm/viewsets/configs/display/prices.py +21 -0
  280. wbfdm/viewsets/configs/display/statement_with_estimates.py +101 -0
  281. wbfdm/viewsets/configs/display/statements.py +48 -0
  282. wbfdm/viewsets/configs/endpoints/__init__.py +41 -0
  283. wbfdm/viewsets/configs/endpoints/classifications.py +87 -0
  284. wbfdm/viewsets/configs/endpoints/esg.py +20 -0
  285. wbfdm/viewsets/configs/endpoints/exchanges.py +6 -0
  286. wbfdm/viewsets/configs/endpoints/financials_analysis.py +65 -0
  287. wbfdm/viewsets/configs/endpoints/instrument_lists.py +38 -0
  288. wbfdm/viewsets/configs/endpoints/instrument_prices.py +51 -0
  289. wbfdm/viewsets/configs/endpoints/instrument_requests.py +20 -0
  290. wbfdm/viewsets/configs/endpoints/instruments.py +13 -0
  291. wbfdm/viewsets/configs/endpoints/instruments_relationships.py +31 -0
  292. wbfdm/viewsets/configs/endpoints/statements.py +6 -0
  293. wbfdm/viewsets/configs/menus/__init__.py +9 -0
  294. wbfdm/viewsets/configs/menus/classifications.py +19 -0
  295. wbfdm/viewsets/configs/menus/exchanges.py +10 -0
  296. wbfdm/viewsets/configs/menus/instrument_lists.py +10 -0
  297. wbfdm/viewsets/configs/menus/instruments.py +20 -0
  298. wbfdm/viewsets/configs/menus/instruments_relationships.py +33 -0
  299. wbfdm/viewsets/configs/titles/__init__.py +42 -0
  300. wbfdm/viewsets/configs/titles/classifications.py +79 -0
  301. wbfdm/viewsets/configs/titles/esg.py +11 -0
  302. wbfdm/viewsets/configs/titles/exchanges.py +12 -0
  303. wbfdm/viewsets/configs/titles/financial_ratio_analysis.py +6 -0
  304. wbfdm/viewsets/configs/titles/financials_analysis.py +50 -0
  305. wbfdm/viewsets/configs/titles/instrument_prices.py +50 -0
  306. wbfdm/viewsets/configs/titles/instrument_requests.py +16 -0
  307. wbfdm/viewsets/configs/titles/instruments.py +31 -0
  308. wbfdm/viewsets/configs/titles/instruments_relationships.py +21 -0
  309. wbfdm/viewsets/configs/titles/market_data.py +13 -0
  310. wbfdm/viewsets/configs/titles/prices.py +15 -0
  311. wbfdm/viewsets/configs/titles/statement_with_estimates.py +10 -0
  312. wbfdm/viewsets/esg.py +72 -0
  313. wbfdm/viewsets/exchanges.py +63 -0
  314. wbfdm/viewsets/financial_analysis/__init__.py +3 -0
  315. wbfdm/viewsets/financial_analysis/financial_metric_analysis.py +85 -0
  316. wbfdm/viewsets/financial_analysis/financial_ratio_analysis.py +85 -0
  317. wbfdm/viewsets/financial_analysis/statement_with_estimates.py +145 -0
  318. wbfdm/viewsets/instruments/__init__.py +80 -0
  319. wbfdm/viewsets/instruments/classifications.py +279 -0
  320. wbfdm/viewsets/instruments/financials_analysis.py +614 -0
  321. wbfdm/viewsets/instruments/instrument_lists.py +77 -0
  322. wbfdm/viewsets/instruments/instrument_prices.py +542 -0
  323. wbfdm/viewsets/instruments/instrument_requests.py +51 -0
  324. wbfdm/viewsets/instruments/instruments.py +106 -0
  325. wbfdm/viewsets/instruments/instruments_relationships.py +235 -0
  326. wbfdm/viewsets/instruments/utils.py +27 -0
  327. wbfdm/viewsets/market_data.py +172 -0
  328. wbfdm/viewsets/mixins.py +9 -0
  329. wbfdm/viewsets/officers.py +27 -0
  330. wbfdm/viewsets/prices.py +62 -0
  331. wbfdm/viewsets/statements/__init__.py +1 -0
  332. wbfdm/viewsets/statements/statements.py +100 -0
  333. wbfdm/viewsets/technical_analysis/__init__.py +1 -0
  334. wbfdm/viewsets/technical_analysis/monthly_performances.py +93 -0
  335. wbfdm-2.2.1.dist-info/METADATA +15 -0
  336. wbfdm-2.2.1.dist-info/RECORD +337 -0
  337. wbfdm-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,322 @@
1
+ import random
2
+ from datetime import date
3
+ from unittest.mock import patch
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ import pytest
8
+ from faker import Faker
9
+ from wbfdm.analysis.financial_analysis.utils import FinancialAnalysisResult, Loader
10
+ from wbfdm.dataloaders.proxies import InstrumentDataloaderProxy
11
+ from wbfdm.enums import Financial, MarketData
12
+
13
+ fake = Faker()
14
+
15
+
16
+ @pytest.mark.django_db
17
+ class TestLoader:
18
+ @pytest.fixture
19
+ def data(self, value, year):
20
+ """
21
+ Data Fixture for the Loader class
22
+ """
23
+ year = int(year)
24
+ data = []
25
+ fake_key = fake.word()
26
+ for year_delta in [0, 1]:
27
+ for interim in range(1, 5):
28
+ data.append(
29
+ {
30
+ "year": year + year_delta,
31
+ "interim": interim,
32
+ "period_type": "Q",
33
+ "period_end_date": date(year, interim * 3, 1),
34
+ "estimate": fake.pybool(),
35
+ "source": "dsws",
36
+ "financial": value,
37
+ "value": fake.pyfloat(),
38
+ fake_key: fake.word(), # add noise key
39
+ }
40
+ )
41
+ # add a uncomplete year for year - 2
42
+ data.append(
43
+ {
44
+ "year": year - 1,
45
+ "interim": 1,
46
+ "period_type": "Q",
47
+ "period_end_date": date(year, 3, 1),
48
+ "estimate": fake.pybool(),
49
+ "source": "dsws",
50
+ "financial": value,
51
+ "value": fake.pyfloat(),
52
+ fake_key: fake.word(), # add noise key
53
+ }
54
+ )
55
+ return data
56
+
57
+ @pytest.fixture()
58
+ def loader(self, instrument, value):
59
+ return Loader(instrument, [Financial(value)])
60
+
61
+ @pytest.mark.parametrize("value, year", [(random.choice(Financial.values()), fake.year())])
62
+ @patch.object(InstrumentDataloaderProxy, "financials")
63
+ def test_load(self, mock_fct, value, year, data, loader):
64
+ # Test that the load method returnn the normalized dataframe
65
+
66
+ mock_fct.return_value = data
67
+ pd.testing.assert_frame_equal(loader.load(), loader._normalize_df(*loader._get_base_df()), check_exact=True)
68
+
69
+ @pytest.mark.parametrize("value, year", [(random.choice(Financial.values()), fake.year())])
70
+ @patch.object(InstrumentDataloaderProxy, "financials")
71
+ def test_load_no_data(self, mock_fct, value, year, data, loader):
72
+ # Test that the load method returnn the normalized dataframe
73
+
74
+ mock_fct.return_value = []
75
+ assert loader.load().empty
76
+
77
+ @pytest.mark.parametrize("value, year", [(random.choice(Financial.values()), fake.year())])
78
+ @patch.object(InstrumentDataloaderProxy, "financials")
79
+ def test__get_base_df(self, mock_fct, value, year, data, loader):
80
+ year = int(year)
81
+ mock_fct.return_value = data
82
+ df, source_df = loader._get_base_df()
83
+ time_idx = df.index.droplevel([3, 4])
84
+ # Test if the _get_base_df private method returned tuple have the same index
85
+ pd.testing.assert_index_equal(time_idx, source_df.index)
86
+
87
+ # Test if the _get_base_df private method returned dataframe index corresponds to the expected year and interim
88
+ assert time_idx.tolist() == [
89
+ (year - 1, 1, "Q"),
90
+ (year, 1, "Q"),
91
+ (year, 2, "Q"),
92
+ (year, 3, "Q"),
93
+ (year, 4, "Q"),
94
+ (year + 1, 1, "Q"),
95
+ (year + 1, 2, "Q"),
96
+ (year + 1, 3, "Q"),
97
+ (year + 1, 4, "Q"),
98
+ ]
99
+ assert source_df.unique() == "dsws"
100
+ assert df.columns == [value]
101
+
102
+ @pytest.mark.parametrize("market_value", [random.choice(MarketData.values())])
103
+ @patch.object(InstrumentDataloaderProxy, "market_data")
104
+ def test__annotate_market_data(self, mock_fct, instrument, market_value):
105
+ # test if annotation market data works and are merged properly to the initial dataframe
106
+ d1 = date(2021, 3, 30)
107
+ d2 = date(2023, 3, 31)
108
+ p1 = fake.pyfloat()
109
+ p2 = fake.pyfloat()
110
+
111
+ df = pd.DataFrame(
112
+ [
113
+ {
114
+ "year": d1.year,
115
+ "estimate": False,
116
+ "interim": 1,
117
+ "period_type": "P",
118
+ "period_end_date": d1,
119
+ "financial": "financial",
120
+ "value": fake.pyfloat(),
121
+ },
122
+ {
123
+ "year": d2.year,
124
+ "estimate": False,
125
+ "interim": 1,
126
+ "period_type": "P",
127
+ "period_end_date": d2,
128
+ "financial": "financial",
129
+ "value": fake.pyfloat(),
130
+ },
131
+ ]
132
+ )
133
+ df["period_end_date"] = pd.to_datetime(df["period_end_date"])
134
+ df = df.pivot_table(
135
+ index=["year", "interim", "period_type", "estimate", "period_end_date"],
136
+ columns="financial",
137
+ values="value",
138
+ )
139
+ mock_fct.return_value = [{"valuation_date": d1, market_value: p1}, {"valuation_date": d2, market_value: p2}]
140
+ res = Loader(instrument, [])._annotate_market_data(df, [MarketData(market_value)])
141
+ assert res.loc[(d1.year, 1, "P", False, "2021-03-30"), market_value] == p1
142
+ assert res.loc[(d2.year, 1, "P", False, "2023-03-31"), market_value] == p2
143
+
144
+ @pytest.mark.parametrize("statement_value", [random.choice(Financial.values())])
145
+ @patch.object(InstrumentDataloaderProxy, "statements")
146
+ def test__annotate_statement_data(self, mock_fct, instrument, statement_value):
147
+ # test if annotation statement works and are merged properly to the initial dataframe
148
+
149
+ d1 = date(2021, 3, 30)
150
+ d2 = date(2023, 3, 31)
151
+ value = fake.pyfloat()
152
+
153
+ df = pd.DataFrame(
154
+ [
155
+ {
156
+ "year": d1.year,
157
+ "estimate": False,
158
+ "interim": 1,
159
+ "period_type": "P",
160
+ "period_end_date": d1,
161
+ "financial": "base",
162
+ "value": fake.pyfloat(),
163
+ },
164
+ {
165
+ "year": d2.year,
166
+ "estimate": False,
167
+ "interim": 1,
168
+ "period_type": "P",
169
+ "period_end_date": d2,
170
+ "financial": "base",
171
+ "value": fake.pyfloat(),
172
+ },
173
+ ]
174
+ )
175
+ df["period_end_date"] = pd.to_datetime(df["period_end_date"])
176
+ df = df.pivot_table(
177
+ index=["year", "interim", "period_type", "estimate", "period_end_date"],
178
+ columns="financial",
179
+ values="value",
180
+ )
181
+ mock_fct.return_value = [
182
+ {
183
+ "year": d1.year,
184
+ "estimate": False,
185
+ "interim": 1,
186
+ "period_type": "P",
187
+ "period_end_date": d1,
188
+ "financial": statement_value,
189
+ "value": value,
190
+ }
191
+ ]
192
+
193
+ res = Loader(instrument, [])._annotate_statement_data(df, [Financial(statement_value)])
194
+ assert res.loc[(d1.year, 1, "P", False, "2021-03-30"), statement_value] == value
195
+ assert np.isnan(res.loc[(d2.year, 1, "P", False, "2023-03-31"), statement_value])
196
+
197
+ def test__normalize_df(self, instrument):
198
+ d1 = date(2021, 3, 30)
199
+ df = pd.DataFrame(
200
+ [
201
+ {
202
+ "source": "dsws",
203
+ "year": d1.year,
204
+ "estimate": False,
205
+ "interim": 1,
206
+ "period_end_date": d1,
207
+ "period_type": "S",
208
+ "financial": "base",
209
+ "value": fake.pyfloat(),
210
+ },
211
+ {
212
+ "source": "dsws",
213
+ "year": d1.year,
214
+ "estimate": False,
215
+ "interim": 2,
216
+ "period_end_date": d1,
217
+ "period_type": "S",
218
+ "financial": "base",
219
+ "value": fake.pyfloat(),
220
+ },
221
+ ]
222
+ )
223
+ source_df = df.source
224
+ df["period_end_date"] = pd.to_datetime(df["period_end_date"])
225
+ df = df.pivot_table(
226
+ index=["year", "interim", "period_type", "estimate", "period_end_date"],
227
+ columns="financial",
228
+ values="value",
229
+ )
230
+ res = Loader(instrument, [])._normalize_df(df, source_df)
231
+ # Test if normalize appends the missing "yearly" row and convert the interim into their verbose representation
232
+ assert res.index.tolist() == [
233
+ (d1.year, "Y"),
234
+ (d1.year, "S1"),
235
+ (d1.year, "S2"),
236
+ ] # We test that the missing yearly row is reindexed
237
+ assert np.isnan(res.loc[(d1.year, "Y"), "base"]) # check that the extrapolated data is nan
238
+
239
+ def test__normalize_df_with_duplicates(self, instrument):
240
+ # we test if normalize detects the duplicates, remove it by taking the first and add the interim into the errors list
241
+ d1 = date(2021, 3, 30)
242
+ v1 = fake.pyfloat()
243
+ v2 = fake.pyfloat()
244
+ df = pd.DataFrame(
245
+ [
246
+ {
247
+ "source": "dsws",
248
+ "year": d1.year,
249
+ "estimate": False,
250
+ "interim": 1,
251
+ "period_end_date": d1,
252
+ "financial": "base",
253
+ "period_type": "P",
254
+ "value": v1,
255
+ },
256
+ {
257
+ "source": "dsws",
258
+ "year": d1.year,
259
+ "estimate": False,
260
+ "interim": 1,
261
+ "period_end_date": date(2021, 1, 30),
262
+ "financial": "base",
263
+ "period_type": "P",
264
+ "value": v2,
265
+ },
266
+ ]
267
+ )
268
+ source_df = df[["year", "interim", "source"]].groupby(["year", "interim"]).first().source
269
+ df["period_end_date"] = pd.to_datetime(df["period_end_date"])
270
+ df = df.pivot_table(
271
+ index=["year", "interim", "period_type", "estimate", "period_end_date"],
272
+ columns="financial",
273
+ values="value",
274
+ )
275
+ loader = Loader(instrument, [])
276
+ res = loader._normalize_df(df, source_df)
277
+ assert res.index.tolist() == [(d1.year, "Y"), (d1.year, "P1")]
278
+ assert (
279
+ res.loc[(d1.year, "P1"), "base"] == v2
280
+ ) # check that the retained value is the first one (with ordered index)
281
+ assert loader.errors["duplicated_interims"] == [f"{d1.year} Interim P1"]
282
+
283
+
284
+ class TestFinancialAnalysisResult:
285
+ @pytest.fixture()
286
+ def df(self, year, value):
287
+ df = pd.DataFrame(
288
+ [
289
+ {
290
+ "year": year,
291
+ "estimate": False,
292
+ "interim": "P1",
293
+ "period_end_date": date(int(year), 3, 1),
294
+ "financial": value,
295
+ "value": fake.pyfloat(),
296
+ },
297
+ {
298
+ "year": year,
299
+ "estimate": True,
300
+ "interim": "Y",
301
+ "period_end_date": date(int(year), 3, 1),
302
+ "financial": value,
303
+ "value": fake.pyfloat(),
304
+ },
305
+ ]
306
+ )
307
+ df = df.pivot_table(
308
+ index=["year", "interim", "estimate", "period_end_date"], columns="financial", values="value"
309
+ )
310
+ return df.reset_index(level=[2, 3])
311
+
312
+ @pytest.mark.parametrize("value, year", [(random.choice(Financial.values()), fake.year())])
313
+ def test_class(self, value, year, df):
314
+ """
315
+ Test the basic esthetic transform (Transposing + group key addition + convertion of multi index to representation
316
+ """
317
+ res = FinancialAnalysisResult(df)
318
+ assert res.formatted_df.loc[0, f"{year}-P1"] == df.loc[(year, "P1"), value]
319
+ assert res.formatted_df.loc[0, f"{year}-Y"] == df.loc[(year, "Y"), value]
320
+ assert res.formatted_df.loc[0, "financial"] == Financial.name_mapping()[value]
321
+ assert res.formatted_df.loc[0, "_group_key"] == value
322
+ assert res.estimated_mapping == {f"{year}-P1": False, f"{year}-Y": True}
@@ -0,0 +1,159 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import pytest
4
+ from faker import Faker
5
+ from wbcore.contrib.currency.factories import CurrencyFXRatesFactory
6
+ from wbcore.contrib.currency.models import CurrencyFXRates
7
+ from wbfdm.analysis.esg.enums import ESGAggregation
8
+ from wbfdm.analysis.esg.esg_analysis import DataLoader
9
+
10
+ fake = Faker()
11
+
12
+ WEIGHTS = [0.15, 0.05, 0.2, 0.1, 0.5]
13
+ ENTERPRISE_VALUE_INCLUDED_CASH = [1e6, 2e6, 3e6, 4e6, 1e6]
14
+ CURRENT_VALUE_INVESTMENT_FACTOR = [1.0, 1.0, 1.0, 1.2, 0.2]
15
+
16
+
17
+ @pytest.mark.django_db
18
+ class TestESGDataLoader:
19
+ @pytest.fixture
20
+ def dataloader(self, weekday):
21
+ index = [1, 2, 3, 4, 5]
22
+
23
+ weight = pd.Series(WEIGHTS, index=index)
24
+ esg_data = pd.Series(np.random.randint(1, 100, size=4), index=[1, 2, 3, 4]) # we remove one data point
25
+ total_value_fx_usd = pd.Series(np.random.randint(1, 100, size=5)) # we remove one data point
26
+ CurrencyFXRatesFactory.create(currency__key="EUR", date=weekday)
27
+
28
+ dataloader = DataLoader(weight, esg_data, weekday, total_value_fx_usd=total_value_fx_usd)
29
+ dataloader.__dict__["enterprise_value_included_cash"] = pd.Series(ENTERPRISE_VALUE_INCLUDED_CASH, index=index)
30
+ dataloader.__dict__["current_value_investment_factor"] = pd.Series(
31
+ CURRENT_VALUE_INVESTMENT_FACTOR, index=index
32
+ )
33
+ dataloader.__dict__["nace_section_code"] = pd.Series(["C", "C", "J", "M", "M"], index=index)
34
+ return dataloader
35
+
36
+ def test_dataloader(self, dataloader):
37
+ assert dataloader.weights_in_coverage.to_dict() == {1: 0.15, 2: 0.05, 3: 0.2, 4: 0.1}
38
+
39
+ def test_get_percentage_sum(self, dataloader):
40
+ dataloader.esg_data = pd.Series(["Yes", "No Evidence", "No", "No", "No"], index=[1, 2, 3, 4, 5])
41
+ res = dataloader._get_percentage_sum("No Evidence")
42
+ assert res.tolist() == [0.0, 0.05, 0.0, 0.0]
43
+
44
+ def test_get_weighted_avg_normalized(self, dataloader):
45
+ res = dataloader._get_weighted_avg_normalized()
46
+ log = dataloader.intermediary_logs[0]
47
+
48
+ # ensure the intermediary log data is set properly
49
+ assert log.series.name == "weights_normalized"
50
+ assert log.series.to_dict() == {1: 0.3, 2: 0.1, 3: 0.4, 4: 0.2}
51
+ assert res.to_dict() == {
52
+ 1: 0.3 * dataloader.esg_data.iloc[0],
53
+ 2: 0.1 * dataloader.esg_data.iloc[1],
54
+ 3: 0.4 * dataloader.esg_data.iloc[2],
55
+ 4: 0.2 * dataloader.esg_data.iloc[3],
56
+ }
57
+
58
+ def test_get_weighted_avg_normalized_per_category(self, dataloader):
59
+ res = dataloader._get_weighted_avg_normalized_per_category()
60
+ intermediary_logs = list(filter(lambda x: not x.series.empty, dataloader.intermediary_logs))
61
+ log_c = intermediary_logs[0]
62
+ log_j = intermediary_logs[1]
63
+ log_m = intermediary_logs[2]
64
+ weights_normalized = [
65
+ 0.15 / (0.15 + 0.05),
66
+ 0.05 / (0.15 + 0.05),
67
+ 0.2 / 0.2,
68
+ 0.1 / (0.1 + 0.5),
69
+ 0.5 / (0.1 + 0.5),
70
+ ]
71
+ # ensure the intermediary log data is set properly
72
+ assert log_c.series.name == "weights_normalized_sector_c"
73
+ assert log_c.series.to_dict() == {
74
+ 1: weights_normalized[0],
75
+ 2: weights_normalized[1],
76
+ # 3: weights_normalized[2],
77
+ # 4: weights_normalized[3],
78
+ # 5: weights_normalized[4],
79
+ }
80
+ assert log_j.series.name == "weights_normalized_sector_j"
81
+ assert log_j.series.to_dict() == {
82
+ 3: weights_normalized[2],
83
+ }
84
+ assert log_m.series.name == "weights_normalized_sector_m"
85
+ assert log_m.series.to_dict() == {
86
+ 4: weights_normalized[3],
87
+ 5: weights_normalized[4],
88
+ }
89
+ assert res.to_dict() == {
90
+ 1: weights_normalized[0] * dataloader.esg_data.iloc[0],
91
+ 2: weights_normalized[1] * dataloader.esg_data.iloc[1],
92
+ 3: weights_normalized[2] * dataloader.esg_data.iloc[2],
93
+ 4: weights_normalized[3] * dataloader.esg_data.iloc[3],
94
+ }
95
+
96
+ def test_get_investor_allocation(self, dataloader):
97
+ exposure = pd.Series(np.random.rand(5), index=dataloader.weights.index)
98
+ res = dataloader._get_investor_allocation(exposure)
99
+ fx_rate = CurrencyFXRates.objects.get(currency__key="EUR", date=dataloader.val_date)
100
+ exposure_eur = exposure * float(fx_rate.value)
101
+
102
+ assert dataloader.intermediary_logs[0].series.to_dict() == exposure.to_dict()
103
+ assert (
104
+ dataloader.intermediary_logs[1].series.to_dict() == exposure_eur.to_dict()
105
+ ) # exposure converted into eur
106
+
107
+ exposure_with_cvi = [exposure_eur.iloc[i] * CURRENT_VALUE_INVESTMENT_FACTOR[i] for i in range(5)]
108
+ assert dataloader.intermediary_logs[2].series.tolist() == exposure_with_cvi
109
+
110
+ rebase_factor = sum(exposure_with_cvi[:4]) / sum(exposure_with_cvi)
111
+
112
+ exposure_normalized = [exposure_with_cvi[i] / rebase_factor for i in range(4)]
113
+ assert dataloader.intermediary_logs[3].series.tolist() == exposure_normalized
114
+
115
+ attribution_factor = [exposure_normalized[i] / ENTERPRISE_VALUE_INCLUDED_CASH[i] for i in range(4)]
116
+ assert dataloader.intermediary_logs[4].series.dropna().tolist() == attribution_factor
117
+
118
+ assert res.to_dict() == {
119
+ 1: dataloader.esg_data.iloc[0] * attribution_factor[0],
120
+ 2: dataloader.esg_data.iloc[1] * attribution_factor[1],
121
+ 3: dataloader.esg_data.iloc[2] * attribution_factor[2],
122
+ 4: dataloader.esg_data.iloc[3] * attribution_factor[3],
123
+ }
124
+
125
+ def test_compute_percentage_sum(self, dataloader):
126
+ pd.testing.assert_series_equal(
127
+ dataloader.compute(ESGAggregation.FOSSIL_FUEL_EXPOSURE),
128
+ dataloader._get_percentage_sum("Yes"),
129
+ check_exact=True,
130
+ )
131
+
132
+ def test_compute_weighted_avg_normalized(self, dataloader):
133
+ res = dataloader.compute(ESGAggregation.GHG_INTENSITY_OF_COMPAGNIES)
134
+ assert res.empty is not None
135
+ pd.testing.assert_series_equal(
136
+ dataloader.compute(ESGAggregation.GHG_INTENSITY_OF_COMPAGNIES),
137
+ dataloader._get_weighted_avg_normalized(),
138
+ check_exact=True,
139
+ )
140
+
141
+ def test_compute_weighted_avg_normalized_per_category(self, dataloader):
142
+ res = dataloader.compute(ESGAggregation.ENERGY_CONSUMPTION_INTENSITY_PER_SECTOR)
143
+ assert res.empty is not None
144
+ pd.testing.assert_series_equal(res, dataloader._get_weighted_avg_normalized_per_category(), check_exact=True)
145
+
146
+ def test_compute_investor_allocation(self, dataloader):
147
+ res = dataloader.compute(ESGAggregation.GHG_EMISSIONS_SCOPE_1)
148
+ assert res.empty is not None
149
+ pd.testing.assert_series_equal(
150
+ res, dataloader._get_investor_allocation(dataloader.total_value_fx_usd), check_exact=True
151
+ )
152
+
153
+ res = dataloader.compute(ESGAggregation.GHG_EMISSIONS_SCOPE_1)
154
+ assert res.empty is not None
155
+ pd.testing.assert_series_equal(
156
+ dataloader.compute(ESGAggregation.EMISSIONS_TO_WATER),
157
+ dataloader._get_investor_allocation_per_million(dataloader.weights * 1000000),
158
+ check_exact=True,
159
+ )
@@ -0,0 +1,92 @@
1
+ from wbcore.tests.conftest import * # isort:skip
2
+ from datetime import date
3
+
4
+ from django.apps import apps
5
+ from django.db.models.signals import pre_migrate
6
+ from faker import Faker
7
+ from pandas.tseries.offsets import BDay
8
+ from pytest_factoryboy import register
9
+ from wbcore.contrib.authentication.factories import (
10
+ InternalUserFactory,
11
+ SuperUserFactory,
12
+ UserFactory,
13
+ )
14
+ from wbcore.contrib.currency.factories import CurrencyFactory, CurrencyFXRatesFactory
15
+ from wbcore.contrib.directory.factories.entries import (
16
+ CompanyFactory,
17
+ CompanyTypeFactory,
18
+ CustomerStatusFactory,
19
+ EntryFactory,
20
+ PersonFactory,
21
+ )
22
+ from wbcore.contrib.geography.factories import (
23
+ CityFactory,
24
+ ContinentFactory,
25
+ CountryFactory,
26
+ StateFactory,
27
+ )
28
+ from wbcore.contrib.geography.tests.signals import app_pre_migration
29
+ from wbcore.contrib.io.factories import (
30
+ CrontabScheduleFactory,
31
+ DataBackendFactory,
32
+ ImportSourceFactory,
33
+ ParserHandlerFactory,
34
+ ProviderFactory,
35
+ SourceFactory,
36
+ )
37
+ from wbfdm.factories import (
38
+ ClassificationFactory,
39
+ ClassificationGroupFactory,
40
+ ExchangeFactory,
41
+ InstrumentClassificationThroughModelFactory,
42
+ InstrumentFactory,
43
+ InstrumentFavoriteGroupFactory,
44
+ InstrumentListFactory,
45
+ InstrumentPriceFactory,
46
+ InstrumentTypeFactory,
47
+ OptionAggregateFactory,
48
+ OptionFactory,
49
+ ParentClassificationFactory,
50
+ RelatedInstrumentThroughModelFactory,
51
+ )
52
+
53
+ fake = Faker()
54
+ register(ImportSourceFactory)
55
+ register(DataBackendFactory)
56
+ register(ProviderFactory)
57
+ register(SourceFactory)
58
+ register(ParserHandlerFactory)
59
+ register(CrontabScheduleFactory)
60
+
61
+ register(ClassificationFactory)
62
+ register(InstrumentClassificationThroughModelFactory)
63
+ register(ParentClassificationFactory)
64
+ register(ClassificationGroupFactory)
65
+ register(ExchangeFactory)
66
+ register(InstrumentFactory)
67
+ register(InstrumentTypeFactory)
68
+ register(InstrumentPriceFactory)
69
+ register(InstrumentFavoriteGroupFactory)
70
+ register(RelatedInstrumentThroughModelFactory)
71
+ register(CurrencyFXRatesFactory)
72
+ register(InstrumentListFactory)
73
+ register(OptionFactory)
74
+ register(OptionAggregateFactory)
75
+
76
+ register(CurrencyFactory)
77
+ register(CityFactory)
78
+ register(StateFactory)
79
+ register(CountryFactory)
80
+ register(ContinentFactory)
81
+
82
+ register(CompanyFactory)
83
+ register(PersonFactory)
84
+ register(InternalUserFactory)
85
+ register(EntryFactory)
86
+ register(CustomerStatusFactory)
87
+ register(CompanyTypeFactory)
88
+
89
+ register(UserFactory)
90
+ register(SuperUserFactory, "superuser")
91
+
92
+ pre_migrate.connect(app_pre_migration, sender=apps.get_app_config("wbfdm"))
File without changes
@@ -0,0 +1,73 @@
1
+ import pytest
2
+ from django.core.cache import cache as cache_layer
3
+ from faker import Faker
4
+ from wbfdm.dataloaders.cache import Cache
5
+
6
+ fake = Faker()
7
+
8
+
9
+ class TestCache:
10
+ @pytest.fixture
11
+ def cache(self):
12
+ return Cache()
13
+
14
+ def test_initialize(self, cache):
15
+ cache.missing_keys = ["a"]
16
+ cache.missing_ids = ("a",)
17
+ cache.missing_symbols = ("a",)
18
+ cache.initialize([1], ["a", "b"])
19
+
20
+ # ensure initialize reset the missing attributes set
21
+ assert cache.missing_keys == list()
22
+ assert cache.missing_ids == set()
23
+ assert cache.missing_symbols == set()
24
+ assert cache.id_symbol_pairs == [("1", "a"), ("1", "b")]
25
+
26
+ cache.initialize([1, 2], ["a"])
27
+ assert cache.id_symbol_pairs == [("1", "a"), ("2", "a")]
28
+
29
+ def test__get_cache_key(self, cache):
30
+ assert cache._get_cache_key("a", "b") == "a_b"
31
+
32
+ def test__deserialize_cache(self, cache):
33
+ assert cache._deserialize_cache("1", "symbol", "value") == {
34
+ cache.identifier_key: 1,
35
+ cache.symbol_key: "symbol",
36
+ cache.value_key: "value",
37
+ }
38
+
39
+ def test_fetch_from_cache(self, cache):
40
+ cache.initialize([1], ["a"])
41
+ assert list(cache.fetch_from_cache()) == list()
42
+ assert cache.missing_keys == ["1_a"]
43
+ assert cache.missing_symbols == {"a"}
44
+ assert cache.missing_ids == {"1"}
45
+
46
+ cache.initialize([1], ["a"])
47
+ assert list(cache.fetch_from_cache()) == []
48
+ assert cache.missing_keys == ["1_a"]
49
+
50
+ value = fake.pyfloat()
51
+ cache_layer.set("1_a", value)
52
+ cache.initialize([1], ["a"])
53
+ assert list(cache.fetch_from_cache()) == [{"instrument_id": 1, "factor_code": "a", "value": value}]
54
+ assert cache.missing_keys == list()
55
+
56
+ cache_layer.set("1_a", None)
57
+ cache.initialize([1], ["a"])
58
+ assert list(cache.fetch_from_cache()) == list()
59
+ assert cache.missing_keys == list()
60
+
61
+ def test_write(self, cache):
62
+ cache.missing_keys = ["1_a"]
63
+ value = fake.pyfloat()
64
+ cache.write({"instrument_id": 1, "factor_code": "a", "value": value})
65
+ assert cache.missing_keys == list()
66
+ assert cache_layer.get("1_a") == value
67
+
68
+ def test_close(self, cache):
69
+ cache.missing_keys = ["3_c"]
70
+ sentinel = object()
71
+ assert cache_layer.get("3_c", sentinel) == sentinel
72
+ cache.close()
73
+ assert cache_layer.get("3_c", sentinel) is None
File without changes