customeranalytics 0.1__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.
Files changed (254) hide show
  1. customeranalytics/__init__.py +319 -0
  2. customeranalytics/configs.py +480 -0
  3. customeranalytics/data_storage_configurations/__init__.py +245 -0
  4. customeranalytics/data_storage_configurations/data_access.py +165 -0
  5. customeranalytics/data_storage_configurations/data_works_pipeline.py +207 -0
  6. customeranalytics/data_storage_configurations/es_create_index.py +544 -0
  7. customeranalytics/data_storage_configurations/logger.py +36 -0
  8. customeranalytics/data_storage_configurations/query_es.py +157 -0
  9. customeranalytics/data_storage_configurations/reports.py +684 -0
  10. customeranalytics/data_storage_configurations/schedule_data_integration.py +355 -0
  11. customeranalytics/docs/queries.yaml +245 -0
  12. customeranalytics/docs/web_configs.yaml +2 -0
  13. customeranalytics/exploratory_analysis/__init__.py +109 -0
  14. customeranalytics/exploratory_analysis/churn.py +232 -0
  15. customeranalytics/exploratory_analysis/cohorts.py +535 -0
  16. customeranalytics/exploratory_analysis/descriptive_statistics.py +470 -0
  17. customeranalytics/exploratory_analysis/funnels.py +407 -0
  18. customeranalytics/exploratory_analysis/product_analytics.py +340 -0
  19. customeranalytics/exploratory_analysis/promotion_analytics.py +299 -0
  20. customeranalytics/exploratory_analysis/rfm.py +202 -0
  21. customeranalytics/exploratory_analysis/sample_data/sample_data_avg_order_count_per_promo_per_cust.csv +99 -0
  22. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_1_search.csv +2 -0
  23. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_2_search.csv +46 -0
  24. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_3_search.csv +2 -0
  25. customeranalytics/exploratory_analysis/sample_data/sample_data_chart_4_search.csv +2 -0
  26. customeranalytics/exploratory_analysis/sample_data/sample_data_churn.csv +2 -0
  27. customeranalytics/exploratory_analysis/sample_data/sample_data_churn_weekly.csv +10 -0
  28. customeranalytics/exploratory_analysis/sample_data/sample_data_client_feature_predicted.csv +1590 -0
  29. customeranalytics/exploratory_analysis/sample_data/sample_data_client_kpis.csv +7060 -0
  30. customeranalytics/exploratory_analysis/sample_data/sample_data_clvrfm_anomaly.csv +561 -0
  31. customeranalytics/exploratory_analysis/sample_data/sample_data_clvsegments_amount.csv +8 -0
  32. customeranalytics/exploratory_analysis/sample_data/sample_data_customer_journey.csv +18 -0
  33. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_clv.csv +103 -0
  34. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_downloads.csv +68 -0
  35. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_1_to_2.csv +66 -0
  36. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_2_to_3.csv +64 -0
  37. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_cohort_from_3_to_4.csv +55 -0
  38. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_dimension_values.csv +199 -0
  39. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_funnel.csv +36 -0
  40. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_funnel_downloads.csv +68 -0
  41. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_inorganic_ratio.csv +6070 -0
  42. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_orders.csv +57 -0
  43. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_organic_orders.csv +64 -0
  44. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_products_of_sales.csv +12574 -0
  45. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_promotion_discount.csv +64 -0
  46. customeranalytics/exploratory_analysis/sample_data/sample_data_daily_promotion_revenue.csv +6070 -0
  47. customeranalytics/exploratory_analysis/sample_data/sample_data_dcohort_anomaly.csv +6 -0
  48. customeranalytics/exploratory_analysis/sample_data/sample_data_dcohort_anomaly_2.csv +118 -0
  49. customeranalytics/exploratory_analysis/sample_data/sample_data_deliver.csv +47 -0
  50. customeranalytics/exploratory_analysis/sample_data/sample_data_deliver_kpis.csv +2 -0
  51. customeranalytics/exploratory_analysis/sample_data/sample_data_deliver_weekday_hour.csv +25 -0
  52. customeranalytics/exploratory_analysis/sample_data/sample_data_dfunnel_anomaly.csv +118 -0
  53. customeranalytics/exploratory_analysis/sample_data/sample_data_dimension_kpis.csv +199 -0
  54. customeranalytics/exploratory_analysis/sample_data/sample_data_dorders_anomaly.csv +85 -0
  55. customeranalytics/exploratory_analysis/sample_data/sample_data_frequency_clusters.csv +6 -0
  56. customeranalytics/exploratory_analysis/sample_data/sample_data_frequency_recency.csv +3740 -0
  57. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_funnel.csv +25 -0
  58. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_funnel_downloads.csv +25 -0
  59. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_inorganic_ratio.csv +2353 -0
  60. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_orders.csv +25 -0
  61. customeranalytics/exploratory_analysis/sample_data/sample_data_hourly_organic_orders.csv +2353 -0
  62. customeranalytics/exploratory_analysis/sample_data/sample_data_inorganic_orders_per_promotion_per_day.csv +6070 -0
  63. customeranalytics/exploratory_analysis/sample_data/sample_data_kpis.csv +2 -0
  64. customeranalytics/exploratory_analysis/sample_data/sample_data_monetary_clusters.csv +6 -0
  65. customeranalytics/exploratory_analysis/sample_data/sample_data_monetary_frequency.csv +3740 -0
  66. customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_funnel.csv +5 -0
  67. customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_funnel_downloads.csv +5 -0
  68. customeranalytics/exploratory_analysis/sample_data/sample_data_monthly_orders.csv +4 -0
  69. customeranalytics/exploratory_analysis/sample_data/sample_data_most_combined_products.csv +11 -0
  70. customeranalytics/exploratory_analysis/sample_data/sample_data_most_ordered_categories.csv +11 -0
  71. customeranalytics/exploratory_analysis/sample_data/sample_data_most_ordered_products.csv +11 -0
  72. customeranalytics/exploratory_analysis/sample_data/sample_data_order_and_payment_amount_differences.csv +91 -0
  73. customeranalytics/exploratory_analysis/sample_data/sample_data_prepare.csv +101 -0
  74. customeranalytics/exploratory_analysis/sample_data/sample_data_prepare_weekday_hour.csv +25 -0
  75. customeranalytics/exploratory_analysis/sample_data/sample_data_product_kpis.csv +201 -0
  76. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_amount_accept.csv +11 -0
  77. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_amount_reject.csv +7 -0
  78. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_orders_accept.csv +11 -0
  79. customeranalytics/exploratory_analysis/sample_data/sample_data_product_usage_before_after_orders_reject.csv +11 -0
  80. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_comparison.csv +91 -0
  81. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_kpis.csv +99 -0
  82. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_number_of_customer.csv +99 -0
  83. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_amount_accept.csv +81 -0
  84. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_amount_reject.csv +11 -0
  85. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_orders_accept.csv +55 -0
  86. customeranalytics/exploratory_analysis/sample_data/sample_data_promotion_usage_before_after_orders_reject.csv +37 -0
  87. customeranalytics/exploratory_analysis/sample_data/sample_data_purchase_amount_distribution.csv +22 -0
  88. customeranalytics/exploratory_analysis/sample_data/sample_data_recency_clusters.csv +6 -0
  89. customeranalytics/exploratory_analysis/sample_data/sample_data_recency_monetary.csv +3740 -0
  90. customeranalytics/exploratory_analysis/sample_data/sample_data_rfm.csv +2001 -0
  91. customeranalytics/exploratory_analysis/sample_data/sample_data_ride.csv +101 -0
  92. customeranalytics/exploratory_analysis/sample_data/sample_data_ride_weekday_hour.csv +25 -0
  93. customeranalytics/exploratory_analysis/sample_data/sample_data_segmentation.csv +11 -0
  94. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_daily_before_after_amount.csv +10 -0
  95. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_daily_before_after_orders.csv +9 -0
  96. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_monthly_before_after_amount.csv +10 -0
  97. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_monthly_before_after_orders.csv +11 -0
  98. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_weekly_before_after_amount.csv +10 -0
  99. customeranalytics/exploratory_analysis/sample_data/sample_data_segments_change_weekly_before_after_orders.csv +10 -0
  100. customeranalytics/exploratory_analysis/sample_data/sample_data_user_counts_per_order_seq.csv +47 -0
  101. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_order_per_user.csv +13 -0
  102. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_payment_amount.csv +13 -0
  103. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_average_session_per_user.csv +13 -0
  104. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_downloads.csv +13 -0
  105. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_1_to_2.csv +13 -0
  106. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_2_to_3.csv +13 -0
  107. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_cohort_from_3_to_4.csv +12 -0
  108. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_funnel.csv +25 -0
  109. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_funnel_downloads.csv +25 -0
  110. customeranalytics/exploratory_analysis/sample_data/sample_data_weekly_orders.csv +10 -0
  111. customeranalytics/ml_process/__init__.py +64 -0
  112. customeranalytics/ml_process/ab_test.py +615 -0
  113. customeranalytics/ml_process/anomaly_detection.py +456 -0
  114. customeranalytics/ml_process/clv_prediction.py +301 -0
  115. customeranalytics/ml_process/customer_segmentation.py +354 -0
  116. customeranalytics/ml_process/delivery_analytics.py +754 -0
  117. customeranalytics/utils.py +186 -0
  118. customeranalytics/web/app/__init__.py +37 -0
  119. customeranalytics/web/app/base/__init__.py +9 -0
  120. customeranalytics/web/app/base/forms.py +16 -0
  121. customeranalytics/web/app/base/models.py +43 -0
  122. customeranalytics/web/app/base/routes.py +126 -0
  123. customeranalytics/web/app/base/static/assets/css/app.css +20 -0
  124. customeranalytics/web/app/base/static/assets/fonts/Inter-Black.woff +0 -0
  125. customeranalytics/web/app/base/static/assets/fonts/Inter-Black.woff2 +0 -0
  126. customeranalytics/web/app/base/static/assets/fonts/Inter-BlackItalic.woff +0 -0
  127. customeranalytics/web/app/base/static/assets/fonts/Inter-BlackItalic.woff2 +0 -0
  128. customeranalytics/web/app/base/static/assets/fonts/Inter-Bold.woff +0 -0
  129. customeranalytics/web/app/base/static/assets/fonts/Inter-Bold.woff2 +0 -0
  130. customeranalytics/web/app/base/static/assets/fonts/Inter-BoldItalic.woff +0 -0
  131. customeranalytics/web/app/base/static/assets/fonts/Inter-BoldItalic.woff2 +0 -0
  132. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBold.woff +0 -0
  133. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBold.woff2 +0 -0
  134. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBoldItalic.woff +0 -0
  135. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraBoldItalic.woff2 +0 -0
  136. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLight-BETA.woff +0 -0
  137. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLight-BETA.woff2 +0 -0
  138. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLightItalic-BETA.woff +0 -0
  139. customeranalytics/web/app/base/static/assets/fonts/Inter-ExtraLightItalic-BETA.woff2 +0 -0
  140. customeranalytics/web/app/base/static/assets/fonts/Inter-Italic.woff +0 -0
  141. customeranalytics/web/app/base/static/assets/fonts/Inter-Italic.woff2 +0 -0
  142. customeranalytics/web/app/base/static/assets/fonts/Inter-Light-BETA.woff +0 -0
  143. customeranalytics/web/app/base/static/assets/fonts/Inter-Light-BETA.woff2 +0 -0
  144. customeranalytics/web/app/base/static/assets/fonts/Inter-LightItalic-BETA.woff +0 -0
  145. customeranalytics/web/app/base/static/assets/fonts/Inter-LightItalic-BETA.woff2 +0 -0
  146. customeranalytics/web/app/base/static/assets/fonts/Inter-Medium.woff +0 -0
  147. customeranalytics/web/app/base/static/assets/fonts/Inter-Medium.woff2 +0 -0
  148. customeranalytics/web/app/base/static/assets/fonts/Inter-MediumItalic.woff +0 -0
  149. customeranalytics/web/app/base/static/assets/fonts/Inter-MediumItalic.woff2 +0 -0
  150. customeranalytics/web/app/base/static/assets/fonts/Inter-Regular.woff +0 -0
  151. customeranalytics/web/app/base/static/assets/fonts/Inter-Regular.woff2 +0 -0
  152. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBold.woff +0 -0
  153. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBold.woff2 +0 -0
  154. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBoldItalic.woff +0 -0
  155. customeranalytics/web/app/base/static/assets/fonts/Inter-SemiBoldItalic.woff2 +0 -0
  156. customeranalytics/web/app/base/static/assets/fonts/Inter-Thin-BETA.woff +0 -0
  157. customeranalytics/web/app/base/static/assets/fonts/Inter-Thin-BETA.woff2 +0 -0
  158. customeranalytics/web/app/base/static/assets/fonts/Inter-ThinItalic-BETA.woff +0 -0
  159. customeranalytics/web/app/base/static/assets/fonts/Inter-ThinItalic-BETA.woff2 +0 -0
  160. customeranalytics/web/app/base/static/assets/fonts/Inter-italic.var.woff2 +0 -0
  161. customeranalytics/web/app/base/static/assets/fonts/Inter-upright.var.woff2 +0 -0
  162. customeranalytics/web/app/base/static/assets/fonts/Inter.var.woff2 +0 -0
  163. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.eot +0 -0
  164. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.svg +3570 -0
  165. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.ttf +0 -0
  166. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.woff +0 -0
  167. customeranalytics/web/app/base/static/assets/fonts/fa-brands-400.woff2 +0 -0
  168. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.eot +0 -0
  169. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.svg +801 -0
  170. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.ttf +0 -0
  171. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.woff +0 -0
  172. customeranalytics/web/app/base/static/assets/fonts/fa-regular-400.woff2 +0 -0
  173. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.eot +0 -0
  174. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.svg +5028 -0
  175. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.ttf +0 -0
  176. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.woff +0 -0
  177. customeranalytics/web/app/base/static/assets/fonts/fa-solid-900.woff2 +0 -0
  178. customeranalytics/web/app/base/static/assets/img/avatars/avatar-1.png +0 -0
  179. customeranalytics/web/app/base/static/assets/img/avatars/avatar-2.png +0 -0
  180. customeranalytics/web/app/base/static/assets/img/avatars/avatar-3.png +0 -0
  181. customeranalytics/web/app/base/static/assets/img/avatars/avatar-4.png +0 -0
  182. customeranalytics/web/app/base/static/assets/img/avatars/avatar-5.png +0 -0
  183. customeranalytics/web/app/base/static/assets/img/avatars/info.jpeg +0 -0
  184. customeranalytics/web/app/base/static/assets/img/icons/customanality.png +0 -0
  185. customeranalytics/web/app/base/static/assets/img/icons/icon-48x48.png +0 -0
  186. customeranalytics/web/app/base/static/assets/js/app.js +2 -0
  187. customeranalytics/web/app/base/static/assets/js/app.js.LICENSE +566 -0
  188. customeranalytics/web/app/base/static/assets/js/app.js.LICENSE.txt +556 -0
  189. customeranalytics/web/app/base/static/assets/js/modules/bootstrap.js +15 -0
  190. customeranalytics/web/app/base/static/assets/js/modules/chartjs.js +4 -0
  191. customeranalytics/web/app/base/static/assets/js/modules/feather.js +8 -0
  192. customeranalytics/web/app/base/static/assets/js/modules/flatpickr.js +4 -0
  193. customeranalytics/web/app/base/static/assets/js/modules/moment.js +4 -0
  194. customeranalytics/web/app/base/static/assets/js/modules/sidebar.js +23 -0
  195. customeranalytics/web/app/base/static/assets/js/modules/theme.js +16 -0
  196. customeranalytics/web/app/base/templates/accounts/login.html +81 -0
  197. customeranalytics/web/app/base/templates/accounts/register.html +80 -0
  198. customeranalytics/web/app/base/templates/includes/footer.html +28 -0
  199. customeranalytics/web/app/base/templates/includes/navigation.html +51 -0
  200. customeranalytics/web/app/base/templates/includes/scripts.html +3 -0
  201. customeranalytics/web/app/base/templates/includes/sidebar.html +146 -0
  202. customeranalytics/web/app/base/templates/layouts/base-fullscreen.html +36 -0
  203. customeranalytics/web/app/base/templates/layouts/base.html +84 -0
  204. customeranalytics/web/app/base/util.py +30 -0
  205. customeranalytics/web/app/home/__init__.py +9 -0
  206. customeranalytics/web/app/home/forms.py +1039 -0
  207. customeranalytics/web/app/home/models.py +438 -0
  208. customeranalytics/web/app/home/profiles.py +310 -0
  209. customeranalytics/web/app/home/routes.py +386 -0
  210. customeranalytics/web/app/home/search.py +288 -0
  211. customeranalytics/web/app/home/static/assets/img/avatars/avatar-1.png +0 -0
  212. customeranalytics/web/app/home/static/assets/img/avatars/avatar-2.png +0 -0
  213. customeranalytics/web/app/home/static/assets/img/avatars/avatar-3.png +0 -0
  214. customeranalytics/web/app/home/static/assets/img/avatars/avatar-4.png +0 -0
  215. customeranalytics/web/app/home/static/assets/img/avatars/avatar-5.png +0 -0
  216. customeranalytics/web/app/home/static/assets/img/icons/customanality.png +0 -0
  217. customeranalytics/web/app/home/static/assets/img/icons/icon-48x48.png +0 -0
  218. customeranalytics/web/app/home/templates/abtest-product.html +280 -0
  219. customeranalytics/web/app/home/templates/abtest-promotion.html +362 -0
  220. customeranalytics/web/app/home/templates/abtest-segments.html +298 -0
  221. customeranalytics/web/app/home/templates/add-data-delivery.html +184 -0
  222. customeranalytics/web/app/home/templates/add-data-product.html +177 -0
  223. customeranalytics/web/app/home/templates/add-data-purchase.html +392 -0
  224. customeranalytics/web/app/home/templates/anomaly.html +261 -0
  225. customeranalytics/web/app/home/templates/clv.html +211 -0
  226. customeranalytics/web/app/home/templates/cohorts.html +308 -0
  227. customeranalytics/web/app/home/templates/customer-segmentation.html +308 -0
  228. customeranalytics/web/app/home/templates/data-es.html +152 -0
  229. customeranalytics/web/app/home/templates/data-execute.html +397 -0
  230. customeranalytics/web/app/home/templates/delivery.html +404 -0
  231. customeranalytics/web/app/home/templates/funnel-customer.html +240 -0
  232. customeranalytics/web/app/home/templates/funnel-session.html +236 -0
  233. customeranalytics/web/app/home/templates/index.html +524 -0
  234. customeranalytics/web/app/home/templates/index2.html +515 -0
  235. customeranalytics/web/app/home/templates/page-403.html +46 -0
  236. customeranalytics/web/app/home/templates/page-404.html +45 -0
  237. customeranalytics/web/app/home/templates/page-500.html +45 -0
  238. customeranalytics/web/app/home/templates/pages-blank.html +33 -0
  239. customeranalytics/web/app/home/templates/pages-sign-in.html +67 -0
  240. customeranalytics/web/app/home/templates/pages-sign-up.html +61 -0
  241. customeranalytics/web/app/home/templates/product.html +203 -0
  242. customeranalytics/web/app/home/templates/profile.html +1009 -0
  243. customeranalytics/web/app/home/templates/rfm.html +254 -0
  244. customeranalytics/web/app/home/templates/search.html +167 -0
  245. customeranalytics/web/app/home/templates/settings.html +205 -0
  246. customeranalytics/web/app/home/templates/stats-desc.html +230 -0
  247. customeranalytics/web/app/home/templates/stats-purchase.html +221 -0
  248. customeranalytics/web/config.py +30 -0
  249. customeranalytics/web/db.sqlite3 +0 -0
  250. customeranalytics/web/run.py +30 -0
  251. customeranalytics-0.1.dist-info/METADATA +1102 -0
  252. customeranalytics-0.1.dist-info/RECORD +254 -0
  253. customeranalytics-0.1.dist-info/WHEEL +4 -0
  254. customeranalytics-0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,340 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import datetime
