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 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 (APA)

## Installation

Expand Down
1 change: 1 addition & 0 deletions 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 (АРА)

Установка
=========
Expand Down
54 changes: 54 additions & 0 deletions src/apa_renderer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""
Функции для генерации выходного файла с оформленным списком использованных источников.
"""
from __future__ import annotations

from pathlib import Path

from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH # pylint: disable=E0611
from docx.shared import Pt


class APARenderer:
"""
Создание выходного файла – Word.
"""

def __init__(self, rows: tuple[str, ...]):
self.rows = rows

def render(self, path: Path | str) -> None:
"""
Метод генерации Word-файла со списком использованных источников.

:param Path | str path: Путь для сохранения выходного файла.
"""

document = Document()

# стилизация заголовка
paragraph = document.add_paragraph()
paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
runner = paragraph.add_run("Список использованной литературы")
runner.bold = True

# стилизация текста
style_normal = document.styles["Normal"]
style_normal.font.name = "Times New Roman"
style_normal.font.size = Pt(12)
style_normal.paragraph_format.line_spacing = 1.5
style_normal.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY

for row in self.rows:
# добавление источника
content = row.split("/i/")
if len(content) > 1:
list_number = document.add_paragraph(content[0], style="List Number")
list_number.add_run(content[1]).italic = True
list_number.add_run(content[2]).italic = False
else:
document.add_paragraph(row, style="List Number")

# сохранение файла Word
document.save(path)
54 changes: 54 additions & 0 deletions src/formatters/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,57 @@ class ArticlesCollectionModel(BaseModel):
publishing_house: str
year: int = Field(..., gt=0)
pages: str


class MagazineArticleModel(BaseModel):

"""
Модель статьи из журнала:

.. code-block::

MagazineArticleModel(
authors="Иванов И.М., Петров С.Н.",
article_title="Наука как искусство",
magazine_title="Образование и наука",
year=2020,
volume=10,
pages="25-30",
)
"""

authors: str
article_title: str
magazine_title: str
year: int = Field(..., gt=0)
volume: int = Field(..., gt=0)
pages: str


class ThesisModel(BaseModel):

"""
Модель диссертации:

.. code-block::

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

author: str
title: str
degree: str
field: str
code: str
city: str
year: int = Field(..., gt=0)
pages: int = Field(..., gt=0)
93 changes: 93 additions & 0 deletions src/formatters/styles/apa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
Стиль цитирования по American Psychological Association.
"""
from string import Template

from pydantic import BaseModel

from formatters.models import InternetResourceModel, MagazineArticleModel
from formatters.styles.base import BaseCitationStyle
from logger import get_logger

logger = get_logger(__name__)


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

data: InternetResourceModel

@property
def template(self) -> Template:
return Template("/i/$article/i/. ($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 APAMagazineArticle(BaseCitationStyle):
"""
Форматирование для статьи из журнала.
"""

data: MagazineArticleModel

@property
def template(self) -> Template:
return Template(
"$authors ($year). $article_title. /i/$magazine_title/i/, $volume, $pages."
)

def substitute(self) -> str:

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

return self.template.substitute(
authors=self.data.authors,
article_title=self.data.article_title,
magazine_title=self.data.magazine_title,
year=self.data.year,
volume=self.data.volume,
pages=self.data.pages,
)


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

formatters_map = {
MagazineArticleModel.__name__: APAMagazineArticle,
InternetResourceModel.__name__: APAInternetResource,
}

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)
68 changes: 63 additions & 5 deletions src/formatters/styles/gost.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@

from pydantic import BaseModel

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


logger = get_logger(__name__)


Expand All @@ -27,7 +32,6 @@ def template(self) -> Template:
)

def substitute(self) -> str:

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

return self.template.substitute(
Expand Down Expand Up @@ -64,7 +68,6 @@ def template(self) -> Template:
)

def substitute(self) -> str:

logger.info('Форматирование интернет-ресурса "%s" ...', self.data.article)

return self.template.substitute(
Expand All @@ -89,7 +92,6 @@ def template(self) -> Template:
)

def substitute(self) -> str:

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

return self.template.substitute(
Expand All @@ -103,6 +105,60 @@ def substitute(self) -> str:
)


class GOSTMagazineArticle(BaseCitationStyle):
"""
Форматирование для статьи из журнала.
"""

data: MagazineArticleModel

@property
def template(self) -> Template:
return Template(
"$authors $article_title // $magazine_title. $year. Т. $volume. С. $pages."
)

def substitute(self) -> str:
logger.info('Форматирование статьи из журнала "%s" ...', self.data.article_title)

return self.template.substitute(
authors=self.data.authors,
article_title=self.data.article_title,
magazine_title=self.data.magazine_title,
year=self.data.year,
volume=self.data.volume,
pages=self.data.pages,
)


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

data: ThesisModel

@property
def template(self) -> Template:
return Template(
"$author $title : $degree $field $code / $city, $year. - $pages с."
)

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

return self.template.substitute(
author=self.data.author,
title=self.data.title,
degree=self.data.degree,
field=self.data.field,
code=self.data.code,
city=self.data.city,
year=self.data.year,
pages=self.data.pages,
)


class GOSTCitationFormatter:
"""
Базовый класс для итогового форматирования списка источников.
Expand All @@ -112,6 +168,8 @@ class GOSTCitationFormatter:
BookModel.__name__: GOSTBook,
InternetResourceModel.__name__: GOSTInternetResource,
ArticlesCollectionModel.__name__: GOSTCollectionArticle,
MagazineArticleModel.__name__: GOSTMagazineArticle,
ThesisModel.__name__: GOSTThesis
}

def __init__(self, models: list[BaseModel]) -> None:
Expand Down
31 changes: 20 additions & 11 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
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 readers.apa_reader import APASourcesReader
from renderer import Renderer
from apa_renderer import APARenderer
from settings import INPUT_FILE_PATH, OUTPUT_FILE_PATH

logger = get_logger(__name__)
Expand All @@ -30,7 +33,7 @@ class CitationEnum(Enum):
"--citation",
"-c",
"citation",
type=click.Choice([item.name for item in CitationEnum], case_sensitive=False),
type=click.Choice(list(CitationEnum), case_sensitive=False),
default=CitationEnum.GOST.name,
show_default=True,
help="Стиль цитирования",
Expand Down Expand Up @@ -75,16 +78,22 @@ def process_input(
path_input,
path_output,
)

models = SourcesReader(path_input).read()
formatted_models = tuple(
str(item) for item in GOSTCitationFormatter(models).format()
)

logger.info("Генерация выходного файла ...")
Renderer(formatted_models).render(path_output)

logger.info("Команда успешно завершена.")
if citation is CitationEnum.APA.name:
models = APASourcesReader(path_input).read()
formatted_models = tuple(
str(item) for item in APACitationFormatter(models).format()
)
logger.info("Генерация выходного файла ...")
APARenderer(formatted_models).render(path_output)
logger.info("Команда успешно завершена.")
else:
models = SourcesReader(path_input).read()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Блок кода похож на код выше. Можно вынести в функцию для избежания дублирования логики

formatted_models = tuple(
str(item) for item in GOSTCitationFormatter(models).format()
)
logger.info("Генерация выходного файла ...")
Renderer(formatted_models).render(path_output)
logger.info("Команда успешно завершена.")


if __name__ == "__main__":
Expand Down
Loading