File size: 6,410 Bytes
4dd2f1d
 
 
 
0ffefbc
361f7d9
503cd1a
 
 
 
0ffefbc
 
 
361f7d9
0ffefbc
361f7d9
0ffefbc
 
 
 
361f7d9
 
0ffefbc
 
 
 
 
 
 
 
361f7d9
0ffefbc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09bc630
 
361f7d9
 
0ffefbc
 
09bc630
 
 
 
 
 
0ffefbc
09bc630
 
 
 
 
 
0ffefbc
 
 
 
 
09bc630
0ffefbc
 
 
 
09bc630
0ffefbc
 
 
09bc630
 
 
 
 
 
 
 
 
 
 
0ffefbc
09bc630
 
 
0ffefbc
 
 
 
4dd2f1d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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