diff --git a/pywikitools/test/data/Heart-32.png b/pywikitools/test/data/Heart-32.png new file mode 100644 index 0000000..f5d6169 Binary files /dev/null and b/pywikitools/test/data/Heart-32.png differ diff --git a/pywikitools/test/data/example.html b/pywikitools/test/data/example.html new file mode 100644 index 0000000..dd26d4b --- /dev/null +++ b/pywikitools/test/data/example.html @@ -0,0 +1,74 @@ +
Other languages:
More information about Russian
+

Бог дал нам Свое Слово, чтобы мы знали, кто Он и чего хочет. Мы читаем Библию, чтобы лучше понять Бога и делать то, что Он говорит. Это самое важное. Но возникает вопрос: с чего начать чтение? В зависимости от вашей жизненной ситуации и прошлого опыта мы предлагаем вам разные варианты того, с чего можно начать. Во всех случаях основа остается одной и той же, но при этом для чтения предлагаются чуть разные книги. Ниже вы найдете "стандартный" план, который предлагает начать чтение с Луки и деяний, а также план, в котором в начале читается история Сотворения и вся книга Бытия, а затем Новый Завет. +

+

Подсказки для чтения Библии (семь историй, полных надежды)

+ +

Подсказки для чтения Библии (Начиная с Сотворения)

+ +

Подсказки для чтения Библии

+

Когда вы только приступаете к чтению Библии, начните с тех книг, которые приведены ниже: +

Евангелие от Луки
+Деяния апостолов +

Каждый раз, перед тем как читать Писание, попросите Бога помочь вам понять, то что вы будете читать. +

Ответьте на несколько вопросов (см.разворот) чтобы лучше понять, о чем говорит текст. +

Поделитесь с другими тем что вы прочитали, а также запишите свои мысли, вопросы и то что говорит вам Бог через прочитанное. +

+

Читая Библию вместе (в группе)

+

План встречи:

+
1. Как дела?
+
2. Подотчетность
+
+
Как мы применили то, что узнали в прошлый раз?
+
3. Благодарение и молитва
+
+
Что хорошего произошло с нами на прошлой неделе? Поблагодарите Бога.
+


+

+
4. Чтение
+
Прочитаем отрывок вместе. Попросить у Бога помощи чтобы понять стихи, которые мы прочитаем.
+
5. Повторение
+
Вместе, не смотря в книгу, повторяем пройденный материал.
+
6. Понимание
+
Ответим на следующие вопросы:
+ + + + +
Head-32.pngРазум: Что мы здесь узнаем из прочитанного? (О Боге / о людях)
Heart-32.pngСердце: Что касается моего сердца? (Как они чувствовали себя? Что я чувствую относительно ... ?)
Hands-32.pngРуки: Как я могу это применить? (Пример для подражания? С кем мы могли бы этим поделиться?)



+

7. Цели
+
+
Какая у нас цель до следующего собрания?
+
8. Молитва
+
+
Время помолиться друг за друга.
+

Важно:

+ +
\ No newline at end of file diff --git "a/pywikitools/test/data/htmlexport/ru/files/\320\230\321\201\321\206\320\265\320\273\320\265\320\275\320\270\320\265.html" "b/pywikitools/test/data/htmlexport/ru/files/\320\230\321\201\321\206\320\265\320\273\320\265\320\275\320\270\320\265.html" new file mode 100644 index 0000000..de81a9d --- /dev/null +++ "b/pywikitools/test/data/htmlexport/ru/files/\320\230\321\201\321\206\320\265\320\273\320\265\320\275\320\270\320\265.html" @@ -0,0 +1,49 @@ +

Исцеление

+

Бог дал нам Свое Слово, чтобы мы знали, кто Он и чего хочет. Мы читаем Библию, чтобы лучше понять Бога и делать то, что Он говорит. Это самое важное. Но возникает вопрос: с чего начать чтение? В зависимости от вашей жизненной ситуации и прошлого опыта мы предлагаем вам разные варианты того, с чего можно начать. Во всех случаях основа остается одной и той же, но при этом для чтения предлагаются чуть разные книги. Ниже вы найдете "стандартный" план, который предлагает начать чтение с Луки и деяний, а также план, в котором в начале читается история Сотворения и вся книга Бытия, а затем Новый Завет. +

+

Подсказки для чтения Библии (семь историй, полных надежды)

+ +

Подсказки для чтения Библии (Начиная с Сотворения)

+ +

Подсказки для чтения Библии

+

Когда вы только приступаете к чтению Библии, начните с тех книг, которые приведены ниже: +

Евангелие от Луки
+Деяния апостолов +

Каждый раз, перед тем как читать Писание, попросите Бога помочь вам понять, то что вы будете читать. +

