Passed
Push — main ( ced03c...e8bc85 )
by Eran
01:33
created

files.create_filesystem_graph_model()   B

Complexity

Conditions 7

Size

Total Lines 45
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 26
nop 2
dl 0
loc 45
rs 7.856
c 0
b 0
f 0
1
import fnmatch
2
import operator
3
import pathlib
4
5
from magika import Magika
6
7
import graphinate
8
9
10
def load_ignore_patterns(ignore_files):
11
    patterns = set()
12
    for ignore_file in ignore_files:
13
        if pathlib.Path(ignore_file).exists():
14
            with open(ignore_file) as file:
15
                patterns.update(line.strip() for line in file if line.strip() and not line.startswith('#'))
16
17
    expand_patterns = {f"**/*{p}" if p.startswith('/') else f"**/*/{p}" for p in patterns}
18
    patterns.update(expand_patterns)
19
    return patterns
20
21
22
def is_ignored(path, patterns):
23
    return any(fnmatch.fnmatch(path.as_posix(), pattern) for pattern in patterns)
24
25
26
def create_filesystem_graph_model(input_folder='.', ignore_files=['.ignore', '.gitignore', '.dockerignore']):
27
    """
28
    Create a graph model of the file system structure.
29
30
    Args:
31
        input_folder (str): The folder to start the traversal from. Defaults to the current folder.
32
        ignore_files (list): A list of files containing ignore patterns.
33
                             Defaults to ['.ignore', '.gitignore', '.dockerignore'].
34
35
    Returns:
36
        GraphModel: A graph model representing the file system structure.
37
    """
38
    graph_model = graphinate.model(name="File System Graph")
39
    magika = Magika()
40
41
    root_folder = pathlib.Path(input_folder)
42
    ignore_patterns = load_ignore_patterns(ignore_files)
43
44
    def file_type(path: pathlib.Path) -> str:
45
        if path.is_file():
46
            return magika.identify_path(path).output.ct_label
47
        elif path.is_dir():
48
            return 'folder'
49
        else:
50
            return 'other'
51
52
    as_posix = operator.methodcaller('as_posix')
53
54
    @graph_model.node(file_type, key=as_posix, value=as_posix)
55
    def file_node():
56
        yield root_folder
57
        for path in root_folder.rglob('*'):
58
            if not is_ignored(path, ignore_patterns, ):
59
                yield path
60
61
    @graph_model.edge()
62
    def contains():
63
        for path in root_folder.rglob('*'):
64
            if not is_ignored(path, ignore_patterns):
65
                yield {
66
                    'source': path.parent.as_posix(),
67
                    'target': path.as_posix()
68
                }
69
70
    return graph_model
71
72
73
if __name__ == '__main__':
74
    input_folder = '..'  # Default to the current folder
75
    ignore_files = ['.ignore', '.gitignore']  # Example list of ignore files
76
    filesystem_model = create_filesystem_graph_model(input_folder, ignore_files)
77
    graphinate.materialize(
78
        filesystem_model,
79
        builder=graphinate.builders.GraphQLBuilder,
80
        builder_output_handler=graphinate.graphql
81
    )
82