-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdata_structures.py
More file actions
222 lines (178 loc) · 7.22 KB
/
data_structures.py
File metadata and controls
222 lines (178 loc) · 7.22 KB
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
#!/usr/bin/env python
# pylint: disable=C0103,W0622,E1136
'''
Loads graph from a file as a text through regexp
'''
# fix: find another way to find points of graph due pythonic RegExp
# import re
from dataclasses import dataclass
from typing import Optional, List, Union, Iterable, FrozenSet
import config
from base import (
StringRegularExpressionMaskAbstract, GE, GGE, GM, Tree,
RepresentativeGraphElementAbstract, GraphTreeRepresintationMaskAbstract)
__all__ = (
'RepresentativeGraphElementMask', 'StringByStringRegularExpressionMask')
@dataclass
class RepresentativeGraphElementMask(RepresentativeGraphElementAbstract):
''' Sensetive turn off '''
id: str = ''
part: str = ''
grouped: str = ''
body: str = ''
graph: StringRegularExpressionMaskAbstract = None
separater_key: str = 'NODE'
def __str__(self):
return f'{self.part} id: {self.id} = {self.grouped} - {self.body}'
def __repr__(self):
return self.__str__()
def __hash__(self):
return self.id # TODO: have to use hasheable function instead
# TODO: move all show logic to new console or graphic interface in MIXIN
# inheretince way
def show_children(self):
''' Texted view of children of the elemenet '''
return self._separeter().join(str(child) for child in self.children)
def show_parents(self):
''' Texted view of parents of the elemenet '''
return self._separeter().join(str(parent) for parent in self.parents)
def walk(self, left: bool = True, chain: Optional[List] = None):
'''
Walking down through the graph to the deep to see how grap was changed
'''
if chain:
index = chain.pop()
else:
return self
next_el = self.children[index] if left else self.children.end(index)
yield next_el
# fix: make deep searching algorithm based on this property
yield from next_el.walk(left=left, chain=chain)
def _separeter(self):
return config.SEPARATES.get(self.separater_key, self.graph.separeter)
@dataclass
class GraphTreeRepresentationMask(GraphTreeRepresintationMaskAbstract):
''' Frozen Tree '''
_sliced_graph: GM = None
# TODO: find the way of searching elements by a hash
element_ids: FrozenSet[int] = None
def __iter__(self) -> GGE:
return iter(self[_id] for _id in self.element_ids)
def __len__(self) -> int:
return len(list(self[_id] for _id in self.element_ids))
def __str__(self) -> str:
return str(self._sliced_graph + ' Tree')
def __repr__(self) -> str:
return self.__str__()
def __getitem__(self, key: int, pythonic_list: bool = True) -> GE:
if key not in self.element_ids:
raise config.OutFromTreeError
return self._sliced_graph[key]
def __contains__(self, element: GE) -> bool:
return element.id in self.element_ids
@property
def depth(self) -> int:
''' The deepth of the graph '''
# fix: make deep searching algorithm based on this property
return self.bfs()[0]
@property
def longest_chain(self) -> Iterable[int]:
''' The logest chain to iterate through the DFS algorithm '''
return self.bfs()[1]
def dfs(self) -> GGE:
# TODO: should to work in the composition way
maxdepth = 0
visited = []
queue = []
visited.append(self._sliced_graph.tree_topic)
queue.append((self._sliced_graph.tree_topic,1))
while queue:
x, depth = queue.pop(0)
maxdepth = max(maxdepth, depth)
# print(x)
for child in self._sliced_graph[x].children:
if child not in visited:
visited.append(child)
queue.append((child,depth+1))
return maxdepth, map(lambda x: x.id, visited)
def bfs(self) -> GGE:
pass
@dataclass
class StringByStringRegularExpressionMask(StringRegularExpressionMaskAbstract):
''' Sensetive turn off '''
# TODO: move it to an another class like composition
# element_mask: Optional[str] = r'.+(?P<id>\D+)\..?(?P<grouped>.+): (?P<body>.*)\n'
# node_mask: Optional[str] = r'(?P<id>\D+)\((?P<children_list>.*)\)'
# part_mask: Optional[str] = r'.*(?P<id>\S+\D+\).\n'
tmp: Optional[str] = None
separeter: str = config.SEPARATES.get('NODE')
file: str = config.FILE_DATA_LOADER_PATH
last_part: str = 'A1.'
element_class: GE = RepresentativeGraphElementMask
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.tmp:
with open(self.file, 'r', encoding='utf8') as file:
self.tmp: str = file.read()
# TODO: it worth but i have to load self.ids_map
for _ in self:
continue
def __iter__(self) -> GGE:
return iter(self._get_formated_links())
def __len__(self) -> int:
return len(list(self._get_formated_links()))
def __str__(self) -> str:
return self.separeter.join(str(tmp) for tmp in iter(self))
def __repr__(self) -> str:
return self.__str__()
def __getitem__(self, key: int, pythonic_list: bool = True) -> GE:
if pythonic_list:
return tuple(self)[key]
for part, _id in self.ids_map:
if key <= _id:
return self.get_element(part, key)
key -= _id
continue
raise IndexError()
def __contains__(self, element: GE) -> bool:
try:
element = self.get_element(element.part, element.id)
except IndexError:
return False
return isinstance(element, self.element_class)
def _get_formated_links(self):
for link in self.tmp.split(self.separeter):
if (tmp := link.strip()).endswith('.'):
self.last_part = tmp
continue
if not tmp:
# ATTENTION: ignore blank line
continue
yield from self._convert_element(tmp, self.last_part)
def get_elements(self, part: str =None, id: Union[str, int] =None) -> GE:
if not id and not part:
raise IndexError()
if not part:
# fix: it would be good idea if we can search only by id???
raise IndexError('Part has not defined when id was passed')
if id and part:
yield from (el for el in self if el.part == part and el.id == id)
def get_element(self, part: str =None, id: Union[str, int] =None) -> GE:
# TODO: refactor the idea of methods get_element and get_elements
for key, value in self.ids_map.items():
if key == part:
break
id += value
# TODO: sequence of keis have to start from zero indstead of one
return self[id-1]
@property
def tree_topic(self) -> GE:
''' Highest element in the biggest tree of the graph '''
return self[0] # TODO: make magic algortihm which return the top of the biggest tree
def exlude_tree(self) -> Tree:
'''
Find the sequence which can work like a tree. Raise
Vaildation Error if it has no any tree variant
'''
ids = {el.id for el in self.tree_topic.walk()}
return GraphTreeRepresentationMask(self, ids)