Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@ dmypy.json

# IDE
.idea
/.vs
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 7

## Installation

Expand Down Expand Up @@ -57,6 +58,8 @@ to fill it without changing the original template:
docker compose run app python main.py --citation gost --path_input /media/input.xlsx --path_output /media/output.docx
```

The command is given for GOST formatting, to change the citation style you need to change the citation parameter ('gost' or 'apa' for GOST or APA7 style citation respectively).

Also, it is possible to omit the arguments to use their defaults:
```shell
docker compose run app python main.py
Expand Down
3 changes: 2 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

Поддерживаемые стили цитирования:
- ГОСТ Р 7.0.5-2008
- American Psychological Association 7

Установка
=========
Expand Down Expand Up @@ -95,7 +96,7 @@

docker compose run app python main.py --citation gost --path_input /media/input.xlsx --path_output /media/output.docx

Таким образом можно определять стиль цитирования и пути ко входному и выходному файлам.
Таким образом можно определять стиль цитирования (`gost` - для ГОСТ Р 7.0.5-2008 и `apa` - для APA 7) и пути ко входному и выходному файлам.

Автоматизация
=============
Expand Down
56 changes: 56 additions & 0 deletions src/formatters/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,59 @@ class ArticlesCollectionModel(BaseModel):
publishing_house: str
year: int = Field(..., gt=0)
pages: str


class DissertationModel(BaseModel):
"""
Модель дисссертации:

.. code-block::

DissertationModel(
author="Иванов И.М.",
title="Наука как искусство",
degree="д-р. / канд.",
branch="экон.",
speciality_code="01.01.01",
city="СПб.",
year=2020,
pages=199,
)
"""

author: str = Field(..., description="Фамилия и инициалы автора")
title: str = Field(..., description="Название диссертации")
degree: str = Field(..., description="Ученая степень (д-р. / канд.)")
branch: str = Field(..., description="Отрасль наук (сокращённо)")
speciality_code: str = Field(..., description="Код специальности")
city: str = Field(..., description="Город издательства")
year: int = Field(..., gt=0, description="Год защиты")
pages: int = Field(..., gt=0, description="Количество страниц")


class AbstractModel(BaseModel):
"""
Модель автореферата:

.. code-block::

AbstractModel(
author="Иванов И.М.",
title="Наука как искусство",
degree="д-р. / канд.",
branch="экон.",
speciality_code="01.01.01",
city="СПб.",
year=2020,
pages=199,
)
"""

author: str = Field(..., description="Фамилия и инициалы автора")
title: str = Field(..., description="Название диссертации")
degree: str = Field(..., description="Ученая степень (д-р. / канд.)")
branch: str = Field(..., description="Отрасль наук (сокращённо)")
speciality_code: str = Field(..., description="Код специальности")
city: str = Field(..., description="Город издательства")
year: int = Field(..., gt=0, description="Год издания")
pages: int = Field(..., gt=0, description="Количество страниц")
283 changes: 283 additions & 0 deletions src/formatters/styles/apa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
""" Стиль цитирования по APA. """
from string import Template

from pydantic import BaseModel

from formatters.models import (
BookModel,
InternetResourceModel,
ArticlesCollectionModel,
DissertationModel,
AbstractModel,
)
from formatters.styles.base import BaseCitationStyle
from logger import get_logger


logger = get_logger(__name__)


class APABookFormatter(BaseCitationStyle):
"""
Форматирование для книг в стиле APA.
"""

data: BookModel

@property
def template(self) -> Template:
return Template("$authors ($year). $title. $edition. $city: $publishing_house.")

def substitute(self) -> str:
"""
Замена переменных в шаблоне источника.

:return: Отформатированная строка источника.
"""

logger.info('Форматирование книги "%s" в стиле APA...', self.data.title)

return self.template.substitute(
authors=self.get_authors(),
year=self.data.year,
title=self.data.title,
edition=self.get_edition(),
city=self.data.city,
publishing_house=self.data.publishing_house,
)

def get_authors(self) -> str:
"""
Получение списка авторов в формате APA.

:return: Отформатированный список авторов.
"""
authors = self.data.authors.split(
", "
) # Предполагая, что имена авторов разделены запятой
formatted_authors = []
for author in authors:
# Разбиваем полное имя автора на фамилию и инициалы
author_parts = author.split(" ")
# Если есть хотя бы фамилия и одна инициала
if len(author_parts) >= 2:
# Форматируем фамилию, инициалы и точку
formatted_author = (
author_parts[0]
+ " "
+ " ".join([initial[0] + "." for initial in author_parts[1:]])
)
formatted_authors.append(formatted_author)
else:
# В случае, если в имени только одно слово (без инициалов)
formatted_authors.append(author)
# Объединяем отформатированных авторов через запятую и пробел
if len(formatted_authors) == 1:
return formatted_authors[0]
if len(formatted_authors) == 2:
return formatted_authors[0] + " & " + formatted_authors[1]
else:
# Если авторов больше двух, добавляем "и др."
return formatted_authors[0] + " et al."