Ответьте на несколько вопросов (см.разворот) чтобы лучше понять, о чем говорит текст. +

Поделитесь с другими тем что вы прочитали, а также запишите свои мысли, вопросы и то что говорит вам Бог через прочитанное. +

+

Читая Библию вместе (в группе)

+

План встречи:

+
1. Как дела?
+
2. Подотчетность
+
+
Как мы применили то, что узнали в прошлый раз?
+
3. Благодарение и молитва
+
+
Что хорошего произошло с нами на прошлой неделе? Поблагодарите Бога.
+


+

+
4. Чтение
+
Прочитаем отрывок вместе. Попросить у Бога помощи чтобы понять стихи, которые мы прочитаем.
+
5. Повторение
+
Вместе, не смотря в книгу, повторяем пройденный материал.
+
6. Понимание
+
Ответим на следующие вопросы:
+ + + + +
Head-32.pngРазум: Что мы здесь узнаем из прочитанного? (О Боге / о людях)
Heart-32.pngСердце: Что касается моего сердца? (Как они чувствовали себя? Что я чувствую относительно ... ?)
Hands-32.pngРуки: Как я могу это применить? (Пример для подражания? С кем мы могли бы этим поделиться?)



+

7. Цели
+
+
Какая у нас цель до следующего собрания?
+
8. Молитва
+
+
Время помолиться друг за друга.
+

Важно:

+ diff --git a/pywikitools/test/data/htmlexport/ru/structure/content.json b/pywikitools/test/data/htmlexport/ru/structure/content.json new file mode 100644 index 0000000..7a99795 --- /dev/null +++ b/pywikitools/test/data/htmlexport/ru/structure/content.json @@ -0,0 +1,48 @@ +{ + "language_code": "ru", + "english_name": "Russian", + "worksheets": [ + { + "page": "Healing", + "title": "\u0418\u0441\u0446\u0435\u043b\u0435\u043d\u0438\u0435", + "filename": "\u0418\u0441\u0446\u0435\u043b\u0435\u043d\u0438\u0435.html", + "version": "1.0", + "pdf": "\u0418\u0441\u0446\u0435\u043b\u0435\u043d\u0438\u0435.pdf" + }, + { + "page": "My_Story_with_God", + "title": "\u041c\u043e\u0451 \u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e", + "filename": "\u041c\u043e\u0451_\u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e.html", + "version": "1.1", + "pdf": "\u041c\u043e\u0451_\u0441\u0432\u0438\u0434\u0435\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e.pdf" + }, + { + "page": "Bible_Reading_Hints", + "title": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0411\u0438\u0431\u043b\u0438\u0438", + "filename": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438_\u0434\u043b\u044f_\u0447\u0442\u0435\u043d\u0438\u044f_\u0411\u0438\u0431\u043b\u0438\u0438.html", + "version": "2.0", + "pdf": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438_\u0434\u043b\u044f_\u0447\u0442\u0435\u043d\u0438\u044f_\u0411\u0438\u0431\u043b\u0438\u0438.pdf" + }, + { + "page": "Bible_Reading_Hints_(Seven_Stories_full_of_Hope)", + "title": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0411\u0438\u0431\u043b\u0438\u0438 (\u0421\u0435\u043c\u044c \u0438\u0441\u0442\u043e\u0440\u0438\u0439, \u043f\u043e\u043b\u043d\u044b\u0445 \u043d\u0430\u0434\u0435\u0436\u0434\u044b)", + "filename": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438_\u0434\u043b\u044f_\u0447\u0442\u0435\u043d\u0438\u044f_\u0411\u0438\u0431\u043b\u0438\u0438_(\u0421\u0435\u043c\u044c_\u0438\u0441\u0442\u043e\u0440\u0438\u0439,_\u043f\u043e\u043b\u043d\u044b\u0445_\u043d\u0430\u0434\u0435\u0436\u0434\u044b).html", + "version": "2.0", + "pdf": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438_\u0434\u043b\u044f_\u0447\u0442\u0435\u043d\u0438\u044f_\u0411\u0438\u0431\u043b\u0438\u0438_(\u0421\u0435\u043c\u044c_\u0438\u0441\u0442\u043e\u0440\u0438\u0439,_\u043f\u043e\u043b\u043d\u044b\u0445_\u043d\u0430\u0434\u0435\u0436\u0434\u044b).pdf" + }, + { + "page": "Bible_Reading_Hints_(Starting_with_the_Creation)", + "title": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438 \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0411\u0438\u0431\u043b\u0438\u0438 (\u041d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u0421\u043e\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f)", + "filename": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438_\u0434\u043b\u044f_\u0447\u0442\u0435\u043d\u0438\u044f_\u0411\u0438\u0431\u043b\u0438\u0438_(\u041d\u0430\u0447\u0438\u043d\u0430\u044f_\u0441_\u0421\u043e\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f).html", + "version": "2.0", + "pdf": "\u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438_\u0434\u043b\u044f_\u0447\u0442\u0435\u043d\u0438\u044f_\u0411\u0438\u0431\u043b\u0438\u0438_(\u041d\u0430\u0447\u0438\u043d\u0430\u044f_\u0441_\u0421\u043e\u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044f).pdf" + }, + { + "page": "Four_Kinds_of_Disciples", + "title": "\u0427\u0435\u0442\u044b\u0440\u0435 \u0432\u0438\u0434\u0430 \u0443\u0447\u0435\u043d\u0438\u043a\u043e\u0432", + "filename": "\u0427\u0435\u0442\u044b\u0440\u0435_\u0432\u0438\u0434\u0430_\u0443\u0447\u0435\u043d\u0438\u043a\u043e\u0432.html", + "version": "1.1", + "pdf": "\u0427\u0435\u0442\u044b\u0440\u0435_\u0432\u0438\u0434\u0430_\u0443\u0447\u0435\u043d\u0438\u043a\u043e\u0432.pdf" + } + ] +} \ No newline at end of file diff --git a/pywikitools/test/test_export_html.py b/pywikitools/test/test_export_html.py new file mode 100644 index 0000000..2817635 --- /dev/null +++ b/pywikitools/test/test_export_html.py @@ -0,0 +1,158 @@ +""" +Test all the functionalities of export_html.py +- creating folders if necessary +- get html contents for worksheets from API +- Export htmls into local directory +- download image files into local directory +- export content.json (with current content) + +Run tests: + python3 pywikitools/test/test_export_html.py +""" + +from os.path import abspath, dirname, join, exists +import tempfile +import json +import unittest + +import requests +from unittest.mock import Mock, patch + +from pywikitools.fortraininglib import ForTrainingLib +from pywikitools.resourcesbot.changes import ChangeLog, ChangeType +from pywikitools.resourcesbot.data_structures import LanguageInfo, json_decode +from pywikitools.resourcesbot.export_html import ExportHTML + + +class TestExportHTML(unittest.TestCase): + @classmethod + def setUpClass(self): + with open(join(dirname(abspath(__file__)), "data", "ru.json"), 'r') as f: + self.language_info: LanguageInfo = json.load(f, object_hook=json_decode) + # Create a pseudo English LanguageInfo - enough for our testing purposes (version is always the same) + self.english_info = LanguageInfo("en", "English") + for worksheet, info in self.language_info.worksheets.items(): + self.english_info.add_worksheet_info(worksheet, info) + self.fortraininglib = ForTrainingLib("https://test.4training.net") + + @patch("os.makedirs") + def test_run_with_empty_base_folder(self, mock_makedirs): + with self.assertLogs('pywikitools.resourcesbot.export_html', level='WARNING'): + export_html = ExportHTML(self.fortraininglib, "", force_rewrite=False) + + # run() should return without doing anything because of empty base folder + export_html.run(self.language_info, self.english_info, ChangeLog(), ChangeLog()) + mock_makedirs.assert_not_called() + + def test_run_filters_unfinished_worksheets(self): + fortraininglib_mock = Mock() + with tempfile.TemporaryDirectory() as temp_dir: + export_html = ExportHTML(fortraininglib_mock, temp_dir, force_rewrite=False) + with patch.object(export_html, 'has_relevant_change', return_value=False) as mock_has_relevant_change: + export_html.run(self.language_info, self.english_info, ChangeLog(), ChangeLog()) + calls = [call[0][0] for call in mock_has_relevant_change.call_args_list] + # Healing is finished, Church is an unfinished worksheet + self.assertIn('Healing', calls) + self.assertNotIn('Church', calls) + + fortraininglib_mock.get_page_html.assert_not_called() + # Verify that `language_info` remains unchanged + self.assertIsNotNone(self.language_info.get_worksheet('Church')) + + def test_directory_structure_creation(self): + with tempfile.TemporaryDirectory() as temp_dir: + # Create target paths to check later + base_folder = join(temp_dir, "not_existing_yet") + + # Base folder should be created directly when initializing the class + export_html = ExportHTML(self.fortraininglib, base_folder, force_rewrite=False) + self.assertTrue(exists(base_folder)) + export_html.run(self.language_info, self.english_info, ChangeLog(), ChangeLog()) + + # Assert that the right directories were created + self.assertTrue(exists(join(base_folder, "ru"))) + self.assertTrue(exists(join(base_folder, "ru", "files/"))) + self.assertTrue(exists(join(base_folder, "ru", "structure/"))) + + # assert that the method still works if the folders are already there + with self.assertNoLogs(level='WARNING'): + export_html.run(self.language_info, self.english_info, ChangeLog(), ChangeLog()) + + @patch('pywikitools.fortraininglib.ForTrainingLib.get_page_html') + def test_download_and_save_transformed_html_and_images(self, mock_get_page_html): + with open(join(dirname(abspath(__file__)), "data", "example.html"), 'r') as f: + mock_get_page_html.return_value = f.read() + changelog = ChangeLog() + changelog.add_change('Healing', ChangeType.UPDATED_WORKSHEET) + changelog.add_change("Church", ChangeType.NEW_WORKSHEET) + + # Mock the response for the image download + response = requests.Response() + response.status_code = 200 + with open(join(dirname(abspath(__file__)), "data", "Heart-32.png"), 'rb') as f: + response._content = f.read() + + # Initialize the ExportHTML class with a valid base folder + with tempfile.TemporaryDirectory() as temp_dir: + export_html = ExportHTML(self.fortraininglib, temp_dir, force_rewrite=False) + + with patch('requests.get', return_value=response): + export_html.run(self.language_info, self.english_info, changelog, ChangeLog()) + + # Assert the file was created correctly + path_to_transformed_html = join(temp_dir, 'ru', 'Исцеление.html') + self.assertTrue(exists(path_to_transformed_html)) + + # Assert the content is correct + expected_html = join(dirname(abspath(__file__)), "data", "htmlexport", "ru", "files", "Исцеление.html") + with open(path_to_transformed_html, 'r', encoding='utf-8') as test_file: + with open(expected_html, 'r', encoding='utf-8') as expected_file: + self.assertEqual(test_file.read(), expected_file.read()) + + self.assertTrue(exists(join(temp_dir, 'ru', 'files', 'Heart-32.png'))) + + path_to_contents = join(temp_dir, 'ru', 'structure', 'contents.json') + self.assertTrue(exists(path_to_contents)) + with open(path_to_contents, 'r') as test_file: + with open(join(dirname(abspath(__file__)), + "data", "htmlexport", "ru", "structure", "content.json"), 'r') as expected_file: + self.assertEqual(expected_file.read(), test_file.read()) + + def test_complex_export_html(self): + with tempfile.TemporaryDirectory() as temp_dir: + ar_changelog = ChangeLog() + # normal worksheet + ar_changelog.add_change('Hearing_from_God', ChangeType.UPDATED_WORKSHEET) + expected_path_hearing = join(temp_dir, 'ar', 'الاستماع_من_الله.html') + # worksheet with images + ar_changelog.add_change('Time_with_God', ChangeType.UPDATED_WORKSHEET) + expected_path_time = join(temp_dir, 'ar', 'قضاء_وقت_مع_الله.html') + # unfinished worksheet -> shouldn't be exported + ar_changelog.add_change("Church", ChangeType.NEW_WORKSHEET) + expected_path_church = join(temp_dir, 'ar', 'كنيسة.html') + # normal worksheet -> will only be created with force rewrite + expected_path_prayer = join(temp_dir, 'ar', 'الصلاة.html') + + with open(join(dirname(abspath(__file__)), "data", "ar.json"), 'r') as f: + ar_language_info: LanguageInfo = json.load(f, object_hook=json_decode) + with open(join(dirname(abspath(__file__)), "data", "en.json"), 'r') as f: + en_language_info: LanguageInfo = json.load(f, object_hook=json_decode) + + export_html = ExportHTML(self.fortraininglib, temp_dir, force_rewrite=False) + export_html.run(ar_language_info, en_language_info, ar_changelog, ChangeLog()) + + self.assertTrue(exists(join(temp_dir, 'ar', 'files', 'Head-32.png'))) + self.assertTrue(exists(expected_path_hearing)) + self.assertTrue(exists(expected_path_time)) + self.assertFalse(exists(expected_path_church)) + + # run with force rewrite + self.assertFalse(exists(expected_path_prayer)) + export_html = ExportHTML(self.fortraininglib, temp_dir, force_rewrite=True) + with self.assertLogs(): + export_html.run(ar_language_info, en_language_info, ar_changelog, ChangeLog()) + self.assertTrue(exists(expected_path_prayer)) + + +if __name__ == '__main__': + unittest.main() diff --git a/setup.py b/setup.py index 3090d48..0d371b2 100644 --- a/setup.py +++ b/setup.py @@ -19,17 +19,17 @@ setup( author="4training", author_email='pywikitools@4training.net', - python_requires='>=3.5', + python_requires='>=3.10', classifiers=[ 'Development Status :: 2 - Pre-Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Natural Language :: English', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ], description="Python tools for mediawiki with the Translate plugin (some based on pywikibot)", entry_points={