4
+ import random
5
+ from time import gmtime, strftime
6
+ import pytz
7
+ from elasticsearch import Elasticsearch
8
+ from elasticsearch import helpers
9
+ import argparse
10
+ from itertools import product
11
+
12
+ import sys, os, inspect
13
+ currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
14
+ parentdir = os.path.dirname(currentdir)
15
+ sys.path.insert(0, parentdir)
16
+
17
+ from customeranalytics.configs import default_es_port, default_es_host
18
+ from customeranalytics.utils import current_date_to_day, get_index_group, convert_str_to_hour, convert_to_date, convert_dt_to_day_str
19
+ from customeranalytics.utils import dimension_decision, convert_to_iso_format
20
+ from customeranalytics.data_storage_configurations.query_es import QueryES
21
+
22
+
23
+ class ProductAnalytics:
24
+ """
25
+
26
+ """
27
+ def __init__(self,
28
+ has_product_connection=True,
29
+ host=None,
30
+ port=None,
31
+ download_index='downloads',
32
+ order_index='orders'):
33
+ """
34
+ !!!!
35
+ ******* ******** *****
36
+ Dimensional Product Analytic:
37
+ Product Analytic must be created individually for dimensions. For instance, the Data set contains locations dimension.
38
+ In this case, each location of 'orders' and 'downloads' indexes must be created individually.
39
+ by using 'download_index' and 'order_index' dimension can be assigned in order to create a Product Analyse
40
+
41
+ download_index; downloads_location1 this will be the location dimension of
42
+ parameters in order to query downloads indexes; 'location1'.
43
+ download_index; orders_location1 this will be the location dimension of
44
+ parameters in order to query orders indexes; 'location1'.
45
+ ******* ******** *****
46
+ !!!
47
+
48
+ :param has_product_connection: has_product_connection if there is no products data then, no product analytics
49
+ :param host: elasticsearch host
50
+ :param port: elasticsearch port
51
+ :param download_index: elasticsearch port
52
+ :param order_index: elasticsearch port
53
+ """
54
+ self.port = default_es_port if port is None else port
55
+ self.host = default_es_host if host is None else host
56
+ self.has_product_connection = has_product_connection
57
+ self.download_index = download_index
58
+ self.order_index = order_index
59
+ self.query_es = QueryES(port=port, host=host)
60
+ self.fields_products = ["id", "client", "basket", "session_start_date", "payment_amount", "discount_amount"]
61
+ self.products = pd.DataFrame()
62
+ self.hourly_products = pd.DataFrame()
63
+ self.hourly_product_cat = pd.DataFrame()
64
+ self.product_pairs = pd.DataFrame()
65
+ self.daily_products = pd.DataFrame()
66
+ self.product_kpis = pd.DataFrame()
67
+ self.analysis = ["most_ordered_products", "most_ordered_categories",
68
+ "hourly_products_of_sales", "hourly_categories_of_sales",
69
+ "most_combined_products", 'daily_products_of_sales', 'product_kpis']
70
+
71
+ def dimensional_query(self, boolean_query=None):
72
+ if dimension_decision(self.order_index):
73
+ if boolean_query is None:
74
+ boolean_query = [{"term": {"dimension": self.order_index}}]
75
+ else:
76
+ boolean_query += [{"term": {"dimension": self.order_index}}]
77
+ return boolean_query
78
+
79
+ def get_products(self, end_date):
80
+ """
81
+ 1. Fetch data from orders with filter; actions.purchased: True
82
+ 2. Example of basket dictionary on order document;
83
+ 'basket': {'p_10': {'price': 6.12, 'category': 'p_c_8', 'rank': 109},
84
+ 'p_145': {'price': 12.0, 'category': 'p_c_9', 'rank': 175},
85
+ 'p_168': {'price': 13.12, 'category': 'p_c_10', 'rank': 82},
86
+ 'p_9': {'price': 0.52, 'category': 'p_c_3', 'rank': 9},
87
+ 'p_4': {'price': 3.72, 'category': 'p_c_8', 'rank': 69},
88
+ 'p_104': {'price': 8.88, 'category': 'p_c_10', 'rank': 97},
89
+ 'p_74': {'price': 8.395, 'category': 'p_c_10', 'rank': 35}
90
+ }
91
+ a. Keys of dictionary are products.
92
+ :param end_date: last date of data set
93
+ """
94
+ self.query_es = QueryES(port=self.port, host=self.host)
95
+ self.query_es.date_queries_builder({"session_start_date": {"lt": end_date}})
96
+ self.query_es.query_builder(fields=None, _source=True,
97
+ boolean_queries=self.dimensional_query([{"term": {"actions.has_basket": True}}]))
98
+ self.products = self.query_es.get_data_from_es()
99
+ self.products = pd.DataFrame([{col: r['_source'][col] for col in self.fields_products} for r in self.products])
100
+ self.products = self.products.query('basket == basket')
101
+ self.products['products'] = self.products['basket'].apply(
102
+ lambda x: list(x.keys()) if x == x else None) # get product_ids
103
+ self.products = self.products.query('products == products')
104
+ # get prices
105
+ self.products['price'] = self.products.apply(
106
+ lambda row: [row['basket'][i]['price'] for i in row['products']], axis=1)
107
+ self.products['category'] = self.products.apply(
108
+ lambda row: [row['basket'][i]['category'] for i in row['products']], axis=1)
109
+ self.products['products'] = self.products.apply(
110
+ lambda row: list(zip([row['id']] * len(row['products']),
111
+ [row['client']] * len(row['products']),
112
+ [row['payment_amount']] * len(row['products']),
113
+ [row['discount_amount']] * len(row['products']),
114
+ [row['session_start_date']] * len(row['products']),
115
+ row['products'],
116
+ row['category'],
117
+ row['price'])), axis=1)
118
+ # concatenate products columns and convert it to dataframe with columns produc_id and price
119
+ self.products = pd.DataFrame(np.concatenate(list(self.products['products']))).rename(
120
+ columns={0: "id", 1: "client", 2: "payment_amount", 3: "discount_amount",
121
+ 4: 'session_start_date', 5: "products", 6: "category", 7: "price"})
122
+ # subtract price from payment amount.
123
+ # This will works to see how product of addition affects the total basket of payment amount
124
+ self.products['payment_amount'] = self.products['payment_amount'].apply(lambda x: float(x))
125
+ self.products['price'] = self.products['price'].apply(lambda x: float(x))
126
+ self.products['session_start_date'] = self.products['session_start_date'].apply(lambda x: convert_to_date(x))
127
+
128
+ def get_customer_segments(self, date):
129
+ """
130
+ Collecting segments of customers from the reports index.
131
+ :param date: given report date
132
+ """
133
+ date = current_date_to_day().isoformat() if date is None else date
134
+ self.products = pd.merge(self.products,
135
+ self.cs.fetch(start_date=convert_dt_to_day_str(date))[['client', 'segments']],
136
+ on='client', how='left')
137
+
138
+ def get_most_ordered_products(self):
139
+ """
140
+ the products of orders are counted and sorted according to the order counts.
141
+ So, the most ordered products are detected.
142
+ """
143
+ return self.products.groupby("products").agg(
144
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(
145
+ columns={"id": "order_count"}).sort_values(by='order_count', ascending=False)
146
+
147
+ def get_most_ordered_categories(self):
148
+ """
149
+ the product categories of orders are counted and sorted according to the order counts.
150
+ So, the most ordered products are detected.
151
+ """
152
+ return self.products.groupby("category").agg({"id": lambda x: len(np.unique(x))}).reset_index().rename(
153
+ columns={"id": "order_count"}).sort_values(by='order_count', ascending=False)
154
+
155
+ def get_hourly_products_of_sales(self):
156
+ """
157
+ Which product is preferred to purchase via purchased hour?
158
+ """
159
+ self.products['hours'] = self.products['session_start_date'].apply(lambda x: convert_str_to_hour(x))
160
+ self.hourly_products = self.products.groupby(["hours", "products"]).agg(
161
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "order_count"})
162
+ self.hourly_products['product_order_count_rank'] = self.hourly_products.sort_values(
163
+ by=["hours", "order_count"], ascending=False).groupby("hours").cumcount() + 1
164
+ self.hourly_products = pd.merge(self.hourly_products,
165
+ self.products.groupby("hours").agg(
166
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(
167
+ columns={"id": "hourly_total_orders"}), on='hours', how='left')
168
+ self.hourly_products['hourly_order_ratio'] = self.hourly_products['order_count'] / self.hourly_products[
169
+ 'hourly_total_orders']
170
+ return self.hourly_products
171
+
172
+ def get_hourly_categories_of_sales(self):
173
+ """
174
+ Which category is preferred to purchase via purchased hour?
175
+ """
176
+ self.hourly_product_cat = self.products.groupby(["hours", "category"]).agg(
177
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "order_count"})
178
+ self.hourly_product_cat['product_cat_order_count_rank'] = self.hourly_product_cat.sort_values(
179
+ by=["hours", "order_count"], ascending=False).groupby("hours").cumcount() + 1
180
+ self.hourly_product_cat = pd.merge(self.hourly_product_cat,
181
+ self.products.groupby("hours").agg(
182
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(
183
+ columns={"id": "hourly_total_orders"}),
184
+ on='hours', how='left')
185
+ self.hourly_product_cat['hourly_order_ratio'] = self.hourly_product_cat['order_count'] / \
186
+ self.hourly_product_cat['hourly_total_orders']
187
+ return self.hourly_product_cat
188
+
189
+ def get_most_combined_products(self):
190
+ """
191
+ The number of product in the basket shows us the the combination of products that is chosen by client.
192
+ In this analysis, pair of products are counted according to
193
+ purchased order or products that are selected by user.
194
+ Ex: basket 1; prod_1, prod_2 || basket 2; prod_1, prod_2, prod_3 || basket 3; prod_1, prod_2, prod_3
195
+
196
+ pairs || number_of_pairs
197
+ -----------------------------------
198
+ prod_1 - prod_2 || 3
199
+ prod_2 - prod_3 || 2
200
+ prod_3 - prod_1 || 2
201
+
202
+ """
203
+ self.product_pairs = self.products.groupby("id").agg({"products": lambda x: list(x)}).reset_index()
204
+ self.product_pairs['product_pairs'] = self.product_pairs['products'].apply(
205
+ lambda x: np.array(list(product(x, x))).tolist())
206
+ self.product_pairs = pd.DataFrame(np.concatenate(list(self.product_pairs['product_pairs'])).tolist())
207
+ self.product_pairs['total_pairs'] = 1
208
+ self.product_pairs = self.product_pairs[self.product_pairs[0] != self.product_pairs[1]]
209
+ self.product_pairs = self.product_pairs.groupby([0, 1]).agg({"total_pairs": "sum"}).reset_index()
210
+ self.product_pairs = self.product_pairs.sort_values('total_pairs', ascending=False)
211
+ self.product_pairs = self.product_pairs.rename(columns={0: "pair_1", 1: "pair_2"})
212
+ self.product_pairs['product_pair'] = self.product_pairs.apply(
213
+ lambda row: " - ".join(list(sorted([row['pair_1'], row['pair_2']]))), axis=1)
214
+ self.product_pairs = self.product_pairs.groupby("product_pair").agg({"total_pairs": "first"}).reset_index()
215
+ return self.product_pairs
216
+
217
+ def get_daily_product_sales(self):
218
+ """
219
+ Which product is preferred to purchase via purchased day?
220
+ """
221
+ self.products['daily'] = self.products['session_start_date'].apply(lambda x: convert_dt_to_day_str(x))
222
+ self.products = self.products.query("payment_amount == payment_amount")
223
+ self.products['payment_amount'] = self.products['payment_amount'].apply(lambda x: float(x))
224
+ self.daily_products = self.products.reset_index().groupby(["daily", "products"]).agg(
225
+ {"payment_amount": "sum", 'index': 'count'}).reset_index().rename(columns={"index": "order_count"})
226
+ return self.daily_products
227
+
228
+ def get_product_kpis(self):
229
+ """
230
+ These KPIs is for the searching bar, when searching text is detected as product. There are 4 KPIS
231
+ which are shown at the the left top of the dashboard.
232
+ """
233
+ kpi_1 = self.products[['products', 'client']].reset_index().groupby(["client", "products"]).agg(
234
+ {"index": "count"}).reset_index().groupby("products").agg(
235
+ {"index": "mean"}).reset_index().rename(columns={"index": "average_product_sold_per_user_kpi"})
236
+ kpi_2 = self.products[['products', 'payment_amount']].groupby("products").agg(
237
+ {"payment_amount": "sum"}).reset_index().rename(columns={"payment_amount": "total_product_revenue_kpi"})
238
+ try:
239
+ kpi_3 = self.products[['products', 'discount_amount']].query("discount_amount == discount_amount")
240
+ kpi_3['discount_amount'] = kpi_3['discount_amount'].apply(lambda x: float(x))
241
+ kpi_3 = kpi_3.groupby("products").agg({"discount_amount": "sum"}).reset_index().rename(
242
+ columns={"discount_amount": "total_product_discount_kpi"})
243
+ except:
244
+ kpi_3 = self.products[['products']]
245
+ kpi_3['total_product_discount_kpi'] = 0
246
+
247
+ kpi_4 = self.products[['products', 'client']].groupby("products").agg(
248
+ {"client": lambda x: len(np.unique(x))}).reset_index().rename(columns={"client": "total_product_cust_kpi"})
249
+
250
+ self.product_kpis = pd.DataFrame(self.products.groupby('products').count().reset_index()[['products']]).merge(
251
+ kpi_1, on='products', how='left').merge(
252
+ kpi_2, on='products', how='left').merge(
253
+ kpi_3, on='products', how='left').merge(kpi_4, on='products', how='left').fillna(0)
254
+
255
+ return self.product_kpis
256
+
257
+ def execute_product_analysis(self, end_date=None):
258
+ """
259
+ 1. Check for products data available on elasticsearch orders index.
260
+ 2. Get products data and convert basket data into the data-frame format.
261
+ 3. Execute each analysis separately.
262
+ 4. Insert into 'reports' index.
263
+ """
264
+ if self.has_product_connection:
265
+ self.get_products(end_date=convert_to_iso_format(current_date_to_day() if end_date is None else end_date))
266
+ for ins in list(zip(self.analysis, [self.get_most_ordered_products, self.get_most_ordered_categories,
267
+ self.get_hourly_products_of_sales, self.get_hourly_categories_of_sales,
268
+ self.get_most_combined_products, self.get_daily_product_sales,
269
+ self.get_product_kpis])):
270
+ _result = ins[1]()
271
+ self.insert_into_reports_index(product_analytic=_result,
272
+ pa_type=ins[0], start_date=end_date, index=self.order_index)
273
+ del _result
274
+
275
+ def insert_into_reports_index(self, product_analytic, pa_type, start_date=None, index='orders'):
276
+ """
277
+ via query_es.py, each report can be inserted into the reports index with the given format.
278
+ {"id": unique report id,
279
+ "report_date": start_date or current date,
280
+ "report_name": "product_analytic",
281
+ "index": "main",
282
+ "report_types": {"type": "most_ordered_products", "most_ordered_categories", "hourly_products_of_sales",
283
+ "hourly_categories_of_sales", "hourly_categories_of_sales", "most_combined_products"
284
+ },
285
+ "data": product_analytic.to_dict("records") - dataframe to list of dictionary
286
+ }
287
+ :param product_analytic: data set, data frame
288
+ :param start_date: data start date
289
+ :param pa_type: product analytic type
290
+ :param index: dimentionality of data index orders_location1 ; dimension = location1
291
+ """
292
+ list_of_obj = [{"id": np.random.randint(200000000),
293
+ "report_date": current_date_to_day().isoformat() if start_date is None else start_date,
294
+ "report_name": "product_analytic",
295
+ "index": get_index_group(index),
296
+ "report_types": {"type": pa_type},
297
+ "data": product_analytic.fillna(0).to_dict("records")}]
298
+ self.query_es.insert_data_to_index(list_of_obj, index='reports')
299
+
300
+ def fetch(self, product_analytic_name, start_date=None):
301
+ """
302
+ This allows us to query the created Product Analytics.
303
+ product_analytic_name is crucial for us to collect the correct filters.
304
+ Example of queries;
305
+ - product_analytic_name: most_ordered_products,
306
+ - start_date: 2021-01-01T00:00:00
307
+
308
+ {'size': 10000000,
309
+ 'from': 0,
310
+ '_source': True,
311
+ 'query': {'bool': {'must': [
312
+ {'term': {'report_name': 'p'}},
313
+ {"term": {"index": "orders_location1"}}
314
+ {'term': {'report_types.type': 'most_ordered_products'}},
315
+ {'range': {'report_date': {'lt': '2021-04-01T00:00:00'}}}]}}}
316
+
317
+ - start date will be filtered from data frame. In this example; .query("daily > @start_date")
318
+
319
+ :param product_analytic_name: most_ordered_products.
320
+ :param start_date: product analytic report created date
321
+ :param index: index_name in order to get dimension_of data. If there is no dimension, no need to be assigned
322
+ :return: data frame
323
+ """
324
+ boolean_queries, date_queries = [], []
325
+ boolean_queries = [{"term": {"report_types.type": product_analytic_name}},
326
+ {"term": {"index": get_index_group(self.order_index)}}]
327
+
328
+ if start_date is not None:
329
+ date_queries = [{"range": {"report_date": {"lt": convert_to_iso_format(start_date)}}}]
330
+
331
+ self.query_es = QueryES(port=self.port,
332
+ host=self.host)
333
+ self.query_es.query_builder(fields=None, _source=True,
334
+ date_queries=date_queries,
335
+ boolean_queries=boolean_queries)
336
+ _res = self.query_es.get_data_from_es(index="reports")
337
+ _data = pd.DataFrame()
338
+ if len(_res) != 0:
339
+ _data = pd.DataFrame(_res[0]['_source']['data'])
340
+ return _data
@@ -0,0 +1,299 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import datetime
4
+ import random
5
+ from time import gmtime, strftime
6
+ import pytz
7
+ from elasticsearch import Elasticsearch
8
+ from elasticsearch import helpers
9
+ import argparse
10
+ from itertools import product
11
+
12
+ import sys, os, inspect
13
+ currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
14
+ parentdir = os.path.dirname(currentdir)
15
+ sys.path.insert(0, parentdir)
16
+
17
+ from customeranalytics.configs import default_es_port, default_es_host, none_types
18
+ from customeranalytics.utils import current_date_to_day, get_index_group, convert_str_to_hour, convert_to_date, convert_dt_to_day_str
19
+ from customeranalytics.utils import dimension_decision, convert_to_iso_format
20
+ from customeranalytics.data_storage_configurations.query_es import QueryES
21
+
22
+
23
+ class PromotionAnalytics:
24
+ """
25
+
26
+ """
27
+ def __init__(self,
28
+ has_promotion_connection=True,
29
+ host=None,
30
+ port=None,
31
+ download_index='downloads',
32
+ order_index='orders'):
33
+ """
34
+ !!!!
35
+ ******* ******** *****
36
+ Dimensional Promotion Analytic:
37
+ Promotion must be created individually for dimensions. For instance, the Data set contains locations dimension.
38
+ In this case, each location of 'orders' and 'downloads' indexes must be created individually.
39
+ by using 'download_index' and 'order_index' dimension can be assigned in order to create a Promotion Analyse
40
+
41
+ download_index; downloads_location1 this will be the location dimension of
42
+ parameters in order to query downloads indexes; 'location1'.
43
+ download_index; orders_location1 this will be the location dimension of
44
+ parameters in order to query orders indexes; 'location1'.
45
+ ******* ******** *****
46
+ !!!
47
+
48
+ :param has_promotion_connection: has_promotion_connection if there is no products data then, no promotion analytics
49
+ :param host: elasticsearch host
50
+ :param port: elasticsearch port
51
+ :param download_index: elasticsearch port
52
+ :param order_index: elasticsearch port
53
+ """
54
+ self.port = default_es_port if port is None else port
55
+ self.host = default_es_host if host is None else host
56
+ self.has_promotion_connection = has_promotion_connection
57
+ self.download_index = download_index
58
+ self.order_index = order_index
59
+ self.query_es = QueryES(port=port, host=host)
60
+ self.fields_promotions = ["id", "client", "promotion_id", "session_start_date",
61
+ "payment_amount", "discount_amount"]
62
+ self.promotions = pd.DataFrame()
63
+ self.time_periods = ["hourly", "daily"]
64
+ self.inorganic_orders = pd.DataFrame()
65
+ self.inorganic_orders_hour = pd.DataFrame()
66
+ self.daily_promotion_revenue = pd.DataFrame()
67
+ self.daily_promotion_discount = pd.DataFrame()
68
+ self.promotion_number_of_customer = pd.DataFrame()
69
+ self.analysis = ["inorganic_orders_per_promotion_per_day", "daily_organic_orders", "hourly_organic_orders",
70
+ "daily_inorganic_ratio", "hourly_inorganic_ratio",
71
+ "daily_promotion_revenue", "daily_promotion_discount", "avg_order_count_per_promo_per_cust",
72
+ "promotion_number_of_customer", "promotion_kpis"]
73
+
74
+ def dimensional_query(self, boolean_query=None):
75
+ if dimension_decision(self.order_index):
76
+ if boolean_query is None:
77
+ boolean_query = [{"term": {"dimension": self.order_index}}]
78
+ else:
79
+ boolean_query += [{"term": {"dimension": self.order_index}}]
80
+ return boolean_query
81
+
82
+ def get_time_period(self):
83
+ """
84
+ converting date column of values into the time_periods (hourly weekly, monthly,..)
85
+ orders; total data (orders/downloads data with actions)
86
+ final data; data set with time periods
87
+ """
88
+ for p in list(zip(self.time_periods,
89
+ [convert_str_to_hour, convert_dt_to_day_str])):
90
+ self.promotions[p[0]] = self.promotions["session_start_date"].apply(lambda x: p[1](x))
91
+
92
+ def get_promotions(self, end_date):
93
+ """
94
+ 1. Fetch data from orders with filter; actions.purchased: True with promotion Id
95
+ :param end_date: last date of data set
96
+ """
97
+ self.query_es = QueryES(port=self.port, host=self.host)
98
+ self.query_es.date_queries_builder({"session_start_date": {"lt": end_date}})
99
+ self.query_es.query_builder(fields=None, _source=True,
100
+ boolean_queries=self.dimensional_query([{"term": {"actions.purchased": True}}]))
101
+ self.promotions = self.query_es.get_data_from_es()
102
+ self.promotions = pd.DataFrame(
103
+ [{col: r['_source'][col] for col in self.fields_promotions} for r in self.promotions])
104
+
105
+ self.promotions['payment_amount'] = self.promotions['payment_amount'].apply(lambda x: float(x))
106
+ self.promotions['session_start_date'] = self.promotions['session_start_date'].apply(lambda x: convert_to_date(x))
107
+ print(self.promotions.query("promotion_id != promotion_id"))
108
+ self.promotions['has_promotion'] = self.promotions['promotion_id'].apply(lambda x: True if x not in none_types else False)
109
+ self.get_time_period()
110
+ print(self.promotions.head())
111
+
112
+ def get_inorganic_orders_per_promotion_per_day(self):
113
+ """
114
+ Orders which have promotion columns with ID not null values.
115
+ This process indicates total promoted order count per day per promotion_id.
116
+ """
117
+ self.inorganic_orders = self.promotions.query("has_promotion == True").groupby(["daily", "promotion_id"]).agg(
118
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "order_count"})
119
+ return self.inorganic_orders
120
+
121
+ def get_inorganic_orders_per_promotion_per_hour(self):
122
+ """
123
+ Orders which have promotion columns with ID not null values.
124
+ This process indicates total promoted order count per hour per promotion_id.
125
+ """
126
+ self.inorganic_orders_hour = self.promotions.query("has_promotion == True").groupby(["hourly", "promotion_id"]).agg(
127
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "order_count"})
128
+ return self.inorganic_orders_hour
129
+
130
+ def get_organic_orders_per_day(self):
131
+ """
132
+ Orders which have promotion columns with null values.
133
+ This process indicates total **NOT** promoted order count per hour per promotion_id.
134
+ """
135
+ return self.promotions.query("has_promotion != True").groupby("daily").agg(
136
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "order_count"})
137
+
138
+ def get_daily_inorganic_ratio(self):
139
+ """
140
+ daily organic orders / daily total order count
141
+ """
142
+ daily_inorganic_ratio = self.promotions.groupby(["daily", "promotion_id"]).agg(
143
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "total_order_count"})
144
+ daily_inorganic_ratio = pd.merge(daily_inorganic_ratio, self.inorganic_orders, how='left', on=["daily", "promotion_id"])
145
+ daily_inorganic_ratio['inorganic_ratio'] = daily_inorganic_ratio['order_count'] / daily_inorganic_ratio['total_order_count']
146
+ return daily_inorganic_ratio
147
+
148
+ def get_hourly_inorganic_ratio(self):
149
+ """
150
+ hourly organic orders / hourly total order count
151
+ """
152
+ hourly_inorganic_ratio = self.promotions.groupby(["hourly", "promotion_id"]).agg(
153
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "total_order_count"})
154
+ hourly_inorganic_ratio = pd.merge(hourly_inorganic_ratio, self.inorganic_orders_hour, how='left', on=["hourly", "promotion_id"])
155
+ hourly_inorganic_ratio['inorganic_ratio'] = hourly_inorganic_ratio['order_count'] / hourly_inorganic_ratio['total_order_count']
156
+ return hourly_inorganic_ratio
157
+
158
+ def get_daily_promotion_revenue(self):
159
+ """
160
+ Orders which have promotion columns with null values.
161
+ This process indicates total **NOT** promoted payment amount per hour per promotion_id.
162
+ """
163
+ self.daily_promotion_revenue = self.promotions.query("has_promotion == True").groupby(
164
+ ["daily", "promotion_id"]).agg({"payment_amount": 'sum'}).reset_index().rename(
165
+ columns={"payment_amount": "total_revenue"})
166
+ return self.daily_promotion_revenue
167
+
168
+ def get_daily_promotion_discount(self):
169
+ """
170
+ Orders which have promotion columns with null values.
171
+ This process indicates total **NOT** promoted discount amount per hour per promotion_id.
172
+ """
173
+
174
+ if len(self.promotions.query("discount_amount == discount_amount")) != 0:
175
+ self.daily_promotion_discount = self.promotions.query(
176
+ "has_promotion == True and discount_amount == discount_amount").groupby(["daily", "promotion_id"]).agg(
177
+ {"discount_amount": 'sum'}).reset_index().rename(columns={"discount_amount": "total_discount"})
178
+ return self.daily_promotion_discount
179
+
180
+ def get_promotion_average_order_count_per_customer(self):
181
+ """
182
+ average order count per promotion per customer
183
+ """
184
+ average_order_count_per_cust = self.promotions.groupby(["client", "promotion_id"]).agg(
185
+ {"id": lambda x: len(np.unique(x))}).reset_index().rename(columns={"id": "average_order_count"})
186
+ return average_order_count_per_cust.groupby("promotion_id").agg({"average_order_count": "mean"}).reset_index()
187
+
188
+ def get_promotion_number_of_customer(self):
189
+ """
190
+ average order count per promotion per customer
191
+ """
192
+ self.promotion_number_of_customer = self.promotions.groupby("promotion_id").agg(
193
+ {"client": lambda x: len(np.unique(x))}).reset_index().rename(columns={"client": "client_count"})
194
+ return self.promotion_number_of_customer
195
+
196
+ def get_promotion_kpis(self):
197
+ """
198
+ average daily order count per promotion
199
+ average daily payment amount per promotion
200
+ average daily discount amount per promotion
201
+ number of user per promotion
202
+ """
203
+ kpi_1 = self.inorganic_orders.groupby("promotion_id").agg({"order_count": "mean"}).reset_index()
204
+ kpi_2 = self.daily_promotion_revenue.groupby("promotion_id").agg({"total_revenue": "mean"}).reset_index()
205
+ kpi_3 = self.daily_promotion_discount.groupby("promotion_id").agg({"total_discount": "mean"}).reset_index()
206
+ kpi_4 = self.promotion_number_of_customer
207
+
208
+ promotions = pd.DataFrame(list(self.promotions['promotion_id'].unique())).rename(columns={0: "promotion_id"})
209
+ return promotions.merge(kpi_1, on='promotion_id').merge(
210
+ kpi_2, on='promotion_id').merge(kpi_3, on='promotion_id').merge(kpi_4, on='promotion_id').fillna(0)
211
+
212
+ def execute_promotion_analysis(self, end_date=None):
213
+ """
214
+ 1. Check for promotions data available on elasticsearch orders index.
215
+ 2. Execute each analysis separately.
216
+ 3. Insert into 'reports' index.
217
+ """
218
+ if self.has_promotion_connection:
219
+ self.get_promotions(end_date=convert_to_iso_format(current_date_to_day() if end_date is None else end_date))
220
+ for ins in list(zip(self.analysis, [self.get_inorganic_orders_per_promotion_per_day,
221
+ self.get_organic_orders_per_day,
222
+ self.get_inorganic_orders_per_promotion_per_hour,
223
+ self.get_daily_inorganic_ratio, self.get_hourly_inorganic_ratio,
224
+ self.get_daily_promotion_revenue, self.get_daily_promotion_discount,
225
+ self.get_promotion_average_order_count_per_customer,
226
+ self.get_promotion_number_of_customer,
227
+ self.get_promotion_kpis])):
228
+ _result = ins[1]()
229
+ self.insert_into_reports_index(promotion_analytics=_result,
230
+ pa_type=ins[0], start_date=end_date, index=self.order_index)
231
+ del _result
232
+
233
+ def insert_into_reports_index(self, promotion_analytics, pa_type, start_date=None, index='orders'):
234
+ """
235
+ via query_es.py, each report can be inserted into the reports index with the given format.
236
+ {"id": unique report id,
237
+ "report_date": start_date or current date,
238
+ "report_name": "promotion_analytic",
239
+ "index": "main",
240
+ "report_types": {"inorganic_orders_per_promotion_per_day", "daily_organic_orders",
241
+ "daily_inorganic_ratio", "hourly_inorganic_ratio",
242
+ "daily_promotion_revenue", "daily_promotion_discount", "avg_order_count_per_promo_per_cust"
243
+ },
244
+ "data": promotion_analytic.to_dict("records") - dataframe to list of dictionary
245
+ }
246
+ :param promotion_analytic: data set, data frame
247
+ :param start_date: data start date
248
+ :param pa_type: promotion analytic type
249
+ :param index: dimentionality of data index orders_location1 ; dimension = location1
250
+ """
251
+ list_of_obj = [{"id": np.random.randint(200000000),
252
+ "report_date": current_date_to_day().isoformat() if start_date is None else start_date,
253
+ "report_name": "promotion_analytic",
254
+ "index": get_index_group(index),
255
+ "report_types": {"type": pa_type},
256
+ "data": promotion_analytics.fillna(0).to_dict("records")}]
257
+ self.query_es.insert_data_to_index(list_of_obj, index='reports')
258
+
259
+ def fetch(self, promotion_analytic_name, start_date=None):
260
+ """
261
+ This allows us to query the created Promotion Analytics.
262
+ promotion_analytic_name is crucial for us to collect the correct filters.
263
+ Example of queries;
264
+ - promotion_analytic_name: inorganic_orders_per_promotion_per_day,
265
+ - start_date: 2021-01-01T00:00:00
266
+
267
+ {'size': 10000000,
268
+ 'from': 0,
269
+ '_source': True,
270
+ 'query': {'bool': {'must': [
271
+ {'term': {'report_name': 'p'}},
272
+ {"term": {"index": "orders_location1"}}
273
+ {'term': {'report_types.type': 'most_ordered_products'}},
274
+ {'range': {'report_date': {'lt': '2021-04-01T00:00:00'}}}]}}}
275
+
276
+ - start date will be filtered from data frame. In this example; .query("daily > @start_date")
277
+
278
+ :param promotion_analytic_name: most_ordered_products.
279
+ :param start_date: product analytic report created date
280
+ :param index: index_name in order to get dimension_of data. If there is no dimension, no need to be assigned
281
+ :return: data frame
282
+ """
283
+ boolean_queries, date_queries = [], []
284
+ boolean_queries = [{"term": {"report_types.type": promotion_analytic_name}},
285
+ {"term": {"index": get_index_group(self.order_index)}}]
286
+
287
+ if start_date is not None:
288
+ date_queries = [{"range": {"report_date": {"lt": convert_to_iso_format(start_date)}}}]
289
+
290
+ self.query_es = QueryES(port=self.port,
291
+ host=self.host)
292
+ self.query_es.query_builder(fields=None, _source=True,
293
+ date_queries=date_queries,
294
+ boolean_queries=boolean_queries)
295
+ _res = self.query_es.get_data_from_es(index="reports")
296
+ _data = pd.DataFrame()
297
+ if len(_res) != 0:
298
+ _data = pd.DataFrame(_res[0]['_source']['data'])
299
+ return _data