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.
- {bertrend-0.3.22 → bertrend-0.3.23}/PKG-INFO +2 -2
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/openai_client.py +1 -6
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/start.py +6 -3
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/docker-compose.yml +0 -1
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/job_utils/job_functions.py +2 -5
- bertrend-0.3.23/bertrend/tests/apps/test_lazy_scheduler.py +71 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_models.py +5 -24
- bertrend-0.3.23/bertrend/tests/apps/test_multiworker.py +57 -0
- bertrend-0.3.23/bertrend/tests/services/scheduling/test_job_exception_fix.py +74 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_apscheduler_utils.py +16 -39
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_crontab_utils.py +1 -3
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/data_loading.py +7 -1
- bertrend-0.3.23/bertrend_apps/__init__.py +43 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/apscheduler_utils.py +97 -29
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/crontab_utils.py +16 -4
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/scheduler_utils.py +16 -2
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/newscatcher_provider.py +8 -4
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/__main__.py +1 -2
- bertrend-0.3.23/bertrend_apps/prospective_demo/.streamlit/secrets.toml.example +9 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/automated_report_generation.py +25 -14
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_analysis.py +14 -8
- bertrend-0.3.23/bertrend_apps/prospective_demo/docs/architecture.md +82 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/feeds_config.py +14 -5
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/i18n_translations.py +8 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/models_info.py +7 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/process_new_data.py +8 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/report_generation.py +11 -4
- bertrend-0.3.23/bertrend_apps/services/config/default_config.toml +3 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/config/settings.py +0 -1
- bertrend-0.3.23/bertrend_apps/services/models/newsletters_models.py +16 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/bertrend_app.py +2 -2
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/data_provider.py +1 -2
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/newsletters.py +20 -16
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/start.py +2 -6
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/utils/logging_utils.py +4 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/pyproject.toml +2 -2
- bertrend-0.3.22/bertrend_apps/__init__.py +0 -36
- bertrend-0.3.22/bertrend_apps/config/newsletters/decarbonation_newsletter_from_scratch.toml +0 -38
- bertrend-0.3.22/bertrend_apps/config/newsletters/rte_newsletter_from_scratch.toml +0 -38
- bertrend-0.3.22/bertrend_apps/prospective_demo/.streamlit/secrets.toml +0 -14
- bertrend-0.3.22/bertrend_apps/services/config/default_config.toml +0 -4
- bertrend-0.3.22/bertrend_apps/services/models/newsletters_models.py +0 -34
- {bertrend-0.3.22 → bertrend-0.3.23}/AUTHORS.txt +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/LICENSE.md +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/README.md +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/BERTopicModel.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/BERTrend.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/article_scoring.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/prompts.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/article_scoring/scoring_agent.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/bertopic_default_config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/bertrend_default_config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/parameters.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/config/services_default_config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/data_loading_component.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/embed_documents_component.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/i18n.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/i18n_translations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/icons.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/parameters_component.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/demos_utils/state_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/start_summarizer_app.sh +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/start_topic_analysis_app.sh +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/start_weak_signals_app.sh +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/streamlit_components/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/streamlit_components/clickable_df_component.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/streamlit_components/input_with_pills_component.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/summarization/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/summarization/summarizer_app.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/.streamlit/config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/app.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/app_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/explore_topics.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/newsletters_generation.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/temporal_visualizations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/topic_visualizations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/demo_pages/training_page.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/topic_analysis/i18n_translations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/.streamlit/config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/app.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/i18n_translations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/demos/weak_signals/visualizations_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/agent_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter.css +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_features.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_model.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_outlook_template.html +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/newsletter_template.html +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/llm_utils/prompts.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/metrics/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/metrics/temporal_metrics_embedding.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/metrics/topic_metrics.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/common_ngrams.json +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/stopwords-en.json +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/stopwords-fr.json +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/resources/stopwords-rte.json +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/authentication.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/cert.pem +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_client.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/README.md +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/bertrend_client_registry.json +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/config/default_config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/config/settings.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/main.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/models.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/requirements.txt +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/authentication.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/embeddings.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/routers/info.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/security.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/status.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_server/stop.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/embedding_service.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/key.pem +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/.dockerignore +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/Docker_setup.md +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/Dockerfile +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/dashboard.html +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/job_utils/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/models/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/models/scheduling_models.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/requirements.txt +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/routers/info.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/routers/scheduling.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/scheduling/scheduling_service.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/abstractive_summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/chatgpt_summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/extractive_summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/lexrank.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/services/summary/prompts.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/README_integration_tests.md +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_bertrend_service.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_process_new_data.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_routers_bertrend_app.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/apps/test_routers_info.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/conftest.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_bertrend.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_data_structure.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_representative_docs.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_temporal_metrics_embedding.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_topic_description.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_topic_metrics.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_topic_model.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_data_structure.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_prompts.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_visualizations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_trend_analysis_weak_signals.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/core/test_visualizations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_agent_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_article_scoring.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_article_scoring_models.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_newsletter_features.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_newsletter_model.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/llm/test_openai_client.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/embedding/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/embedding/test_embedding_client.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/scheduling/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/scheduling/test_scheduling_service.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_abstractive_summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_chatgpt_summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_extractive_summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/services/summarizer/test_summarizer.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/test_installation.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/test_integration.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_cache_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_config_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_data_loading.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_date_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_mail_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_scheduler_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/tests/utils/test_utils_init.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/data_structure.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/prompts.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/representative_docs.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/topic_description.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/topic_analysis/visualizations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/data_structure.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/prompts.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/signal_llm_template_en.html +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/signal_llm_template_fr.html +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/visualizations.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/trend_analysis/weak_signals.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/cache_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend/utils/config_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/date_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/common/mail_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/arxiv_feed.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/decarbonation_feed.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/llm_en_feed.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/llm_feed.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/nlp_feed.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/feeds/rte_feed.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/arxiv_newsletter_from_last.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/decarbonation_newsletter_from_last.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/llm_en_newsletter_from_last.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/llm_newsletter_from_last.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/nlp_newsletter_from_last.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/config/newsletters/rte_newsletter_from_last.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/__main__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/arxiv_provider.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/atom_feed_provider.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/bing_news_provider.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/data_provider.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/data_provider_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/google_news_provider.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/rss_feed_provider.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/data_provider/utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/.streamlit/config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/app.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/app_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/import_extraction_article.ipynb +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/prompts.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/start_newsletter_generator.sh +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/tabs/tab1.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/tabs/tab2.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/curebot/tabs/tab3.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/dataviz/Main_page.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/dataviz/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/dataviz/pages/1_Data_feeds.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/entity_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/common_factory.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/dbpedia.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/nlp.config +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/geolocalization/spacy/utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/start_apps.sh +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/exploration/stop_apps.sh +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/install_feeds_and_newsletters.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/newsletter_generation.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/newsletters/utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/.streamlit/config.toml +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/app.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/authentication.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_common.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_comparative.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/dashboard_signals.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/data_model.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/detailed_report_template.html +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/docs/automated_report_generation.md +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/feeds_common.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/feeds_data.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/i18n.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/llm_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/perf_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/report_generation_utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/start_demo.sh +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/prospective_demo/utils.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/bertrend_service.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/models/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/models/bertrend_app_models.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/models/data_provider_models.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/__init__.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/routers/info.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/status.py +0 -0
- {bertrend-0.3.22 → bertrend-0.3.23}/bertrend_apps/services/stop.py +0 -0
- {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.
|
|
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:
|
|
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
|
-
#
|
|
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(
|
|
@@ -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 =
|
|
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
|
|
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 =
|
|
96
|
-
|
|
97
|
-
|
|
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.
|
|
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
|
-
|
|
111
|
-
def test_init_success(self, mock_request):
|
|
110
|
+
def test_init_success(self):
|
|
112
111
|
"""Test successful initialization."""
|
|
113
|
-
|
|
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
|
-
|
|
115
|
+
assert scheduler is not None
|
|
120
116
|
|
|
121
|
-
|
|
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
|
-
|
|
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.
|
|
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 ==
|
|
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.
|
|
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.
|
|
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 ==
|
|
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.
|
|
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
|
|
332
|
+
scheduler.schedule_newsletter(newsletter_cfg, feed_cfg)
|
|
352
333
|
|
|
353
|
-
assert mock_request.call_count ==
|
|
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
|
-
#
|
|
421
|
-
assert mock_request.call_count ==
|
|
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
|
|
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()
|