File size: 18,632 Bytes
2249e80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
"""
SFOSR Database Module

Обеспечивает взаимодействие с базой данных SFOSR.
Предоставляет методы для извлечения аксиом, правил вывода,
концептов и их свойств, необходимых для работы системы.
"""

import sqlite3
import json
from typing import Dict, List, Any, Optional, Tuple, Union, Set

class SFOSRDatabase:
    """
    Класс для работы с базой данных SFOSR
    
    Предоставляет интерфейс для:
    - Получения аксиом и правил вывода
    - Извлечения информации о концептах
    - Получения векторных связей между концептами
    - Добавления новых данных в базу знаний
    """
    
    def __init__(self, db_path="sfosr.db"):
        """Инициализация соединения с БД"""
        self.db_path = db_path
        self.connection = None
        
    def connect(self):
        """Подключение к БД"""
        # Возвращаем новое соединение каждый раз
        # row_factory установим здесь же
        connection = sqlite3.connect(self.db_path)
        connection.row_factory = sqlite3.Row
        return connection
        
    # Добавляем контекстный менеджер
    def __enter__(self):
        self.connection = self.connect()
        return self.connection

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.connection:
            self.connection.close()
            self.connection = None # Сбрасываем соединение

    def get_axioms(self) -> List[Dict]:
        """Получение всех аксиом из БД"""
        with self as conn: # Используем with
            cursor = conn.cursor()
            cursor.execute("SELECT * FROM axioms")
            axioms = [dict(row) for row in cursor.fetchall()]
        return axioms
    
    def get_inference_rules(self) -> List[Dict]:
        """Получение всех правил вывода из БД"""
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id, name, description, pattern, premise_types, conclusion_types, domain FROM inference_rules")
            rules = [dict(row) for row in cursor.fetchall()]
        return rules
    
    def get_concept_by_name(self, name: str) -> Optional[Dict]:
        """Поиск концепта по имени"""
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id, name, description, domain, level FROM concepts WHERE name=?", (name,))
            concept = cursor.fetchone()
        return dict(concept) if concept else None
    
    def get_all_concepts(self) -> List[Dict]:
        """Получение всех концептов из БД"""
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT id, name, description, domain, level FROM concepts")
            concepts = [dict(row) for row in cursor.fetchall()]
        return concepts
    
    def get_all_concept_names(self) -> Set[str]:
        """Получение имен всех концептов из БД"""
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT name FROM concepts")
            names = {row['name'] for row in cursor.fetchall()}
        return names
    
    def get_vectors_for_concept(self, concept_id: int) -> List[Dict]:
        """
        Получение всех векторов, связанных с концептом
        
        Args:
            concept_id: ID концепта
            
        Returns:
            Список векторов с именами источника и цели
        """
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT v.id, v.source_id, v.target_id, v.vector_type, v.axis, v.justification, 
                       c1.name as source_name, c2.name as target_name 
                FROM vectors v
                JOIN concepts c1 ON v.source_id = c1.id
                JOIN concepts c2 ON v.target_id = c2.id
                WHERE v.source_id=? OR v.target_id=?
            """, (concept_id, concept_id))
            vectors = [dict(row) for row in cursor.fetchall()]
        return vectors
    
    def get_concept_properties(self, concept_id: int) -> Dict[str, Any]:
        """
        Получение всех свойств концепта
        
        Args:
            concept_id: ID концепта
            
        Returns:
            Словарь свойств в формате {имя_свойства: значение}
        """
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT property_name, property_value 
                FROM concept_properties
                WHERE concept_id=?
            """, (concept_id,))
            properties = {}
            for row in cursor.fetchall():
                prop_name = row['property_name']
                prop_value = row['property_value']
                try:
                    if isinstance(prop_value, str) and (prop_value.startswith('[') or prop_value.startswith('{')):
                        prop_value = json.loads(prop_value)
                except (json.JSONDecodeError, TypeError):
                    pass
                properties[prop_name] = prop_value
        return properties
        
    def get_complete_concept_info(self, concept_name: str) -> Optional[Dict]:
        """
        Получение полной информации о концепте
        
        Args:
            concept_name: Имя концепта
            
        Returns:
            Словарь с информацией о концепте, его свойствах и связях
        """
        concept = self.get_concept_by_name(concept_name)
        if not concept:
            return None
            
        concept_id = concept['id']
        properties = self.get_concept_properties(concept_id)
        vectors = self.get_vectors_for_concept(concept_id)
        
        return {
            "concept": concept,
            "properties": properties,
            "vectors": vectors
        }
    
    def get_related_concepts(self, concept_id: int, depth: int = 1) -> List[Dict]:
        """
        Получение связанных концептов с заданной глубиной
        
        Args:
            concept_id: ID исходного концепта
            depth: Глубина поиска связей (1 = только прямые связи)
            
        Returns:
            Список связанных концептов
        """
        if depth <= 0:
            return []
            
        # Получаем прямые связи
        vectors = self.get_vectors_for_concept(concept_id)
        related_ids = set()
        
        for vector in vectors:
            if vector['source_id'] != concept_id:
                related_ids.add(vector['source_id'])
            if vector['target_id'] != concept_id:
                related_ids.add(vector['target_id'])
                
        # Рекурсивно получаем связи с заданной глубиной
        all_related = []
        for related_id in related_ids:
            with self as conn:
                cursor = conn.cursor()
                cursor.execute("SELECT * FROM concepts WHERE id=?", (related_id,))
                concept = cursor.fetchone()
            
            if concept:
                related_concept = dict(concept)
                all_related.append(related_concept)
                
                # Если нужна большая глубина, рекурсивно получаем связанные концепты
                if depth > 1:
                    deeper_related = self.get_related_concepts(related_id, depth - 1)
                    all_related.extend(deeper_related)
                    
        return all_related
    
    def find_path_between_concepts(self, source_name: str, target_name: str, max_depth: int = 3) -> List[Dict]:
        """
        Поиск пути между двумя концептами
        
        Args:
            source_name: Имя исходного концепта
            target_name: Имя целевого концепта
            max_depth: Максимальная глубина поиска
            
        Returns:
            Список векторов, образующих путь между концептами
        """
        source = self.get_concept_by_name(source_name)
        target = self.get_concept_by_name(target_name)
        
        if not source or not target:
            return []
            
        # Поиск в ширину для нахождения пути
        visited_concepts = {source['id']} # Храним ID концептов, чтобы не зацикливаться
        queue = [(source['id'], [])]  # (id_концепта, path_из_векторов_до_него)
        
        # Ограничиваем глубину поиска
        current_depth = 0
        nodes_at_current_depth = 1
        nodes_at_next_depth = 0

        while queue and current_depth < max_depth:
            if nodes_at_current_depth == 0:
                current_depth += 1
                nodes_at_current_depth = nodes_at_next_depth
                nodes_at_next_depth = 0
                if current_depth >= max_depth: # Проверка после инкремента глубины
                    break 

            current_id, path = queue.pop(0)
            nodes_at_current_depth -= 1
            
            # Проверка, не достигли ли мы цели
            if current_id == target['id']:
                return path # Возвращаем список векторов
                
            # Получаем связанные векторы для текущего концепта
            # Используем get_vectors_for_concept, так как он возвращает нужные данные
            connected_vectors = self.get_vectors_for_concept(current_id)
            
            for vector in connected_vectors:
                next_id = None
                
                # Определяем следующий концепт в пути
                if vector['source_id'] == current_id and vector['target_id'] not in visited_concepts:
                    next_id = vector['target_id']
                elif vector['target_id'] == current_id and vector['source_id'] not in visited_concepts:
                    next_id = vector['source_id']
                    
                if next_id:
                    visited_concepts.add(next_id)
                    # Добавляем сам вектор (как словарь) в путь
                    new_path = path + [vector] 
                    queue.append((next_id, new_path))
                    nodes_at_next_depth += 1
                    
        return []  # Путь не найден в пределах max_depth
    
    def convert_db_vector_to_system_format(self, db_vector: Dict) -> Dict:
        """
        Преобразование вектора из формата БД в формат системы SFOSR
        
        Args:
            db_vector: Вектор в формате БД
            
        Returns:
            Вектор в формате системы SFOSR
        """
        return {
            "id": f"V{db_vector['id']}",
            "source": db_vector['source_name'],
            "target": db_vector['target_name'],
            "type": db_vector['vector_type'],
            "axis": db_vector['axis'],
            "justification": db_vector['justification']
        }
    
    # Методы для обновления БД
    
    def add_concept(self, name: str, description: str, domain: str, level: str) -> int:
        """
        Добавление нового концепта в БД
        
        Args:
            name: Имя концепта
            description: Описание концепта
            domain: Домен (область знаний)
            level: Уровень абстракции
            
        Returns:
            ID добавленного концепта
        """
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("""
                INSERT INTO concepts (name, description, domain, level)
                VALUES (?, ?, ?, ?)
            """, (name, description, domain, level))
            new_id = cursor.lastrowid
            conn.commit()
        return new_id
    
    def add_concept_property(self, concept_id: int, property_name: str, property_value: Union[str, List, Dict]) -> int:
        """
        Добавление свойства концепта
        
        Args:
            concept_id: ID концепта
            property_name: Имя свойства
            property_value: Значение свойства (строка или JSON)
            
        Returns:
            ID добавленного свойства
        """
        # Если значение не строка, преобразуем в JSON
        if not isinstance(property_value, str):
            property_value = json.dumps(property_value)
            
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("""
                INSERT INTO concept_properties (concept_id, property_name, property_value)
                VALUES (?, ?, ?)
            """, (concept_id, property_name, property_value))
            new_id = cursor.lastrowid
            conn.commit()
        return new_id
    
    def add_vector(self, source_id: int, target_id: int, vector_type: str, 
                  axis: str, justification: Optional[str] = None) -> int:
        """
        Добавление нового вектора (связи между концептами)
        
        Args:
            source_id: ID исходного концепта
            target_id: ID целевого концепта
            vector_type: Тип вектора
            axis: Семантическая ось
            justification: Обоснование связи
            
        Returns:
            ID добавленного вектора
        """
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("""
                INSERT INTO vectors (source_id, target_id, vector_type, axis, justification)
                VALUES (?, ?, ?, ?, ?)
            """, (source_id, target_id, vector_type, axis, justification))
            new_id = cursor.lastrowid
            conn.commit()
        return new_id
    
    def add_axiom(self, name: str, description: str, formulation: str, domain: str) -> int:
        """
        Добавление новой аксиомы
        
        Args:
            name: Имя аксиомы
            description: Описание аксиомы
            formulation: Формальная формулировка
            domain: Домен (область применения)
            
        Returns:
            ID добавленной аксиомы
        """
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("""
                INSERT INTO axioms (name, description, formulation, domain)
                VALUES (?, ?, ?, ?)
            """, (name, description, formulation, domain))
            new_id = cursor.lastrowid
            conn.commit()
        return new_id
    
    def add_inference_rule(self, name: str, description: str, pattern: str, 
                          premise_types: str, conclusion_types: str, domain: str) -> int:
        """
        Добавление нового правила вывода
        
        Args:
            name: Имя правила
            description: Описание правила
            pattern: Паттерн вывода
            premise_types: Типы посылок
            conclusion_types: Типы выводов
            domain: Домен (область применения)
            
        Returns:
            ID добавленного правила
        """
        with self as conn:
            cursor = conn.cursor()
            cursor.execute("""
                INSERT INTO inference_rules (name, description, pattern, premise_types, 
                                         conclusion_types, domain)
                VALUES (?, ?, ?, ?, ?, ?)
            """, (name, description, pattern, premise_types, conclusion_types, domain))
            new_id = cursor.lastrowid
            conn.commit()
        return new_id 

    def get_all_vectors(self):
        """Получить все векторы из базы данных"""
        query = """
            SELECT 
                v.id,
                v.source_id,
                v.target_id,
                v.vector_type,
                v.axis,
                v.justification,
                s.name as source_name,
                t.name as target_name,
                v.is_valid
            FROM vectors v
            JOIN concepts s ON v.source_id = s.id
            JOIN concepts t ON v.target_id = t.id
            WHERE v.is_valid = 1
        """
        
        with self as conn:
            cursor = conn.cursor()
            cursor.execute(query)
            rows = cursor.fetchall()
            
            vectors = []
            for row in rows:
                vector = {
                    "id": f"V{row[0]}",  # Добавляем префикс V к ID
                    "source_name": row[6],
                    "target_name": row[7],
                    "type": row[3],  # vector_type из БД становится type в объекте
                    "axis": row[4],
                    "justification": row[5],
                    "is_valid": bool(row[8])
                }
                vectors.append(vector)
            
            return vectors