"""ГОСТ 7.1-2003 — Библиографическая запись. Библиографическое описание. Полная запись для списка литературы. Формат для статьи в журнале: Автор(ы). Название // Журнал. — Год. — Т. X, № X. — С. X–X. Формат для книги: Автор(ы). Название. — Город : Издательство, Год. — X с. Правила по ГОСТ 7.1-2003: - До 3 авторов: перечислить всех - 4+ авторов: первые 3 + "и др." (рус.) / "et al." (англ.) - Разделитель смысловых частей: " — " - Разделитель авторов: ", " - Место публикации через "/" """ import re def _format_author(author: dict) -> str: """ Форматировать одного автора. Args: author: dict с полями last_name, first_name, initials (опционально) Returns: Строка вида "Иванов И. И." или "Иванов И." """ last_name = author.get("last_name", "").strip() initials = author.get("initials", "").strip() first_name = author.get("first_name", "").strip() if not last_name: return "" if initials: # Нормализовать инициалы: обеспечить точки if not initials.endswith("."): initials += "." return f"{last_name} {initials}" elif first_name: # Извлечь инициалы из полного имени parts = first_name.split() inits = "".join(p[0].upper() + "." for p in parts if p) return f"{last_name} {inits}" else: return last_name def _format_authors(authors: list[dict], lang: str = "ru") -> str: """ Форматировать список авторов по правилам ГОСТ 7.1-2003. Args: authors: Список словарей с авторами lang: Язык для "и др." / "et al." Returns: Строка с отформатированными авторами """ if not authors: return "" et_al = "и др." if lang == "ru" else "et al." formatted = [_format_author(a) for a in authors if a.get("last_name")] formatted = [f for f in formatted if f] if not formatted: return "" if len(formatted) <= 3: return ", ".join(formatted) else: return ", ".join(formatted[:3]) + f", {et_al}" class GOST71Formatter: """Форматтер полных библиографических записей по ГОСТ 7.1-2003.""" def format_full(self, doc: dict) -> str: """ Форматировать полную библиографическую запись. Args: doc: Словарь с полями документа Returns: Отформатированная строка библиографической записи """ doc_type = self._detect_type(doc) if doc_type == "article": return self._format_article(doc) elif doc_type == "book": return self._format_book(doc) elif doc_type == "web": return self._format_web(doc) else: return self._format_generic(doc) def _detect_type(self, doc: dict) -> str: """Определить тип документа.""" if doc.get("journal"): return "article" if doc.get("url") and not doc.get("journal"): return "web" return "generic" def _format_article(self, doc: dict) -> str: """ Форматировать статью в журнале. Формат: Автор(ы). Название // Журнал. — Год. — Т. X, № X. — С. X–X. """ lang = doc.get("lang", "ru") or "ru" authors = doc.get("authors", []) author_str = _format_authors(authors, lang=lang) title = (doc.get("title") or "").strip() journal = (doc.get("journal") or "").strip() year = doc.get("year", "") volume = doc.get("volume", "") issue = doc.get("issue", "") pages = doc.get("pages", "") doi = doc.get("doi", "") parts = [] # Авторы и название if author_str: parts.append(f"{author_str}. {title}") else: parts.append(title) # Место публикации location = f"// {journal}" if year: location += f". — {year}" if volume: location += f". — Т. {volume}" if issue: location += f", № {issue}" if pages: location += f". — С. {pages}" parts.append(location) result = " ".join(parts) # DOI if doi: result += f". — DOI: {doi}" return result.strip() def _format_book(self, doc: dict) -> str: """ Форматировать книгу / монографию. Формат: Автор(ы). Название. — Город : Издательство, Год. — X с. """ lang = doc.get("lang", "ru") or "ru" authors = doc.get("authors", []) author_str = _format_authors(authors, lang=lang) title = (doc.get("title") or "").strip() year = doc.get("year", "б. г.") pages = doc.get("pages", "") parts = [] if author_str: parts.append(f"{author_str}.") parts.append(f"{title}.") parts.append(f"— {year}.") if pages: # Предполагаем, что pages содержит количество страниц parts.append(f"— {pages} с.") return " ".join(parts).strip() def _format_web(self, doc: dict) -> str: """ Форматировать электронный ресурс. Формат: Автор(ы). Название [Электронный ресурс]. — URL: ... (дата обращения: ...) """ lang = doc.get("lang", "ru") or "ru" authors = doc.get("authors", []) author_str = _format_authors(authors, lang=lang) title = (doc.get("title") or "").strip() url = (doc.get("url") or "").strip() year = doc.get("year", "") parts = [] if author_str: parts.append(f"{author_str}.") # Для ГОСТ — обозначение электронного ресурса parts.append(f"{title} [Электронный ресурс].") if year: parts.append(f"— {year}.") if url: parts.append(f"— URL: {url}") return " ".join(parts).strip() def _format_generic(self, doc: dict) -> str: """Базовый форматтер для неопределённых типов.""" lang = doc.get("lang", "ru") or "ru" author_str = _format_authors(doc.get("authors", []), lang=lang) title = (doc.get("title") or "").strip() year = doc.get("year", "") result = "" if author_str: result += f"{author_str}. " result += title if year: result += f". — {year}" return result.strip() def _get_sort_key(doc: dict) -> str: """ Получить ключ сортировки для документа. Кириллица идёт перед латиницей (для русских традиций): сначала русскоязычные источники, затем иностранные. Args: doc: Словарь с метаданными Returns: Строка для сортировки """ authors = doc.get("authors", []) if authors: last_name = authors[0].get("last_name", "") else: last_name = doc.get("title", "") if not last_name: return "яяя" # В конец # Кириллица < латиница (традиция: русские источники первыми) first_char = last_name[0].lower() is_latin = ord(first_char) < 256 and first_char.isalpha() # Префикс для сортировки: 0 для кириллицы, 1 для латиницы prefix = "1" if is_latin else "0" return f"{prefix}{last_name.lower()}" def format_full(doc: dict) -> str: """Удобная функция для быстрого форматирования.""" return GOST71Formatter().format_full(doc)