def get_edition(self) -> str:
"""
Получение отформатированной информации об издании.

:return: Информация об издании.
"""
return f"({self.data.edition} изд.)" if self.data.edition else ""


class APAInternetResourceFormatter(BaseCitationStyle):
"""
Форматирование для интернет-ресурсов в стиле APA.
"""

data: InternetResourceModel

@property
def template(self) -> Template:
return Template(
"$article (б. д.). $website URL: $link (дата обращения: $access_date)."
)

def substitute(self) -> str:
"""
Замена переменных в шаблоне источника.

:return: Отформатированная строка источника.
"""

logger.info(
'Форматирование интернет-ресурса "%s" в стиле APA...', 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 APACollectionArticleFormatter(BaseCitationStyle):
"""
Форматирование для статьи из сборника в стиле APA.
"""

data: ArticlesCollectionModel

@property
def template(self) -> Template:
return Template(
"$authors ($year). $article_title. $collection_title (pp. $pages). $city: $publishing_house."
)

def substitute(self) -> str:
"""
Замена переменных в шаблоне источника.

:return: Отформатированная строка источника.
"""

logger.info(
'Форматирование статьи из сборника "%s" в стиле APA...',
self.data.article_title,
)

return self.template.substitute(
authors=self.get_authors(),
year=self.data.year,
article_title=self.data.article_title,
collection_title=self.data.collection_title,
pages=self.data.pages,
city=self.data.city,
publishing_house=self.data.publishing_house,
)

def get_authors(self) -> str:
"""
Получение списка авторов в формате APA.

:return: Отформатированный список авторов.
"""
authors = self.data.authors.split(
", "
) # Предполагая, что имена авторов разделены запятой
formatted_authors = []
for author in authors:
# Разбиваем полное имя автора на фамилию и инициалы
author_parts = author.split(" ")
# Если есть хотя бы фамилия и одна инициала
if len(author_parts) >= 2:
# Форматируем фамилию, инициалы и точку
formatted_author = (
author_parts[0]
+ " "
+ " ".join([initial[0] + "." for initial in author_parts[1:]])
)
formatted_authors.append(formatted_author)
else:
# В случае, если в имени только одно слово (без инициалов)
formatted_authors.append(author)
# Объединяем отформатированных авторов через запятую и пробел
if len(formatted_authors) == 1:
return formatted_authors[0]
if len(formatted_authors) == 2:
return formatted_authors[0] + " & " + formatted_authors[1]
else:
# Если авторов больше двух, добавляем "и др."
return formatted_authors[0] + " et al."


class APADissertationFormatter(BaseCitationStyle):
"""
Форматирование для диссертаций в стиле APA.
"""

data: DissertationModel

@property
def template(self) -> Template:
return Template(
"$author. ($year). $title ($degree) [Диссертация]. $branch, $speciality_code. $city"
)

def substitute(self) -> str:

logger.info('Форматирование диссертации "%s" ...', self.data.title)

return self.template.substitute(
author=self.data.author,
year=self.data.year,
title=self.data.title,
degree=self.data.degree,
branch=self.data.branch,
speciality_code=self.data.speciality_code,
city=self.data.city,
)


class APAAbstractFormatter(BaseCitationStyle):
"""
Форматирование для автореферата в стиле APA.
"""

data: AbstractModel

@property
def template(self) -> Template:
return Template(
"$author. ($year). $title ($degree) [Автореферат]. $branch, $speciality_code. $city"
)

def substitute(self) -> str:

logger.info('Форматирование автореферата "%s" ...', self.data.title)

return self.template.substitute(
author=self.data.author,
year=self.data.year,
title=self.data.title,
degree=self.data.degree,
branch=self.data.branch,
speciality_code=self.data.speciality_code,
city=self.data.city,
)


class APACitationFormatter:
"""
Базовый класс для итогового форматирования списка источников.
"""

formatters_map = {
BookModel.__name__: APABookFormatter,
InternetResourceModel.__name__: APAInternetResourceFormatter,
ArticlesCollectionModel.__name__: APACollectionArticleFormatter,
DissertationModel.__name__: APADissertationFormatter,
AbstractModel.__name__: APAAbstractFormatter,
}

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)
Loading