bertrend 0.3.22__tar.gz → 0.3.23__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. {bertrend-0.3.22 → bertrend-0.3.23}/PKG-INFO +2 -2
  2. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/openai_client.py +1 -6
  3. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/start.py +6 -3
  4. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/docker-compose.yml +0 -1
  5. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/job_utils/job_functions.py +2 -5
  6. bertrend-0.3.23/bertrend/tests/apps/test_lazy_scheduler.py +71 -0
  7. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_models.py +5 -24
  8. bertrend-0.3.23/bertrend/tests/apps/test_multiworker.py +57 -0
  9. bertrend-0.3.23/bertrend/tests/services/scheduling/test_job_exception_fix.py +74 -0
  10. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_apscheduler_utils.py +16 -39
  11. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_crontab_utils.py +1 -3
  12. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/data_loading.py +7 -1
  13. bertrend-0.3.23/bertrend_apps/__init__.py +43 -0
  14. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/apscheduler_utils.py +97 -29
  15. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/crontab_utils.py +16 -4
  16. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/scheduler_utils.py +16 -2
  17. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/newscatcher_provider.py +8 -4
  18. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/__main__.py +1 -2
  19. bertrend-0.3.23/bertrend_apps/prospective_demo/.streamlit/secrets.toml.example +9 -0
  20. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/automated_report_generation.py +25 -14
  21. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_analysis.py +14 -8
  22. bertrend-0.3.23/bertrend_apps/prospective_demo/docs/architecture.md +82 -0
  23. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/feeds_config.py +14 -5
  24. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/i18n_translations.py +8 -0
  25. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/models_info.py +7 -0
  26. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/process_new_data.py +8 -0
  27. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/report_generation.py +11 -4
  28. bertrend-0.3.23/bertrend_apps/services/config/default_config.toml +3 -0
  29. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/config/settings.py +0 -1
  30. bertrend-0.3.23/bertrend_apps/services/models/newsletters_models.py +16 -0
  31. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/bertrend_app.py +2 -2
  32. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/data_provider.py +1 -2
  33. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/newsletters.py +20 -16
  34. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/start.py +2 -6
  35. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/utils/logging_utils.py +4 -0
  36. {bertrend-0.3.22 → bertrend-0.3.23}/pyproject.toml +2 -2
  37. bertrend-0.3.22/bertrend_apps/__init__.py +0 -36
  38. bertrend-0.3.22/bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +0 -38
  39. bertrend-0.3.22/bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +0 -38
  40. bertrend-0.3.22/bertrend_apps/prospective_demo/.streamlit/secrets.toml +0 -14
  41. bertrend-0.3.22/bertrend_apps/services/config/default_config.toml +0 -4
  42. bertrend-0.3.22/bertrend_apps/services/models/newsletters_models.py +0 -34
  43. {bertrend-0.3.22 → bertrend-0.3.23}/AUTHORS.txt +0 -0
  44. {bertrend-0.3.22 → bertrend-0.3.23}/LICENSE.md +0 -0
  45. {bertrend-0.3.22 → bertrend-0.3.23}/README.md +0 -0
  46. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/BERTopicModel.py +0 -0
  47. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/BERTrend.py +0 -0
  48. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/__init__.py +0 -0
  49. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/__init__.py +0 -0
  50. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/article_scoring.py +0 -0
  51. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/prompts.py +0 -0
  52. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/scoring_agent.py +0 -0
  53. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/bertopic_default_config.toml +0 -0
  54. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/bertrend_default_config.toml +0 -0
  55. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/parameters.py +0 -0
  56. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/services_default_config.toml +0 -0
  57. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/__init__.py +0 -0
  58. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/__init__.py +0 -0
  59. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/data_loading_component.py +0 -0
  60. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/embed_documents_component.py +0 -0
  61. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/i18n.py +0 -0
  62. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/i18n_translations.py +0 -0
  63. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/icons.py +0 -0
  64. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/parameters_component.py +0 -0
  65. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/state_utils.py +0 -0
  66. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/start_summarizer_app.sh +0 -0
  67. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/start_topic_analysis_app.sh +0 -0
  68. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/start_weak_signals_app.sh +0 -0
  69. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/streamlit_components/__init__.py +0 -0
  70. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/streamlit_components/clickable_df_component.py +0 -0
  71. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/streamlit_components/input_with_pills_component.py +0 -0
  72. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/summarization/__init__.py +0 -0
  73. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/summarization/summarizer_app.py +0 -0
  74. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/.streamlit/config.toml +0 -0
  75. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/__init__.py +0 -0
  76. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/app.py +0 -0
  77. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/app_utils.py +0 -0
  78. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/__init__.py +0 -0
  79. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/explore_topics.py +0 -0
  80. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +0 -0
  81. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +0 -0
  82. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +0 -0
  83. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/training_page.py +0 -0
  84. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/i18n_translations.py +0 -0
  85. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/.streamlit/config.toml +0 -0
  86. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/__init__.py +0 -0
  87. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/app.py +0 -0
  88. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/i18n_translations.py +0 -0
  89. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/visualizations_utils.py +0 -0
  90. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/__init__.py +0 -0
  91. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/agent_utils.py +0 -0
  92. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter.css +0 -0
  93. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_features.py +0 -0
  94. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_model.py +0 -0
  95. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_outlook_template.html +0 -0
  96. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_template.html +0 -0
  97. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/prompts.py +0 -0
  98. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/metrics/__init__.py +0 -0
  99. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/metrics/temporal_metrics_embedding.py +0 -0
  100. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/metrics/topic_metrics.py +0 -0
  101. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/common_ngrams.json +0 -0
  102. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/stopwords-en.json +0 -0
  103. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/stopwords-fr.json +0 -0
  104. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/stopwords-rte.json +0 -0
  105. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/__init__.py +0 -0
  106. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/authentication.py +0 -0
  107. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/cert.pem +0 -0
  108. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_client.py +0 -0
  109. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/README.md +0 -0
  110. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/__init__.py +0 -0
  111. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/bertrend_client_registry.json +0 -0
  112. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/config/default_config.toml +0 -0
  113. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/config/settings.py +0 -0
  114. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/main.py +0 -0
  115. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/models.py +0 -0
  116. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/requirements.txt +0 -0
  117. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/__init__.py +0 -0
  118. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/authentication.py +0 -0
  119. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/embeddings.py +0 -0
  120. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/info.py +0 -0
  121. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/security.py +0 -0
  122. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/status.py +0 -0
  123. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/stop.py +0 -0
  124. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_service.py +0 -0
  125. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/key.pem +0 -0
  126. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/.dockerignore +0 -0
  127. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/Docker_setup.md +0 -0
  128. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/Dockerfile +0 -0
  129. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/__init__.py +0 -0
  130. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/dashboard.html +0 -0
  131. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/job_utils/__init__.py +0 -0
  132. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/models/__init__.py +0 -0
  133. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/models/scheduling_models.py +0 -0
  134. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/requirements.txt +0 -0
  135. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/routers/info.py +0 -0
  136. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/routers/scheduling.py +0 -0
  137. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/scheduling_service.py +0 -0
  138. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summarizer.py +0 -0
  139. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/__init__.py +0 -0
  140. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/abstractive_summarizer.py +0 -0
  141. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/chatgpt_summarizer.py +0 -0
  142. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/extractive_summarizer.py +0 -0
  143. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/lexrank.py +0 -0
  144. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/prompts.py +0 -0
  145. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/README_integration_tests.md +0 -0
  146. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/__init__.py +0 -0
  147. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/__init__.py +0 -0
  148. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_bertrend_service.py +0 -0
  149. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_process_new_data.py +0 -0
  150. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_routers_bertrend_app.py +0 -0
  151. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_routers_info.py +0 -0
  152. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/conftest.py +0 -0
  153. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/__init__.py +0 -0
  154. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_bertrend.py +0 -0
  155. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_data_structure.py +0 -0
  156. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_representative_docs.py +0 -0
  157. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_temporal_metrics_embedding.py +0 -0
  158. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_topic_description.py +0 -0
  159. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_topic_metrics.py +0 -0
  160. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_topic_model.py +0 -0
  161. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_data_structure.py +0 -0
  162. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_prompts.py +0 -0
  163. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_visualizations.py +0 -0
  164. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_weak_signals.py +0 -0
  165. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_visualizations.py +0 -0
  166. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/__init__.py +0 -0
  167. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_agent_utils.py +0 -0
  168. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_article_scoring.py +0 -0
  169. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_article_scoring_models.py +0 -0
  170. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_newsletter_features.py +0 -0
  171. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_newsletter_model.py +0 -0
  172. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_openai_client.py +0 -0
  173. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/__init__.py +0 -0
  174. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/embedding/__init__.py +0 -0
  175. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/embedding/test_embedding_client.py +0 -0
  176. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/scheduling/__init__.py +0 -0
  177. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/scheduling/test_scheduling_service.py +0 -0
  178. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/__init__.py +0 -0
  179. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_abstractive_summarizer.py +0 -0
  180. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_chatgpt_summarizer.py +0 -0
  181. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_extractive_summarizer.py +0 -0
  182. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_summarizer.py +0 -0
  183. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/test_installation.py +0 -0
  184. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/test_integration.py +0 -0
  185. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/__init__.py +0 -0
  186. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_cache_utils.py +0 -0
  187. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_config_utils.py +0 -0
  188. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_data_loading.py +0 -0
  189. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_date_utils.py +0 -0
  190. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_mail_utils.py +0 -0
  191. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_scheduler_utils.py +0 -0
  192. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_utils_init.py +0 -0
  193. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/__init__.py +0 -0
  194. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/data_structure.py +0 -0
  195. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/prompts.py +0 -0
  196. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/representative_docs.py +0 -0
  197. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/topic_description.py +0 -0
  198. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/visualizations.py +0 -0
  199. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/__init__.py +0 -0
  200. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/data_structure.py +0 -0
  201. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/prompts.py +0 -0
  202. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/signal_llm_template_en.html +0 -0
  203. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/signal_llm_template_fr.html +0 -0
  204. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/visualizations.py +0 -0
  205. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/weak_signals.py +0 -0
  206. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/__init__.py +0 -0
  207. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/cache_utils.py +0 -0
  208. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/config_utils.py +0 -0
  209. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/__init__.py +0 -0
  210. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/date_utils.py +0 -0
  211. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/mail_utils.py +0 -0
  212. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/arxiv_feed.toml +0 -0
  213. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/decarbonation_feed.toml +0 -0
  214. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/llm_en_feed.toml +0 -0
  215. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/llm_feed.toml +0 -0
  216. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/nlp_feed.toml +0 -0
  217. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/rte_feed.toml +0 -0
  218. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +0 -0
  219. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +0 -0
  220. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +0 -0
  221. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +0 -0
  222. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +0 -0
  223. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +0 -0
  224. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/__init__.py +0 -0
  225. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/__main__.py +0 -0
  226. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/arxiv_provider.py +0 -0
  227. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/atom_feed_provider.py +0 -0
  228. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/bing_news_provider.py +0 -0
  229. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/data_provider.py +0 -0
  230. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/data_provider_utils.py +0 -0
  231. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/google_news_provider.py +0 -0
  232. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/rss_feed_provider.py +0 -0
  233. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/utils.py +0 -0
  234. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/__init__.py +0 -0
  235. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/.streamlit/config.toml +0 -0
  236. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/__init__.py +0 -0
  237. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/app.py +0 -0
  238. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/app_utils.py +0 -0
  239. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/config.toml +0 -0
  240. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/import_extraction_article.ipynb +0 -0
  241. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/prompts.py +0 -0
  242. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/start_newsletter_generator.sh +0 -0
  243. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/tabs/tab1.py +0 -0
  244. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/tabs/tab2.py +0 -0
  245. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/tabs/tab3.py +0 -0
  246. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/dataviz/Main_page.py +0 -0
  247. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/dataviz/__init__.py +0 -0
  248. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +0 -0
  249. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/__init__.py +0 -0
  250. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/entity_utils.py +0 -0
  251. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/__init__.py +0 -0
  252. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/common_factory.py +0 -0
  253. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +0 -0
  254. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/nlp.config +0 -0
  255. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/utils.py +0 -0
  256. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/start_apps.sh +0 -0
  257. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/stop_apps.sh +0 -0
  258. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/install_feeds_and_newsletters.py +0 -0
  259. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/__init__.py +0 -0
  260. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/newsletter_generation.py +0 -0
  261. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/utils.py +0 -0
  262. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/.streamlit/config.toml +0 -0
  263. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/__init__.py +0 -0
  264. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/app.py +0 -0
  265. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/authentication.py +0 -0
  266. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_common.py +0 -0
  267. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_comparative.py +0 -0
  268. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_signals.py +0 -0
  269. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/data_model.py +0 -0
  270. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/detailed_report_template.html +0 -0
  271. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/docs/automated_report_generation.md +0 -0
  272. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/feeds_common.py +0 -0
  273. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/feeds_data.py +0 -0
  274. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/i18n.py +0 -0
  275. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/llm_utils.py +0 -0
  276. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/perf_utils.py +0 -0
  277. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/report_generation_utils.py +0 -0
  278. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/start_demo.sh +0 -0
  279. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/utils.py +0 -0
  280. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/__init__.py +0 -0
  281. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/bertrend_service.py +0 -0
  282. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/models/__init__.py +0 -0
  283. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/models/bertrend_app_models.py +0 -0
  284. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/models/data_provider_models.py +0 -0
  285. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/__init__.py +0 -0
  286. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/info.py +0 -0
  287. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/status.py +0 -0
  288. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/stop.py +0 -0
  289. {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/utils/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bertrend
3
- Version: 0.3.22
3
+ Version: 0.3.23
4
4
  Summary: BERTrend analyses topic evolution over time using state-of-the-art transformer models: topic modelling and LLM
5
5
  License: LICENSE.md
6
6
  License-File: AUTHORS.txt
@@ -42,7 +42,7 @@ Requires-Dist: langdetect (>=1.0.9)
42
42
  Requires-Dist: loguru (>=0.7.3)
43
43
  Requires-Dist: lxml-html-clean (>=0.2.2)
44
44
  Requires-Dist: markdown (>=3.7)
45
- Requires-Dist: newscatcherapi (>=0.7.3) ; extra == "apps"
45
+ Requires-Dist: newscatcher-sdk (>=1.4.0) ; extra == "apps"
46
46
  Requires-Dist: newspaper4k (>=0.9.3.1) ; extra == "apps"
47
47
  Requires-Dist: nltk (>=3.9.1)
48
48
  Requires-Dist: numpy (<2)
@@ -8,17 +8,12 @@ import re
8
8
  from enum import Enum
9
9
  from typing import Type
10
10
 
11
- from dotenv import load_dotenv
12
11
  from openai import OpenAI, AzureOpenAI, Timeout, Stream
13
12
  from loguru import logger
14
13
  from openai.types.chat import ChatCompletion, ChatCompletionChunk
15
14
  from pydantic import BaseModel
16
15
 
17
- # Load environment variables at module import
18
- if load_dotenv(override=True):
19
- logger.info("Loaded .env file")
20
- else:
21
- logger.warning("Failed to load .env file")
16
+ # Note: .env is loaded in bertrend/__init__.py which is imported before this module
22
17
 
23
18
  MAX_ATTEMPTS = 3
24
19
  TIMEOUT = 60.0
@@ -4,16 +4,19 @@
4
4
  # This file is part of BERTrend.
5
5
 
6
6
  import os
7
- import uvicorn
8
7
 
8
+ # Load the configuration BEFORE any other imports that might use CUDA
9
9
  from bertrend.services.embedding_server.config.settings import get_config
10
10
 
11
- # Load the configuration
12
11
  CONFIG = get_config()
13
12
 
14
- # Set the CUDA_VISIBLE_DEVICES environment variable
13
+ # Set the CUDA_VISIBLE_DEVICES environment variable BEFORE importing uvicorn
14
+ # This is critical because uvicorn will import the app module, which imports torch
15
+ # We override the value with the content of the config
15
16
  os.environ["CUDA_VISIBLE_DEVICES"] = CONFIG.cuda_visible_devices
16
17
 
18
+ import uvicorn
19
+
17
20
  # Start the FastAPI application
18
21
  if __name__ == "__main__":
19
22
  uvicorn.run(
@@ -4,7 +4,6 @@ services:
4
4
  context: .
5
5
  dockerfile: Dockerfile
6
6
  container_name: bertrend-scheduler
7
- user: "${HOST_UID:-1000}:${HOST_GID:-1000}"
8
7
  ports:
9
8
  - "8882:8000"
10
9
  volumes:
@@ -5,7 +5,6 @@
5
5
  import time
6
6
 
7
7
  import requests
8
- from fastapi import HTTPException
9
8
  from loguru import logger
10
9
 
11
10
 
@@ -24,7 +23,7 @@ def http_request(
24
23
  method: str = "GET",
25
24
  headers: dict = None,
26
25
  json_data: dict = None,
27
- timeout: int = 30,
26
+ timeout: int = 600, # 10 min
28
27
  ):
29
28
  """Execute an HTTP request (curl-like functionality)"""
30
29
  logger.info(f"Executing HTTP {method} request to {url}")
@@ -42,9 +41,7 @@ def http_request(
42
41
  return f"Request to {url} completed with status {response.status_code}"
43
42
  except Exception as e:
44
43
  logger.error(f"HTTP request failed: {str(e)}")
45
- raise HTTPException(
46
- status_code=500, detail=f"Error executing request: {str(e)}"
47
- )
44
+ raise RuntimeError(f"Error executing request: {str(e)}")
48
45
 
49
46
 
50
47
  # Job function registry
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python
2
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
3
+ # See AUTHORS.txt
4
+ # SPDX-License-Identifier: MPL-2.0
5
+ # This file is part of BERTrend.
6
+
7
+ """
8
+ Test script to verify lazy initialization of SCHEDULER_UTILS.
9
+ This tests that importing bertrend_apps doesn't immediately create scheduler instances.
10
+ """
11
+ import os
12
+ import sys
13
+ import multiprocessing
14
+
15
+
16
+ def helper_test_lazy_init_in_worker():
17
+ """Test that SCHEDULER_UTILS is lazily initialized in worker processes."""
18
+ try:
19
+ pid = os.getpid()
20
+ print(f"[Worker {pid}] Importing bertrend_apps...")
21
+
22
+ # Import should not trigger initialization
23
+ from bertrend_apps import SCHEDULER_UTILS
24
+
25
+ print(f"[Worker {pid}] Import complete (should not have initialized yet)")
26
+
27
+ # Now actually use it - this should trigger initialization
28
+ print(f"[Worker {pid}] Accessing SCHEDULER_UTILS...")
29
+ scheduler_type = type(SCHEDULER_UTILS).__name__
30
+ print(f"[Worker {pid}] Successfully accessed SCHEDULER_UTILS: {scheduler_type}")
31
+
32
+ # Try to call a method to ensure proxy works
33
+ if hasattr(SCHEDULER_UTILS, "add_job_to_crontab"):
34
+ print(f"[Worker {pid}] Proxy working correctly - method accessible")
35
+
36
+ return True
37
+ except Exception as e:
38
+ print(f"[Worker {pid}] Failed: {e}")
39
+ import traceback
40
+
41
+ traceback.print_exc()
42
+ return False
43
+
44
+
45
+ def test_lazy_init_in_worker():
46
+ """Main test function."""
47
+ print(f"[Main {os.getpid()}] Starting lazy initialization test...")
48
+
49
+ # Test with multiple processes (simulating uvicorn workers)
50
+ num_workers = 2
51
+ processes = []
52
+
53
+ for i in range(num_workers):
54
+ p = multiprocessing.Process(target=helper_test_lazy_init_in_worker)
55
+ p.start()
56
+ processes.append(p)
57
+
58
+ # Wait for all processes to complete
59
+ success = True
60
+ for p in processes:
61
+ p.join()
62
+ if p.exitcode != 0:
63
+ success = False
64
+ print(f"[Main] Worker process {p.pid} exited with code {p.exitcode}")
65
+
66
+ if success:
67
+ print("[Main] ✓ All workers completed successfully with lazy initialization!")
68
+ else:
69
+ print("[Main] ✗ Some workers failed!")
70
+
71
+ assert success == True
@@ -14,8 +14,6 @@ from bertrend_apps.services.models.bertrend_app_models import (
14
14
  )
15
15
  from bertrend_apps.services.models.newsletters_models import (
16
16
  NewsletterRequest,
17
- NewsletterResponse,
18
- ScheduleNewsletterRequest,
19
17
  )
20
18
  from bertrend_apps.services.models.data_provider_models import (
21
19
  ScrapeRequest,
@@ -83,32 +81,15 @@ class TestNewslettersModels:
83
81
  with pytest.raises(ValidationError):
84
82
  NewsletterRequest(newsletter_toml_path=Path("test.toml"))
85
83
 
86
- def test_newsletter_response_valid(self, tmp_path):
87
- output_path = tmp_path / "output.html"
88
- resp = NewsletterResponse(output_path=output_path, status="success")
89
- assert resp.output_path == output_path
90
- assert resp.status == "success"
91
-
92
84
  def test_schedule_newsletter_request_valid(self, tmp_path):
93
85
  newsletter_path = tmp_path / "newsletter.toml"
94
86
  feed_path = tmp_path / "feed.toml"
95
- req = ScheduleNewsletterRequest(
96
- newsletter_toml_cfg_path=newsletter_path,
97
- data_feed_toml_cfg_path=feed_path,
98
- )
99
- assert req.newsletter_toml_cfg_path == newsletter_path
100
- assert req.data_feed_toml_cfg_path == feed_path
101
- assert req.cuda_devices is None # default value
102
-
103
- def test_schedule_newsletter_request_with_cuda(self, tmp_path):
104
- newsletter_path = tmp_path / "newsletter.toml"
105
- feed_path = tmp_path / "feed.toml"
106
- req = ScheduleNewsletterRequest(
107
- newsletter_toml_cfg_path=newsletter_path,
108
- data_feed_toml_cfg_path=feed_path,
109
- cuda_devices="0,1",
87
+ req = NewsletterRequest(
88
+ newsletter_toml_path=newsletter_path,
89
+ data_feed_toml_path=feed_path,
110
90
  )
111
- assert req.cuda_devices == "0,1"
91
+ assert req.newsletter_toml_path == newsletter_path
92
+ assert req.data_feed_toml_path == feed_path
112
93
 
113
94
 
114
95
  class TestDataProviderModels:
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env python
2
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
3
+ # See AUTHORS.txt
4
+ # SPDX-License-Identifier: MPL-2.0
5
+ # This file is part of BERTrend.
6
+
7
+ """
8
+ Test script to verify the multi-worker fix.
9
+ This script simulates what happens when uvicorn forks worker processes.
10
+ """
11
+ import os
12
+ import sys
13
+ import multiprocessing
14
+
15
+
16
+ def helper_test_import_in_worker():
17
+ """Test that importing bertrend_apps works in a forked worker process."""
18
+ try:
19
+ # Import bertrend_apps which triggers SCHEDULER_UTILS initialization
20
+ from bertrend_apps import SCHEDULER_UTILS
21
+
22
+ print(
23
+ f"[Worker {os.getpid()}] Successfully imported SCHEDULER_UTILS: {type(SCHEDULER_UTILS).__name__}"
24
+ )
25
+ return True
26
+ except Exception as e:
27
+ print(f"[Worker {os.getpid()}] Failed to import: {e}")
28
+ return False
29
+
30
+
31
+ def test_multiworker():
32
+ """Main test function."""
33
+ print(f"[Main {os.getpid()}] Starting multi-worker import test...")
34
+
35
+ # Test with multiple processes (simulating uvicorn workers)
36
+ num_workers = 2
37
+ processes = []
38
+
39
+ for i in range(num_workers):
40
+ p = multiprocessing.Process(target=helper_test_import_in_worker)
41
+ p.start()
42
+ processes.append(p)
43
+
44
+ # Wait for all processes to complete
45
+ success = True
46
+ for p in processes:
47
+ p.join()
48
+ if p.exitcode != 0:
49
+ success = False
50
+ print(f"[Main] Worker process {p.pid} exited with code {p.exitcode}")
51
+
52
+ if success:
53
+ print("[Main] ✓ All workers completed successfully!")
54
+ else:
55
+ print("[Main] ✗ Some workers failed!")
56
+
57
+ assert success
@@ -0,0 +1,74 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+
6
+ """
7
+ Pytest tests to verify that job_functions.http_request properly raises
8
+ RuntimeError instead of HTTPException, which fixes the pickling issue
9
+ in multiprocessing contexts.
10
+ """
11
+
12
+ import pickle
13
+ import pytest
14
+ from concurrent.futures import ProcessPoolExecutor
15
+
16
+ from bertrend.services.scheduling.job_utils.job_functions import http_request
17
+
18
+
19
+ def job_wrapper_for_pool():
20
+ """Module-level wrapper to call http_request in a subprocess"""
21
+ try:
22
+ http_request("http://invalid-host-that-does-not-exist-12345.com", timeout=1)
23
+ except RuntimeError as e:
24
+ return f"RuntimeError: {str(e)[:50]}"
25
+ except Exception as e:
26
+ return f"OtherError: {type(e).__name__}: {str(e)[:50]}"
27
+
28
+
29
+ def test_exception_is_picklable():
30
+ """Test that the exception raised by http_request can be pickled"""
31
+ with pytest.raises(RuntimeError) as exc_info:
32
+ # Try to trigger an exception by connecting to an invalid URL
33
+ http_request("http://invalid-host-that-does-not-exist-12345.com", timeout=1)
34
+
35
+ # Verify the exception was caught
36
+ exception = exc_info.value
37
+ assert isinstance(exception, RuntimeError)
38
+
39
+ # Try to pickle the exception
40
+ pickled = pickle.dumps(exception)
41
+ unpickled = pickle.loads(pickled)
42
+
43
+ # Verify unpickling worked correctly
44
+ assert isinstance(unpickled, RuntimeError)
45
+ assert type(exception).__name__ == type(unpickled).__name__
46
+
47
+
48
+ def test_exception_in_process_pool():
49
+ """Test that http_request exceptions work properly in ProcessPoolExecutor"""
50
+ with ProcessPoolExecutor(max_workers=1) as executor:
51
+ future = executor.submit(job_wrapper_for_pool)
52
+ result = future.result(timeout=10)
53
+
54
+ # Verify that the RuntimeError was properly handled across process boundary
55
+ assert "RuntimeError" in result
56
+ assert "Error executing request" in result
57
+
58
+
59
+ def test_timeout_scenario():
60
+ """Test with a timeout scenario (simulates the original error)"""
61
+ with pytest.raises(RuntimeError) as exc_info:
62
+ # Use a valid host but very short timeout to trigger timeout
63
+ http_request("https://www.google.com", timeout=0.001)
64
+
65
+ error_msg = str(exc_info.value)
66
+
67
+ # Verify it's a timeout-related or network-related error
68
+ # (GitHub runners may have no network access, causing "network is unreachable" instead of timeout)
69
+ assert (
70
+ "timeout" in error_msg.lower()
71
+ or "timed out" in error_msg.lower()
72
+ or "network is unreachable" in error_msg.lower()
73
+ )
74
+ assert "Error executing request" in error_msg
@@ -107,23 +107,17 @@ class TestHelperFunctions:
107
107
  class TestAPSchedulerUtils:
108
108
  """Tests for APSchedulerUtils class."""
109
109
 
110
- @patch("bertrend_apps.common.apscheduler_utils._request")
111
- def test_init_success(self, mock_request):
110
+ def test_init_success(self):
112
111
  """Test successful initialization."""
113
- mock_response = MagicMock()
114
- mock_response.status_code = 200
115
- mock_request.return_value = mock_response
116
-
112
+ # Health check removed from __init__ to avoid blocking worker startup
117
113
  scheduler = APSchedulerUtils()
118
114
 
119
- mock_request.assert_called_once_with("GET", "/health")
115
+ assert scheduler is not None
120
116
 
121
- @patch("bertrend_apps.common.apscheduler_utils._request")
122
- def test_init_service_unavailable(self, mock_request):
117
+ def test_init_service_unavailable(self):
123
118
  """Test initialization when service is unavailable."""
124
- mock_request.side_effect = Exception("Connection error")
125
-
126
- # Should not raise, just log error
119
+ # Health check removed from __init__ to avoid blocking worker startup
120
+ # Initialization should always succeed regardless of service availability
127
121
  scheduler = APSchedulerUtils()
128
122
 
129
123
  assert scheduler is not None
@@ -233,15 +227,11 @@ class TestAPSchedulerUtils:
233
227
  @patch("bertrend_apps.common.apscheduler_utils._request")
234
228
  def test_add_job_to_crontab_success(self, mock_request):
235
229
  """Test successfully adding a job."""
236
- # Mock health check
237
- mock_health_response = MagicMock()
238
- mock_health_response.status_code = 200
239
-
240
230
  # Mock job creation
241
231
  mock_job_response = MagicMock()
242
232
  mock_job_response.status_code = 201
243
233
 
244
- mock_request.side_effect = [mock_health_response, mock_job_response]
234
+ mock_request.return_value = mock_job_response
245
235
 
246
236
  scheduler = APSchedulerUtils()
247
237
  result = scheduler.add_job_to_crontab(
@@ -251,7 +241,7 @@ class TestAPSchedulerUtils:
251
241
  )
252
242
 
253
243
  assert result is True
254
- assert mock_request.call_count == 2
244
+ assert mock_request.call_count == 1
255
245
 
256
246
  @patch("bertrend_apps.common.apscheduler_utils._request")
257
247
  def test_add_job_to_crontab_with_kwargs(self, mock_request):
@@ -280,15 +270,12 @@ class TestAPSchedulerUtils:
280
270
  @patch("bertrend_apps.common.apscheduler_utils._request")
281
271
  def test_add_job_to_crontab_failure(self, mock_request):
282
272
  """Test adding a job when request fails."""
283
- mock_health_response = MagicMock()
284
- mock_health_response.status_code = 200
285
-
286
273
  mock_job_response = MagicMock()
287
274
  mock_job_response.status_code = 400
288
275
  mock_job_response.text = "Bad request"
289
276
  mock_job_response.json.return_value = {"detail": "Invalid data"}
290
277
 
291
- mock_request.side_effect = [mock_health_response, mock_job_response]
278
+ mock_request.return_value = mock_job_response
292
279
 
293
280
  scheduler = APSchedulerUtils()
294
281
  result = scheduler.add_job_to_crontab(
@@ -310,20 +297,17 @@ class TestAPSchedulerUtils:
310
297
  }
311
298
  mock_load_config.return_value = mock_config
312
299
 
313
- mock_health_response = MagicMock()
314
- mock_health_response.status_code = 200
315
-
316
300
  mock_job_response = MagicMock()
317
301
  mock_job_response.status_code = 201
318
302
 
319
- mock_request.side_effect = [mock_health_response, mock_job_response]
303
+ mock_request.return_value = mock_job_response
320
304
 
321
305
  scheduler = APSchedulerUtils()
322
306
  feed_cfg = Path("/path/to/feed.toml")
323
307
  scheduler.schedule_scrapping(feed_cfg, user="testuser")
324
308
 
325
309
  # Verify job creation was called
326
- assert mock_request.call_count == 2
310
+ assert mock_request.call_count == 1
327
311
 
328
312
  @patch("bertrend_apps.common.apscheduler_utils.load_toml_config")
329
313
  @patch("bertrend_apps.common.apscheduler_utils._request")
@@ -337,20 +321,17 @@ class TestAPSchedulerUtils:
337
321
  }
338
322
  mock_load_config.return_value = mock_config
339
323
 
340
- mock_health_response = MagicMock()
341
- mock_health_response.status_code = 200
342
-
343
324
  mock_job_response = MagicMock()
344
325
  mock_job_response.status_code = 200
345
326
 
346
- mock_request.side_effect = [mock_health_response, mock_job_response]
327
+ mock_request.return_value = mock_job_response
347
328
 
348
329
  scheduler = APSchedulerUtils()
349
330
  newsletter_cfg = Path("/path/to/newsletter.toml")
350
331
  feed_cfg = Path("/path/to/feed.toml")
351
- scheduler.schedule_newsletter(newsletter_cfg, feed_cfg, cuda_devices="1")
332
+ scheduler.schedule_newsletter(newsletter_cfg, feed_cfg)
352
333
 
353
- assert mock_request.call_count == 2
334
+ assert mock_request.call_count == 1
354
335
 
355
336
  @patch("bertrend_apps.common.apscheduler_utils._request")
356
337
  def test_schedule_training_for_user(self, mock_request):
@@ -400,10 +381,6 @@ class TestAPSchedulerUtils:
400
381
  @patch("bertrend_apps.common.apscheduler_utils._request")
401
382
  def test_schedule_report_generation_auto_send_disabled(self, mock_request):
402
383
  """Test that report generation is not scheduled when auto_send is disabled."""
403
- mock_health_response = MagicMock()
404
- mock_health_response.status_code = 200
405
- mock_request.return_value = mock_health_response
406
-
407
384
  scheduler = APSchedulerUtils()
408
385
  report_config = {
409
386
  "auto_send": False,
@@ -417,8 +394,8 @@ class TestAPSchedulerUtils:
417
394
  )
418
395
 
419
396
  assert result is False
420
- # Only health check should be called
421
- assert mock_request.call_count == 1
397
+ # No job creation should happen when auto_send is disabled
398
+ assert mock_request.call_count == 0
422
399
 
423
400
  @patch("bertrend_apps.common.apscheduler_utils._request")
424
401
  def test_schedule_report_generation_no_recipients(self, mock_request):
@@ -190,7 +190,6 @@ class TestCrontabSchedulerUtils:
190
190
  "bertrend_apps.common.crontab_utils.CrontabSchedulerUtils.add_job_to_crontab"
191
191
  )
192
192
  @patch("bertrend_apps.common.crontab_utils.BERTREND_LOG_PATH", Path("/logs"))
193
- @patch("bertrend_apps.common.crontab_utils.BEST_CUDA_DEVICE", "0")
194
193
  def test_schedule_newsletter(self, mock_add_job, mock_load_config):
195
194
  """Test scheduling a newsletter job."""
196
195
  mock_config = {
@@ -204,13 +203,12 @@ class TestCrontabSchedulerUtils:
204
203
 
205
204
  newsletter_cfg = Path("/path/to/newsletter.toml")
206
205
  feed_cfg = Path("/path/to/feed.toml")
207
- self.scheduler.schedule_newsletter(newsletter_cfg, feed_cfg, cuda_devices="0")
206
+ self.scheduler.schedule_newsletter(newsletter_cfg, feed_cfg)
208
207
 
209
208
  mock_add_job.assert_called_once()
210
209
  call_args = mock_add_job.call_args
211
210
  assert call_args[0][0] == "0 8 * * *"
212
211
  assert "newsletters" in call_args[0][1]
213
- assert "CUDA_VISIBLE_DEVICES=0" in call_args[0][2]
214
212
 
215
213
  @patch(
216
214
  "bertrend_apps.common.crontab_utils.CrontabSchedulerUtils.add_job_to_crontab"
@@ -106,9 +106,15 @@ def load_data(
106
106
  language (str): The language of the text data ('French' or 'English').
107
107
 
108
108
  Returns:
109
- pd.DataFrame: The loaded and preprocessed DataFrame.
109
+ pd.DataFrame: The loaded and preprocessed DataFrame, or None if the file is empty.
110
110
  """
111
111
  df = _file_to_pd(selected_file)
112
+
113
+ # Check if the DataFrame is empty (no rows or no columns)
114
+ if df is None or df.empty or len(df.columns) == 0:
115
+ logger.warning(f"Skipping empty file: {selected_file.name}")
116
+ return None
117
+
112
118
  _check_data(df, selected_file)
113
119
  return _clean_data(df, language)
114
120
 
@@ -0,0 +1,43 @@
1
+ # Copyright (c) 2024, RTE (https://www.rte-france.com)
2
+ # See AUTHORS.txt
3
+ # SPDX-License-Identifier: MPL-2.0
4
+ # This file is part of BERTrend.
5
+ import os
6
+
7
+ from dotenv import load_dotenv
8
+ from loguru import logger
9
+
10
+ from bertrend_apps.common.apscheduler_utils import APSchedulerUtils
11
+ from bertrend_apps.common.crontab_utils import CrontabSchedulerUtils
12
+
13
+
14
+ # Lazy initialization to avoid fork issues with multiple workers
15
+ _scheduler_utils = None
16
+
17
+
18
+ def _get_scheduler_utils():
19
+ """Get or create the scheduler utils instance for the current process."""
20
+ global _scheduler_utils
21
+ if _scheduler_utils is None:
22
+ # Note: .env is already loaded in bertrend/__init__.py
23
+ # Load scheduler type and URL from environment variables
24
+ scheduler_type = os.getenv("SCHEDULER_SERVICE_TYPE", "crontab").lower()
25
+
26
+ if scheduler_type == "apscheduler":
27
+ _scheduler_utils = APSchedulerUtils()
28
+ else: # assume default="crontab":
29
+ _scheduler_utils = CrontabSchedulerUtils()
30
+ logger.info("Using CrontabScheduler")
31
+
32
+ return _scheduler_utils
33
+
34
+
35
+ # Provide SCHEDULER_UTILS as a property-like access for backward compatibility
36
+ class _SchedulerUtilsProxy:
37
+ """Proxy to provide lazy initialization while maintaining the same API."""
38
+
39
+ def __getattr__(self, name):
40
+ return getattr(_get_scheduler_utils(), name)
41
+
42
+
43
+ SCHEDULER_UTILS = _SchedulerUtilsProxy()