| 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 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 78 |  |  |     schema = graphinate.builders.GraphQLBuilder(filesystem_model).build() | 
            
                                                        
            
                                    
            
            
                | 79 |  |  |     graphinate.graphql.server(schema) | 
            
                                                        
            
                                    
            
            
                | 80 |  |  |  |