| | import pandas as pd |
| | import re |
| |
|
| |
|
| | |
| | YEARS_ALIASES = { |
| | 'O': 1918, |
| | 'M': 2000, |
| | 'N': 2026 |
| | } |
| |
|
| |
|
| | def _parse_single_year(year_str: str) -> int: |
| | """ |
| | Args: |
| | year_str: "1962" or alias like "O", "M", "N" |
| | |
| | Returns: |
| | int: Год |
| | """ |
| | if year_str in YEARS_ALIASES: |
| | return YEARS_ALIASES[year_str] |
| | else: |
| | try: |
| | return int(year_str) |
| | except ValueError: |
| | raise ValueError(f"Невозможно распарсить год: {year_str}") |
| |
|
| |
|
| | def _parse_date_range(date_str: str) -> tuple[int, int]: |
| | """Парсит строку с датой и возвращает (start_year, end_year). |
| | |
| | Поддерживает: |
| | - "1962-2002" -> (1962, 2002) |
| | - "1962" -> (1962, 1962) |
| | |
| | Args: |
| | date_str: Строка с датой |
| | |
| | Returns: |
| | tuple: (start_year, end_year) |
| | """ |
| | date_str = date_str.strip() |
| | |
| | |
| | if '-' in date_str: |
| | parts = date_str.split('-') |
| | start = _parse_single_year(parts[0].strip()) |
| | end = _parse_single_year(parts[1].strip()) |
| | assert start <= end, f"Год начала {start} должен быть меньше или равен году конца {end}" |
| | return (start, end) |
| | year = _parse_single_year(date_str) |
| | return (year, year) |
| |
|
| |
|
| | def parse_metadata_from_document(text: str) -> list[tuple[str, tuple[int, int], str]]: |
| | """Парсит markdown текст и возвращает список (чанк_текста, (год_начала, год_конца), summary). |
| | |
| | Формат разметки ОБЯЗАТЕЛЕН: |
| | - ## Summary text - заголовок summary (двойной хэш + пробел) |
| | - ### 1962-2002 - заголовок с годом (тройной хэш + пробел) |
| | |
| | Правила: |
| | - Каждый документ ДОЛЖЕН начинаться с "## {summary}" |
| | - После summary ДОЛЖНЫ быть заголовки "### {годы}" с текстом |
| | - ## распространяется на все абзацы ниже до следующего ## или конца файла |
| | - ### распространяется на абзацы ниже до следующего ### или ## |
| | - Текст БЕЗ предшествующего ### Не добавляется в результат |
| | |
| | Args: |
| | text: Полный текст документа |
| | |
| | Returns: |
| | list: [(chunk_text, (start_year, end_year), summary), ...] |
| | """ |
| | lines = text.split('\n') |
| | |
| | result = [] |
| | current_summary = None |
| | current_year_range = None |
| | |
| | for line in lines: |
| | strip_line = line.strip() |
| | if not strip_line: |
| | continue |
| | |
| | if strip_line.startswith('## '): |
| | current_summary = strip_line[3:].strip() |
| | |
| | |
| | elif strip_line.startswith('### '): |
| | current_year_range = _parse_date_range(strip_line[4:]) |
| | |
| | else: |
| | |
| | assert current_year_range and current_summary, breakpoint() |
| | result.append((line, current_year_range, current_summary)) |
| | |
| | return result |
| |
|
| |
|
| | def process_documents(documents) -> tuple[pd.DataFrame, pd.DataFrame]: |
| | """ |
| | Обрабатывает документы с парсингом дат и создает два датафрейма. |
| | |
| | Returns: |
| | tuple: (paragraphs_df, chunks_df) |
| | |
| | paragraphs_df: |
| | - paragraph_id: уникальный идентификатор абзаца |
| | - summary: название документа/раздела |
| | - start_year: год начала периода |
| | - end_year: год окончания периода |
| | - text: текст абзаца |
| | - document_id: ссылка на исходный документ |
| | |
| | chunks_df: |
| | - chunk_id: уникальный идентификатор чанка |
| | - paragraph_id: ссылка на абзац (foreign key) |
| | - text: текст чанка |
| | - lemmatized_text: лемматизированный текст (добавляется позже) |
| | """ |
| | paragraphs_data = [] |
| | chunks_data = [] |
| | |
| | paragraph_id_counter = 0 |
| | chunk_id_counter = 0 |
| | |
| | for doc_id, document in enumerate(documents): |
| | dated_chunks = parse_metadata_from_document(document) |
| | |
| | for chunk_text, year_range, summary in dated_chunks: |
| | paragraphs = chunk_text.split('\n') |
| | |
| | for paragraph in paragraphs: |
| | paragraph = paragraph.strip() |
| | |
| | |
| | paragraphs_data.append({ |
| | 'paragraph_id': paragraph_id_counter, |
| | 'summary': summary, |
| | 'start_year': year_range[0], |
| | 'end_year': year_range[1], |
| | 'text': paragraph, |
| | 'document_id': doc_id |
| | }) |
| | |
| | |
| | sentences = re.split(r'(?<=[.!?])\s+', paragraph) |
| | for sent in sentences: |
| | chunks_data.append({ |
| | 'chunk_id': chunk_id_counter, |
| | 'paragraph_id': paragraph_id_counter, |
| | 'text': sent.strip() |
| | }) |
| | chunk_id_counter += 1 |
| | |
| | paragraph_id_counter += 1 |
| | |
| | |
| | paragraphs_df = pd.DataFrame(paragraphs_data) |
| | chunks_df = pd.DataFrame(chunks_data) |
| | |
| | print(f"Создано {len(chunks_df)} чанков") |
| | print(f"Из {len(paragraphs_df)} абзацев в {len(set(paragraphs_df['document_id']))} документах") |
| | |
| | return paragraphs_df, chunks_df |
| |
|