From a22785a1d70024e01724a26c69a338b5515f1f04 Mon Sep 17 00:00:00 2001 From: Maria Akhaladze Date: Sat, 11 Feb 2023 22:23:01 +0400 Subject: [PATCH 1/6] Added models and readers for them --- src/formatters/models.py | 83 +++++++++++++++++++++++++++++++++++++- src/readers/reader.py | 86 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/src/formatters/models.py b/src/formatters/models.py index c9236ca..46c75f0 100644 --- a/src/formatters/models.py +++ b/src/formatters/models.py @@ -54,7 +54,6 @@ class InternetResourceModel(BaseModel): class ArticlesCollectionModel(BaseModel): - """ Модель сборника статей: @@ -78,3 +77,85 @@ class ArticlesCollectionModel(BaseModel): publishing_house: str year: int = Field(..., gt=0) pages: str + + +class AbstractModel(BaseModel): + """ + Модель автореферата: + + .. code-block:: + + ArticlesCollectionModel( + author="Иванов И.М.", + abstract_title="Наука как искусство", + author_status="д-р. / канд.", + science_field="экон.", + specialty_code="01.01.01", + city="Спб.", + year=2020, + pages="199", + ) + """ + + author: str + abstract_title: str + author_status: str + science_field: str + specialty_code: str + city: str + year: int = Field(..., gt=0) + pages: str + + +class RegulationModel(BaseModel): + """ + Модель нормативного акта: + + .. code-block:: + + ArticlesCollectionModel( + regulation_title="Наука как искусство", + source="Конституция Российской Федерации", + publishing_source="Парламентская газета", + regulation_id="1234-56", + acceptance_date="1/1/2000", + publishing_year="2020", + publishing_source_id="5", + publishing_article_id="15", + modification_date="9/11/2002" + ) + """ + + regulation_title: str + source: str + publishing_source: str + regulation_id: str + acceptance_date: str + publishing_year: int = Field(..., gt=0) + publishing_source_id: str + publishing_article_id: str + modification_date: str + + +class NewsPaperModel(BaseModel): + """ + Модель газеты: + + .. code-block:: + + NewsPaperModel( + article_title="Наука как искусство", + authors="Иванов И.М., Петров С.Н.", + news_title="Южный Урал", + publishing_year="1980", + publishing_date="01.10", + publishing_number="5" + ) + """ + + article_title: str + authors: str + news_title: str + publishing_year: int = Field(..., gt=0) + publishing_date: str + publishing_number: int = Field(..., gt=0) diff --git a/src/readers/reader.py b/src/readers/reader.py index 9007a80..6098b3e 100644 --- a/src/readers/reader.py +++ b/src/readers/reader.py @@ -7,11 +7,11 @@ import openpyxl from openpyxl.workbook import Workbook -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel +from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel, \ + NewsPaperModel from logger import get_logger from readers.base import BaseReader - logger = get_logger(__name__) @@ -90,6 +90,85 @@ def attributes(self) -> dict: } +class AbstractReader(BaseReader): + """ + Чтение модели автореферата. + """ + + @property + def model(self) -> Type[AbstractModel]: + return AbstractModel + + @property + def sheet(self) -> str: + return "Автореферат" + + @property + def attributes(self) -> dict: + return { + "author": {0: str}, + "abstract_title": {1: str}, + "author_status": {2: str}, + "science_field": {3: str}, + "specialty_code": {4: str}, + "city": {5: str}, + "year": {6: int}, + "pages": {7: str}, + } + + +class RegulationReader(BaseReader): + """ + Чтение модели нормативного акта. + """ + + @property + def model(self) -> Type[RegulationModel]: + return RegulationModel + + @property + def sheet(self) -> str: + return " Закон, нормативный акт и т.п." + + @property + def attributes(self) -> dict: + return { + "source": {0: str}, + "regulation_title": {1: str}, + "acceptance_date": {2: date}, + "regulation_id": {3: str}, + "publishing_source": {4: str}, + "publishing_year": {5: int}, + "publishing_source_id": {6: str}, + "publishing_article_id": {7: str}, + "modification_date": {8: date}, + } + + +class NewsPaperReader(BaseReader): + """ + Чтение модели газеты. + """ + @property + def model(self) -> Type[NewsPaperModel]: + return NewsPaperModel + + @property + def sheet(self) -> str: + return "Статья из газеты" + + @property + def attributes(self) -> dict: + return { + "authors": {0: str}, + "article_title": {1: str}, + "news_title": {2: str}, + "publishing_year": {3: int}, + "publishing_date": {4: str}, + "publishing_number": {5: int} + } + + class SourcesReader: """ Чтение из источника данных. @@ -100,6 +179,9 @@ class SourcesReader: BookReader, InternetResourceReader, ArticlesCollectionReader, + AbstractReader, + RegulationReader, + NewsPaperReader ] def __init__(self, path: str) -> None: From cea2c31d17b50da41106e6c709c242aa5baa4962 Mon Sep 17 00:00:00 2001 From: Maria Akhaladze Date: Sat, 11 Feb 2023 22:23:16 +0400 Subject: [PATCH 2/6] Added gost and apa formatters --- src/formatters/styles/apa.py | 117 ++++++++++++++++++++++++++++++++++ src/formatters/styles/gost.py | 68 ++++++++++++++++++-- src/main.py | 27 +++++--- 3 files changed, 198 insertions(+), 14 deletions(-) create mode 100644 src/formatters/styles/apa.py diff --git a/src/formatters/styles/apa.py b/src/formatters/styles/apa.py new file mode 100644 index 0000000..7fead2d --- /dev/null +++ b/src/formatters/styles/apa.py @@ -0,0 +1,117 @@ +""" +Стиль цитирования по American Psychological Association +""" +from string import Template + +from pydantic import BaseModel + +from formatters.models import BookModel, InternetResourceModel, NewsPaperModel +from formatters.styles.base import BaseCitationStyle +from logger import get_logger + +logger = get_logger(__name__) + + +class APABook(BaseCitationStyle): + """ + Форматирование для книг. + """ + + data: BookModel + + @property + def template(self) -> Template: + return Template( + "$authors ($year). $title. $publishing_house." + ) + + def substitute(self) -> str: + logger.info('Форматирование книги "%s" ...', self.data.title) + + return self.template.substitute( + authors=self.data.authors, + title=self.data.title, + publishing_house=self.data.publishing_house, + year=self.data.year, + ) + + +class APAInternetResource(BaseCitationStyle): + """ + Форматирование для интернет-ресурсов. + """ + + data: InternetResourceModel + + @property + def template(self) -> Template: + return Template( + "$article ($access_date) $website $link" + ) + + def substitute(self) -> str: + logger.info('Форматирование интернет-ресурса "%s" ...', self.data.article) + + return self.template.substitute( + article=self.data.article, + website=self.data.website, + link=self.data.link, + access_date=self.data.access_date, + ) + + +class APANewsPaperResource(BaseCitationStyle): + """ + Форматирование для газеты. + """ + + data: NewsPaperModel + + @property + def template(self) -> Template: + return Template( + "$authors ($publishing_year, $publishing_date). $article_title. $news_title." + ) + + def substitute(self) -> str: + logger.info('Форматирование газеты "%s" ...', self.data.article_title) + + return self.template.substitute( + article_title=self.data.article_title, + authors=self.data.authors, + news_title=self.data.news_title, + publishing_year=self.data.publishing_year, + publishing_date=self.data.publishing_date + ) + + +class APACitationFormatter: + """ + Базовый класс для итогового форматирования списка источников. + """ + + formatters_map = { + BookModel.__name__: APABook, + InternetResourceModel.__name__: APAInternetResource, + NewsPaperModel.__name__: APANewsPaperResource, + } + + def __init__(self, models: list[BaseModel]) -> None: + """ + Конструктор. + :param models: Список объектов для форматирования + """ + + formatted_items = [] + for model in models: + formatted_items.append(self.formatters_map.get(type(model).__name__)(model)) # type: ignore + + self.formatted_items = formatted_items + + def format(self) -> list[BaseCitationStyle]: + """ + Форматирование списка источников. + :return: + """ + + return sorted(self.formatted_items, key=lambda item: item.formatted) diff --git a/src/formatters/styles/gost.py b/src/formatters/styles/gost.py index b237f8a..d02ec6b 100644 --- a/src/formatters/styles/gost.py +++ b/src/formatters/styles/gost.py @@ -5,11 +5,10 @@ from pydantic import BaseModel -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel +from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel from formatters.styles.base import BaseCitationStyle from logger import get_logger - logger = get_logger(__name__) @@ -27,7 +26,6 @@ def template(self) -> Template: ) def substitute(self) -> str: - logger.info('Форматирование книги "%s" ...', self.data.title) return self.template.substitute( @@ -64,7 +62,6 @@ def template(self) -> Template: ) def substitute(self) -> str: - logger.info('Форматирование интернет-ресурса "%s" ...', self.data.article) return self.template.substitute( @@ -89,7 +86,6 @@ def template(self) -> Template: ) def substitute(self) -> str: - logger.info('Форматирование сборника статей "%s" ...', self.data.article_title) return self.template.substitute( @@ -103,6 +99,66 @@ def substitute(self) -> str: ) +class GOSTAbtract(BaseCitationStyle): + """ + Форматирование для автореферата. + """ + + data: AbstractModel + + @property + def template(self) -> Template: + return Template( + "$author $abstract_title: автореф. дис. ... $author_status $science_field наук: $specialty_code. $city, " + "$year. $pages с." + ) + + def substitute(self) -> str: + logger.info('Форматирование автореферата "%s" ...', self.data.abstract_title) + + return self.template.substitute( + author=self.data.author, + abstract_title=self.data.abstract_title, + author_status=self.data.author_status, + science_field=self.data.science_field, + specialty_code=self.data.specialty_code, + city=self.data.city, + year=self.data.year, + pages=self.data.pages, + ) + + +class GOSTRegulation(BaseCitationStyle): + """ + Форматирование для нормативного акта. + """ + + data: RegulationModel + + @property + def template(self) -> Template: + return Template( + "$source \"$regulation_title\" от $acceptance_date № $regulation_id // $publishing_source. " + "$publishing_year г. № $publishing_source_id. Ст. $publishing_article_id с изм. и допол. в ред. от " + "$modification_date" + ) + + def substitute(self) -> str: + logger.info('Форматирование нормативного акта "%s" ...', self.data.regulation_title) + + return self.template.substitute( + regulation_title=self.data.regulation_title, + source=self.data.source, + publishing_source=self.data.publishing_source, + regulation_id=self.data.regulation_id, + acceptance_date=self.data.acceptance_date, + publishing_year=self.data.publishing_year, + publishing_source_id=self.data.publishing_source_id, + publishing_article_id=self.data.publishing_article_id, + modification_date=self.data.modification_date + ) + + class GOSTCitationFormatter: """ Базовый класс для итогового форматирования списка источников. @@ -112,6 +168,8 @@ class GOSTCitationFormatter: BookModel.__name__: GOSTBook, InternetResourceModel.__name__: GOSTInternetResource, ArticlesCollectionModel.__name__: GOSTCollectionArticle, + AbstractModel.__name__: GOSTAbtract, + RegulationModel.__name__: GOSTRegulation } def __init__(self, models: list[BaseModel]) -> None: diff --git a/src/main.py b/src/main.py index 7a9fa8e..75200d1 100644 --- a/src/main.py +++ b/src/main.py @@ -6,6 +6,7 @@ import click from formatters.styles.gost import GOSTCitationFormatter +from formatters.styles.apa import APACitationFormatter from logger import get_logger from readers.reader import SourcesReader from renderer import Renderer @@ -25,6 +26,19 @@ class CitationEnum(Enum): APA = "apa" # American Psychological Association +style_map = {CitationEnum.APA.name: APACitationFormatter, + CitationEnum.GOST.name: GOSTCitationFormatter} + + +def format_by_style(citation: str, models, path_output: str): + logger.info(style_map[citation]) + formatted_models = tuple( + str(item) for item in style_map[citation](models).format() + ) + logger.info(f"Генерация выходного файла в формате ${citation}...") + Renderer(formatted_models).render(path_output) + + @click.command() @click.option( "--citation", @@ -54,9 +68,9 @@ class CitationEnum(Enum): help="Путь к выходному файлу", ) def process_input( - citation: str = CitationEnum.GOST.name, - path_input: str = INPUT_FILE_PATH, - path_output: str = OUTPUT_FILE_PATH, + citation: str = CitationEnum.GOST.name, + path_input: str = INPUT_FILE_PATH, + path_output: str = OUTPUT_FILE_PATH, ) -> None: """ Генерация файла Word с оформленным библиографическим списком. @@ -77,12 +91,7 @@ def process_input( ) models = SourcesReader(path_input).read() - formatted_models = tuple( - str(item) for item in GOSTCitationFormatter(models).format() - ) - - logger.info("Генерация выходного файла ...") - Renderer(formatted_models).render(path_output) + format_by_style(citation, models, path_output) logger.info("Команда успешно завершена.") From ff163f56e6601965b49807cc320ba786cf7ad391 Mon Sep 17 00:00:00 2001 From: Maria Akhaladze Date: Sat, 11 Feb 2023 22:23:21 +0400 Subject: [PATCH 3/6] Added tests --- src/tests/conftest.py | 62 ++++++++++++++++++++- src/tests/formatters/test_apa.py | 90 ++++++++++++++++++++++++++++++ src/tests/formatters/test_gost.py | 78 ++++++++++++++++++++------ src/tests/readers/test_readers.py | 91 ++++++++++++++++++++++++++++++- 4 files changed, 301 insertions(+), 20 deletions(-) create mode 100644 src/tests/formatters/test_apa.py diff --git a/src/tests/conftest.py b/src/tests/conftest.py index ac5c9aa..303a858 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -3,7 +3,8 @@ """ import pytest -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel +from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, \ + RegulationModel, NewsPaperModel @pytest.fixture @@ -58,3 +59,62 @@ def articles_collection_model_fixture() -> ArticlesCollectionModel: year=2020, pages="25-30", ) + + +@pytest.fixture +def abstract_model_fixture() -> AbstractModel: + """ + Фикстура модели автореферата. + + :return: AbstractModel + """ + + return AbstractModel( + author="Иванов И.М.", + abstract_title="Наука как искусство", + author_status="д-р. / канд.", + science_field="экон.", + specialty_code="01.01.01", + city="Спб.", + year=2020, + pages="199" + ) + + +@pytest.fixture +def regulation_model_fixture() -> RegulationModel: + """ + Фикстура модели нормативного акта. + + :return: RegulationModel + """ + + return RegulationModel( + regulation_title="Наука как искусство", + source="Конституция Российской Федерации", + publishing_source="Парламентская газета", + regulation_id="1234-56", + acceptance_date="1/1/2000", + publishing_year="2020", + publishing_source_id="5", + publishing_article_id="15", + modification_date="9/11/2002" + ) + + +@pytest.fixture +def newspaper_model_fixture() -> NewsPaperModel: + """ + Фикстура модели газеты. + + :return: NewsPaperModel + """ + + return NewsPaperModel( + article_title="Наука как искусство", + authors="Иванов И.М.", + news_title="Южный Урал", + publishing_year="2020", + publishing_date="01.10", + publishing_number="5" + ) diff --git a/src/tests/formatters/test_apa.py b/src/tests/formatters/test_apa.py new file mode 100644 index 0000000..ede9f29 --- /dev/null +++ b/src/tests/formatters/test_apa.py @@ -0,0 +1,90 @@ +""" +Тестирование функций оформления списка источников по APA. +""" + +from formatters.base import BaseCitationFormatter +from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, NewsPaperModel +from formatters.styles.apa import APABook, APAInternetResource, APANewsPaperResource + + +class TestAPA: + """ + Тестирование оформления списка источников согласно APA + """ + + def test_book(self, book_model_fixture: BookModel) -> None: + """ + Тестирование форматирования книги. + + :param BookModel book_model_fixture: Фикстура модели книги + :return: + """ + + model = APABook(book_model_fixture) + + assert ( + model.formatted + == "Иванов И.М., Петров С.Н. (2020). Наука как искусство. Просвещение." + ) + + def test_internet_resource( + self, internet_resource_model_fixture: InternetResourceModel + ) -> None: + """ + Тестирование форматирования интернет-ресурса. + + :param InternetResourceModel internet_resource_model_fixture: Фикстура модели интернет-ресурса + :return: + """ + + model = APAInternetResource(internet_resource_model_fixture) + + assert ( + model.formatted + == "Наука как искусство (01.01.2021) Ведомости https://www.vedomosti.ru" + ) + + def test_news_paper( + self, newspaper_model_fixture: NewsPaperModel + ) -> None: + """ + Тестирование форматирования газеты. + + :param ArticlesCollectionModel newspaper_model_fixture: Фикстура модели газеты + :return: + """ + + model = APANewsPaperResource(newspaper_model_fixture) + + assert ( + model.formatted + == "Иванов И.М. (2020, 01.10). Наука как искусство. Южный Урал." + ) + + def test_citation_formatter( + self, + book_model_fixture: BookModel, + internet_resource_model_fixture: InternetResourceModel, + articles_collection_model_fixture: ArticlesCollectionModel, + newspaper_model_fixture: NewsPaperModel + ) -> None: + """ + Тестирование функции итогового форматирования списка источников. + + :param BookModel book_model_fixture: Фикстура модели книги + :param InternetResourceModel internet_resource_model_fixture: Фикстура модели интернет-ресурса + :param ArticlesCollectionModel newspaper_model_fixture: Фикстура модели газеты + :return: + """ + + models = [ + APABook(book_model_fixture), + APAInternetResource(internet_resource_model_fixture), + APANewsPaperResource(newspaper_model_fixture) + ] + result = BaseCitationFormatter(models).format() + + # тестирование сортировки списка источников + assert result[0] == models[2] + assert result[1] == models[0] + assert result[2] == models[1] diff --git a/src/tests/formatters/test_gost.py b/src/tests/formatters/test_gost.py index c93e1e7..604d243 100644 --- a/src/tests/formatters/test_gost.py +++ b/src/tests/formatters/test_gost.py @@ -3,8 +3,8 @@ """ from formatters.base import BaseCitationFormatter -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel -from formatters.styles.gost import GOSTBook, GOSTInternetResource, GOSTCollectionArticle +from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel +from formatters.styles.gost import GOSTBook, GOSTInternetResource, GOSTCollectionArticle, GOSTAbtract, GOSTRegulation class TestGOST: @@ -23,12 +23,12 @@ def test_book(self, book_model_fixture: BookModel) -> None: model = GOSTBook(book_model_fixture) assert ( - model.formatted - == "Иванов И.М., Петров С.Н. Наука как искусство. – 3-е изд. – СПб.: Просвещение, 2020. – 999 с." + model.formatted + == "Иванов И.М., Петров С.Н. Наука как искусство. – 3-е изд. – СПб.: Просвещение, 2020. – 999 с." ) def test_internet_resource( - self, internet_resource_model_fixture: InternetResourceModel + self, internet_resource_model_fixture: InternetResourceModel ) -> None: """ Тестирование форматирования интернет-ресурса. @@ -40,12 +40,12 @@ def test_internet_resource( model = GOSTInternetResource(internet_resource_model_fixture) assert ( - model.formatted - == "Наука как искусство // Ведомости URL: https://www.vedomosti.ru (дата обращения: 01.01.2021)." + model.formatted + == "Наука как искусство // Ведомости URL: https://www.vedomosti.ru (дата обращения: 01.01.2021)." ) def test_articles_collection( - self, articles_collection_model_fixture: ArticlesCollectionModel + self, articles_collection_model_fixture: ArticlesCollectionModel ) -> None: """ Тестирование форматирования сборника статей. @@ -57,15 +57,53 @@ def test_articles_collection( model = GOSTCollectionArticle(articles_collection_model_fixture) assert ( - model.formatted - == "Иванов И.М., Петров С.Н. Наука как искусство // Сборник научных трудов. – СПб.: АСТ, 2020. – С. 25-30." + model.formatted + == "Иванов И.М., Петров С.Н. Наука как искусство // Сборник научных трудов. – СПб.: АСТ, 2020. – С. 25-30." + ) + + def test_abstract( + self, abstract_model_fixture: AbstractModel + ) -> None: + """ + Тестирование форматирования автореферата. + + :param AbstractModel abstract_model_fixture: Фикстура модели автореферата + :return: + """ + + model = GOSTAbtract(abstract_model_fixture) + + assert ( + model.formatted + == "Иванов И.М. Наука как искусство: автореф. дис. ... д-р. / канд. экон. наук: 01.01.01. Спб., " + "2020. 199 с." + ) + + def test_regulation( + self, regulation_model_fixture: RegulationModel + ) -> None: + """ + Тестирование форматирования нормативного акта. + + :param RegulationModel regulation_model_fixture: Фикстура модели нормативного акта + :return: + """ + + model = GOSTRegulation(regulation_model_fixture) + + assert ( + model.formatted + == "Конституция Российской Федерации \"Наука как искусство\" от 1/1/2000 № 1234-56 // Парламентская " + "газета. 2020 г. № 5. Ст. 15 с изм. и допол. в ред. от 9/11/2002" ) def test_citation_formatter( - self, - book_model_fixture: BookModel, - internet_resource_model_fixture: InternetResourceModel, - articles_collection_model_fixture: ArticlesCollectionModel, + self, + book_model_fixture: BookModel, + internet_resource_model_fixture: InternetResourceModel, + articles_collection_model_fixture: ArticlesCollectionModel, + abstract_model_fixture: AbstractModel, + regulation_model_fixture: RegulationModel ) -> None: """ Тестирование функции итогового форматирования списка источников. @@ -73,6 +111,8 @@ def test_citation_formatter( :param BookModel book_model_fixture: Фикстура модели книги :param InternetResourceModel internet_resource_model_fixture: Фикстура модели интернет-ресурса :param ArticlesCollectionModel articles_collection_model_fixture: Фикстура модели сборника статей + :param AbstractModel abstract_model_fixture: Фикстура модели автореферата + :param RegulationModel regulation_model_fixture: Фикстура модели нормативного акта :return: """ @@ -80,10 +120,14 @@ def test_citation_formatter( GOSTBook(book_model_fixture), GOSTInternetResource(internet_resource_model_fixture), GOSTCollectionArticle(articles_collection_model_fixture), + GOSTAbtract(abstract_model_fixture), + GOSTRegulation(regulation_model_fixture) ] result = BaseCitationFormatter(models).format() # тестирование сортировки списка источников - assert result[0] == models[2] - assert result[1] == models[0] - assert result[2] == models[1] + assert result[0] == models[3] + assert result[1] == models[2] + assert result[2] == models[0] + assert result[3] == models[4] + assert result[4] == models[1] diff --git a/src/tests/readers/test_readers.py b/src/tests/readers/test_readers.py index 67d863b..3451e05 100644 --- a/src/tests/readers/test_readers.py +++ b/src/tests/readers/test_readers.py @@ -5,12 +5,16 @@ import pytest -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel +from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel, \ + NewsPaperModel from readers.reader import ( BookReader, SourcesReader, InternetResourceReader, ArticlesCollectionReader, + AbstractReader, + RegulationReader, + NewsPaperReader ) from settings import TEMPLATE_FILE_PATH @@ -104,6 +108,86 @@ def test_articles_collection(self, workbook: Any) -> None: # проверка общего количества атрибутов assert len(model_type.schema().get("properties", {}).keys()) == 7 + def test_abstract(self, workbook: Any) -> None: + """ + Тестирование чтения автореферата. + + :param workbook: Объект тестовой рабочей книги. + """ + + models = AbstractReader(workbook).read() + + assert len(models) == 1 + model = models[0] + + model_type = AbstractModel + + assert isinstance(model, model_type) + assert model.author == "Иванов И.М." + assert model.abstract_title == "Наука как искусство" + assert model.author_status == "д-р. / канд." + assert model.science_field == "экон." + assert model.specialty_code == "01.01.01" + assert model.city == "СПб." + assert model.year == 2020 + assert model.pages == "199" + + # проверка общего количества атрибутов + assert len(model_type.schema().get("properties", {}).keys()) == 8 + + def test_regulation(self, workbook: Any) -> None: + """ + Тестирование чтения нормативного акта. + + :param workbook: Объект тестовой рабочей книги. + """ + + models = RegulationReader(workbook).read() + + assert len(models) == 1 + model = models[0] + + model_type = RegulationModel + + assert isinstance(model, model_type) + assert model.regulation_title == "Наука как искусство" + assert model.source == "Конституция Российской Федерации" + assert model.publishing_source == "Парламентская газета" + assert model.regulation_id == "1234-56" + assert model.acceptance_date == "01.01.2000" + assert model.publishing_year == 2020 + assert model.publishing_source_id == "5" + assert model.publishing_article_id == "15" + assert model.modification_date == "11.09.2002" + + # проверка общего количества атрибутов + assert len(model_type.schema().get("properties", {}).keys()) == 9 + + def test_newspaper(self, workbook: Any) -> None: + """ + Тестирование чтения газеты. + + :param workbook: Объект тестовой рабочей книги. + """ + + models = NewsPaperReader(workbook).read() + + assert len(models) == 1 + model = models[0] + + model_type = NewsPaperModel + + assert isinstance(model, model_type) + assert model.article_title == "Наука как искусство" + assert model.authors == "Иванов И.М., Петров С.Н." + assert model.news_title == "Южный Урал" + assert model.publishing_year == 1980 + assert model.publishing_date == "01.10" + assert model.publishing_number == 5 + + # проверка общего количества атрибутов + assert len(model_type.schema().get("properties", {}).keys()) == 6 + def test_sources_reader(self) -> None: """ Тестирование функции чтения всех моделей из источника. @@ -111,7 +195,7 @@ def test_sources_reader(self) -> None: models = SourcesReader(TEMPLATE_FILE_PATH).read() # проверка общего считанного количества моделей - assert len(models) == 8 + assert len(models) == 11 # проверка наличия всех ожидаемых типов моделей среди типов считанных моделей model_types = {model.__class__.__name__ for model in models} @@ -119,4 +203,7 @@ def test_sources_reader(self) -> None: BookModel.__name__, InternetResourceModel.__name__, ArticlesCollectionModel.__name__, + AbstractModel.__name__, + RegulationModel.__name__, + NewsPaperModel.__name__ } From cadc1a960f0b8daf03df581d360a1943c3d50b79 Mon Sep 17 00:00:00 2001 From: Maria Akhaladze Date: Sat, 11 Feb 2023 22:25:44 +0400 Subject: [PATCH 4/6] Updated README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 23fbc4c..bea184d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ The application allows you to automate the process of generating a bibliography Supported citation styles: - ГОСТ Р 7.0.5-2008 +- American Psychological Association ## Installation From 9d1eb8bfa161a06c26ba53fbb228a6bbcfc97aca Mon Sep 17 00:00:00 2001 From: Maria Akhaladze Date: Sat, 11 Feb 2023 22:44:51 +0400 Subject: [PATCH 5/6] Added check for formatter existence --- src/formatters/styles/apa.py | 4 +++- src/formatters/styles/gost.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/formatters/styles/apa.py b/src/formatters/styles/apa.py index 7fead2d..ec8f79e 100644 --- a/src/formatters/styles/apa.py +++ b/src/formatters/styles/apa.py @@ -104,7 +104,9 @@ def __init__(self, models: list[BaseModel]) -> None: formatted_items = [] for model in models: - formatted_items.append(self.formatters_map.get(type(model).__name__)(model)) # type: ignore + formatter = self.formatters_map.get(type(model).__name__) + if formatter is not None: + formatted_items.append(formatter(model)) # type: ignore self.formatted_items = formatted_items diff --git a/src/formatters/styles/gost.py b/src/formatters/styles/gost.py index d02ec6b..185995a 100644 --- a/src/formatters/styles/gost.py +++ b/src/formatters/styles/gost.py @@ -181,7 +181,9 @@ def __init__(self, models: list[BaseModel]) -> None: formatted_items = [] for model in models: - formatted_items.append(self.formatters_map.get(type(model).__name__)(model)) # type: ignore + formatter = self.formatters_map.get(type(model).__name__) + if formatter is not None: + formatted_items.append(formatter(model)) # type: ignore self.formatted_items = formatted_items From 689444f847f963337f655beaec45b492ebb5d8af Mon Sep 17 00:00:00 2001 From: Maria Akhaladze Date: Sat, 11 Feb 2023 23:06:23 +0400 Subject: [PATCH 6/6] Fixed codestyle --- src/formatters/styles/apa.py | 10 ++--- src/formatters/styles/gost.py | 18 ++++++--- src/main.py | 20 +++++----- src/readers/reader.py | 15 +++++-- src/tests/conftest.py | 16 +++++--- src/tests/formatters/test_apa.py | 35 ++++++++-------- src/tests/formatters/test_gost.py | 66 +++++++++++++++++-------------- src/tests/readers/test_readers.py | 14 +++++-- 8 files changed, 113 insertions(+), 81 deletions(-) mode change 100644 => 100755 src/main.py diff --git a/src/formatters/styles/apa.py b/src/formatters/styles/apa.py index ec8f79e..a6aa604 100644 --- a/src/formatters/styles/apa.py +++ b/src/formatters/styles/apa.py @@ -21,9 +21,7 @@ class APABook(BaseCitationStyle): @property def template(self) -> Template: - return Template( - "$authors ($year). $title. $publishing_house." - ) + return Template("$authors ($year). $title. $publishing_house.") def substitute(self) -> str: logger.info('Форматирование книги "%s" ...', self.data.title) @@ -45,9 +43,7 @@ class APAInternetResource(BaseCitationStyle): @property def template(self) -> Template: - return Template( - "$article ($access_date) $website $link" - ) + return Template("$article ($access_date) $website $link") def substitute(self) -> str: logger.info('Форматирование интернет-ресурса "%s" ...', self.data.article) @@ -81,7 +77,7 @@ def substitute(self) -> str: authors=self.data.authors, news_title=self.data.news_title, publishing_year=self.data.publishing_year, - publishing_date=self.data.publishing_date + publishing_date=self.data.publishing_date, ) diff --git a/src/formatters/styles/gost.py b/src/formatters/styles/gost.py index 185995a..b21b8e5 100644 --- a/src/formatters/styles/gost.py +++ b/src/formatters/styles/gost.py @@ -5,7 +5,13 @@ from pydantic import BaseModel -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel +from formatters.models import ( + BookModel, + InternetResourceModel, + ArticlesCollectionModel, + AbstractModel, + RegulationModel, +) from formatters.styles.base import BaseCitationStyle from logger import get_logger @@ -138,13 +144,15 @@ class GOSTRegulation(BaseCitationStyle): @property def template(self) -> Template: return Template( - "$source \"$regulation_title\" от $acceptance_date № $regulation_id // $publishing_source. " + '$source "$regulation_title" от $acceptance_date № $regulation_id // $publishing_source. ' "$publishing_year г. № $publishing_source_id. Ст. $publishing_article_id с изм. и допол. в ред. от " "$modification_date" ) def substitute(self) -> str: - logger.info('Форматирование нормативного акта "%s" ...', self.data.regulation_title) + logger.info( + 'Форматирование нормативного акта "%s" ...', self.data.regulation_title + ) return self.template.substitute( regulation_title=self.data.regulation_title, @@ -155,7 +163,7 @@ def substitute(self) -> str: publishing_year=self.data.publishing_year, publishing_source_id=self.data.publishing_source_id, publishing_article_id=self.data.publishing_article_id, - modification_date=self.data.modification_date + modification_date=self.data.modification_date, ) @@ -169,7 +177,7 @@ class GOSTCitationFormatter: InternetResourceModel.__name__: GOSTInternetResource, ArticlesCollectionModel.__name__: GOSTCollectionArticle, AbstractModel.__name__: GOSTAbtract, - RegulationModel.__name__: GOSTRegulation + RegulationModel.__name__: GOSTRegulation, } def __init__(self, models: list[BaseModel]) -> None: diff --git a/src/main.py b/src/main.py old mode 100644 new mode 100755 index 75200d1..f1242bb --- a/src/main.py +++ b/src/main.py @@ -5,8 +5,8 @@ import click -from formatters.styles.gost import GOSTCitationFormatter from formatters.styles.apa import APACitationFormatter +from formatters.styles.gost import GOSTCitationFormatter from logger import get_logger from readers.reader import SourcesReader from renderer import Renderer @@ -26,15 +26,15 @@ class CitationEnum(Enum): APA = "apa" # American Psychological Association -style_map = {CitationEnum.APA.name: APACitationFormatter, - CitationEnum.GOST.name: GOSTCitationFormatter} +style_map = { + CitationEnum.APA.name: APACitationFormatter, + CitationEnum.GOST.name: GOSTCitationFormatter, +} -def format_by_style(citation: str, models, path_output: str): +def format_by_style(citation: str, models: list, path_output: str) -> None: logger.info(style_map[citation]) - formatted_models = tuple( - str(item) for item in style_map[citation](models).format() - ) + formatted_models = tuple(str(item) for item in style_map[citation](models).format()) logger.info(f"Генерация выходного файла в формате ${citation}...") Renderer(formatted_models).render(path_output) @@ -68,9 +68,9 @@ def format_by_style(citation: str, models, path_output: str): help="Путь к выходному файлу", ) def process_input( - citation: str = CitationEnum.GOST.name, - path_input: str = INPUT_FILE_PATH, - path_output: str = OUTPUT_FILE_PATH, + citation: str = CitationEnum.GOST.name, + path_input: str = INPUT_FILE_PATH, + path_output: str = OUTPUT_FILE_PATH, ) -> None: """ Генерация файла Word с оформленным библиографическим списком. diff --git a/src/readers/reader.py b/src/readers/reader.py index 6098b3e..820a514 100644 --- a/src/readers/reader.py +++ b/src/readers/reader.py @@ -7,8 +7,14 @@ import openpyxl from openpyxl.workbook import Workbook -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel, \ - NewsPaperModel +from formatters.models import ( + BookModel, + InternetResourceModel, + ArticlesCollectionModel, + AbstractModel, + RegulationModel, + NewsPaperModel, +) from logger import get_logger from readers.base import BaseReader @@ -149,6 +155,7 @@ class NewsPaperReader(BaseReader): """ Чтение модели газеты. """ + @property def model(self) -> Type[NewsPaperModel]: return NewsPaperModel @@ -165,7 +172,7 @@ def attributes(self) -> dict: "news_title": {2: str}, "publishing_year": {3: int}, "publishing_date": {4: str}, - "publishing_number": {5: int} + "publishing_number": {5: int}, } @@ -181,7 +188,7 @@ class SourcesReader: ArticlesCollectionReader, AbstractReader, RegulationReader, - NewsPaperReader + NewsPaperReader, ] def __init__(self, path: str) -> None: diff --git a/src/tests/conftest.py b/src/tests/conftest.py index 303a858..7335b83 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -3,8 +3,14 @@ """ import pytest -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, \ - RegulationModel, NewsPaperModel +from formatters.models import ( + BookModel, + InternetResourceModel, + ArticlesCollectionModel, + AbstractModel, + RegulationModel, + NewsPaperModel, +) @pytest.fixture @@ -77,7 +83,7 @@ def abstract_model_fixture() -> AbstractModel: specialty_code="01.01.01", city="Спб.", year=2020, - pages="199" + pages="199", ) @@ -98,7 +104,7 @@ def regulation_model_fixture() -> RegulationModel: publishing_year="2020", publishing_source_id="5", publishing_article_id="15", - modification_date="9/11/2002" + modification_date="9/11/2002", ) @@ -116,5 +122,5 @@ def newspaper_model_fixture() -> NewsPaperModel: news_title="Южный Урал", publishing_year="2020", publishing_date="01.10", - publishing_number="5" + publishing_number="5", ) diff --git a/src/tests/formatters/test_apa.py b/src/tests/formatters/test_apa.py index ede9f29..e00516b 100644 --- a/src/tests/formatters/test_apa.py +++ b/src/tests/formatters/test_apa.py @@ -3,7 +3,11 @@ """ from formatters.base import BaseCitationFormatter -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, NewsPaperModel +from formatters.models import ( + BookModel, + InternetResourceModel, + NewsPaperModel, +) from formatters.styles.apa import APABook, APAInternetResource, APANewsPaperResource @@ -23,12 +27,12 @@ def test_book(self, book_model_fixture: BookModel) -> None: model = APABook(book_model_fixture) assert ( - model.formatted - == "Иванов И.М., Петров С.Н. (2020). Наука как искусство. Просвещение." + model.formatted + == "Иванов И.М., Петров С.Н. (2020). Наука как искусство. Просвещение." ) def test_internet_resource( - self, internet_resource_model_fixture: InternetResourceModel + self, internet_resource_model_fixture: InternetResourceModel ) -> None: """ Тестирование форматирования интернет-ресурса. @@ -40,13 +44,11 @@ def test_internet_resource( model = APAInternetResource(internet_resource_model_fixture) assert ( - model.formatted - == "Наука как искусство (01.01.2021) Ведомости https://www.vedomosti.ru" + model.formatted + == "Наука как искусство (01.01.2021) Ведомости https://www.vedomosti.ru" ) - def test_news_paper( - self, newspaper_model_fixture: NewsPaperModel - ) -> None: + def test_news_paper(self, newspaper_model_fixture: NewsPaperModel) -> None: """ Тестирование форматирования газеты. @@ -57,16 +59,15 @@ def test_news_paper( model = APANewsPaperResource(newspaper_model_fixture) assert ( - model.formatted - == "Иванов И.М. (2020, 01.10). Наука как искусство. Южный Урал." + model.formatted + == "Иванов И.М. (2020, 01.10). Наука как искусство. Южный Урал." ) def test_citation_formatter( - self, - book_model_fixture: BookModel, - internet_resource_model_fixture: InternetResourceModel, - articles_collection_model_fixture: ArticlesCollectionModel, - newspaper_model_fixture: NewsPaperModel + self, + book_model_fixture: BookModel, + internet_resource_model_fixture: InternetResourceModel, + newspaper_model_fixture: NewsPaperModel, ) -> None: """ Тестирование функции итогового форматирования списка источников. @@ -80,7 +81,7 @@ def test_citation_formatter( models = [ APABook(book_model_fixture), APAInternetResource(internet_resource_model_fixture), - APANewsPaperResource(newspaper_model_fixture) + APANewsPaperResource(newspaper_model_fixture), ] result = BaseCitationFormatter(models).format() diff --git a/src/tests/formatters/test_gost.py b/src/tests/formatters/test_gost.py index 604d243..befda4a 100644 --- a/src/tests/formatters/test_gost.py +++ b/src/tests/formatters/test_gost.py @@ -3,8 +3,20 @@ """ from formatters.base import BaseCitationFormatter -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel -from formatters.styles.gost import GOSTBook, GOSTInternetResource, GOSTCollectionArticle, GOSTAbtract, GOSTRegulation +from formatters.models import ( + BookModel, + InternetResourceModel, + ArticlesCollectionModel, + AbstractModel, + RegulationModel, +) +from formatters.styles.gost import ( + GOSTBook, + GOSTInternetResource, + GOSTCollectionArticle, + GOSTAbtract, + GOSTRegulation, +) class TestGOST: @@ -23,12 +35,12 @@ def test_book(self, book_model_fixture: BookModel) -> None: model = GOSTBook(book_model_fixture) assert ( - model.formatted - == "Иванов И.М., Петров С.Н. Наука как искусство. – 3-е изд. – СПб.: Просвещение, 2020. – 999 с." + model.formatted + == "Иванов И.М., Петров С.Н. Наука как искусство. – 3-е изд. – СПб.: Просвещение, 2020. – 999 с." ) def test_internet_resource( - self, internet_resource_model_fixture: InternetResourceModel + self, internet_resource_model_fixture: InternetResourceModel ) -> None: """ Тестирование форматирования интернет-ресурса. @@ -40,12 +52,12 @@ def test_internet_resource( model = GOSTInternetResource(internet_resource_model_fixture) assert ( - model.formatted - == "Наука как искусство // Ведомости URL: https://www.vedomosti.ru (дата обращения: 01.01.2021)." + model.formatted + == "Наука как искусство // Ведомости URL: https://www.vedomosti.ru (дата обращения: 01.01.2021)." ) def test_articles_collection( - self, articles_collection_model_fixture: ArticlesCollectionModel + self, articles_collection_model_fixture: ArticlesCollectionModel ) -> None: """ Тестирование форматирования сборника статей. @@ -57,13 +69,11 @@ def test_articles_collection( model = GOSTCollectionArticle(articles_collection_model_fixture) assert ( - model.formatted - == "Иванов И.М., Петров С.Н. Наука как искусство // Сборник научных трудов. – СПб.: АСТ, 2020. – С. 25-30." + model.formatted + == "Иванов И.М., Петров С.Н. Наука как искусство // Сборник научных трудов. – СПб.: АСТ, 2020. – С. 25-30." ) - def test_abstract( - self, abstract_model_fixture: AbstractModel - ) -> None: + def test_abstract(self, abstract_model_fixture: AbstractModel) -> None: """ Тестирование форматирования автореферата. @@ -74,14 +84,12 @@ def test_abstract( model = GOSTAbtract(abstract_model_fixture) assert ( - model.formatted - == "Иванов И.М. Наука как искусство: автореф. дис. ... д-р. / канд. экон. наук: 01.01.01. Спб., " - "2020. 199 с." + model.formatted + == "Иванов И.М. Наука как искусство: автореф. дис. ... д-р. / канд. экон. наук: 01.01.01. Спб., " + "2020. 199 с." ) - def test_regulation( - self, regulation_model_fixture: RegulationModel - ) -> None: + def test_regulation(self, regulation_model_fixture: RegulationModel) -> None: """ Тестирование форматирования нормативного акта. @@ -92,18 +100,18 @@ def test_regulation( model = GOSTRegulation(regulation_model_fixture) assert ( - model.formatted - == "Конституция Российской Федерации \"Наука как искусство\" от 1/1/2000 № 1234-56 // Парламентская " - "газета. 2020 г. № 5. Ст. 15 с изм. и допол. в ред. от 9/11/2002" + model.formatted + == 'Конституция Российской Федерации "Наука как искусство" от 1/1/2000 № 1234-56 // Парламентская ' + "газета. 2020 г. № 5. Ст. 15 с изм. и допол. в ред. от 9/11/2002" ) def test_citation_formatter( - self, - book_model_fixture: BookModel, - internet_resource_model_fixture: InternetResourceModel, - articles_collection_model_fixture: ArticlesCollectionModel, - abstract_model_fixture: AbstractModel, - regulation_model_fixture: RegulationModel + self, + book_model_fixture: BookModel, + internet_resource_model_fixture: InternetResourceModel, + articles_collection_model_fixture: ArticlesCollectionModel, + abstract_model_fixture: AbstractModel, + regulation_model_fixture: RegulationModel, ) -> None: """ Тестирование функции итогового форматирования списка источников. @@ -121,7 +129,7 @@ def test_citation_formatter( GOSTInternetResource(internet_resource_model_fixture), GOSTCollectionArticle(articles_collection_model_fixture), GOSTAbtract(abstract_model_fixture), - GOSTRegulation(regulation_model_fixture) + GOSTRegulation(regulation_model_fixture), ] result = BaseCitationFormatter(models).format() diff --git a/src/tests/readers/test_readers.py b/src/tests/readers/test_readers.py index 3451e05..de99c35 100644 --- a/src/tests/readers/test_readers.py +++ b/src/tests/readers/test_readers.py @@ -5,8 +5,14 @@ import pytest -from formatters.models import BookModel, InternetResourceModel, ArticlesCollectionModel, AbstractModel, RegulationModel, \ - NewsPaperModel +from formatters.models import ( + BookModel, + InternetResourceModel, + ArticlesCollectionModel, + AbstractModel, + RegulationModel, + NewsPaperModel, +) from readers.reader import ( BookReader, SourcesReader, @@ -14,7 +20,7 @@ ArticlesCollectionReader, AbstractReader, RegulationReader, - NewsPaperReader + NewsPaperReader, ) from settings import TEMPLATE_FILE_PATH @@ -205,5 +211,5 @@ def test_sources_reader(self) -> None: ArticlesCollectionModel.__name__, AbstractModel.__name__, RegulationModel.__name__, - NewsPaperModel.__name__ + NewsPaperModel.__name__, }