ebk 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. ebk/__init__.py +35 -0
  2. ebk/ai/__init__.py +23 -0
  3. ebk/ai/knowledge_graph.py +450 -0
  4. ebk/ai/llm_providers/__init__.py +26 -0
  5. ebk/ai/llm_providers/anthropic.py +209 -0
  6. ebk/ai/llm_providers/base.py +295 -0
  7. ebk/ai/llm_providers/gemini.py +285 -0
  8. ebk/ai/llm_providers/ollama.py +294 -0
  9. ebk/ai/metadata_enrichment.py +394 -0
  10. ebk/ai/question_generator.py +328 -0
  11. ebk/ai/reading_companion.py +224 -0
  12. ebk/ai/semantic_search.py +433 -0
  13. ebk/ai/text_extractor.py +393 -0
  14. ebk/calibre_import.py +66 -0
  15. ebk/cli.py +6433 -0
  16. ebk/config.py +230 -0
  17. ebk/db/__init__.py +37 -0
  18. ebk/db/migrations.py +507 -0
  19. ebk/db/models.py +725 -0
  20. ebk/db/session.py +144 -0
  21. ebk/decorators.py +1 -0
  22. ebk/exports/__init__.py +0 -0
  23. ebk/exports/base_exporter.py +218 -0
  24. ebk/exports/echo_export.py +279 -0
  25. ebk/exports/html_library.py +1743 -0
  26. ebk/exports/html_utils.py +87 -0
  27. ebk/exports/hugo.py +59 -0
  28. ebk/exports/jinja_export.py +286 -0
  29. ebk/exports/multi_facet_export.py +159 -0
  30. ebk/exports/opds_export.py +232 -0
  31. ebk/exports/symlink_dag.py +479 -0
  32. ebk/exports/zip.py +25 -0
  33. ebk/extract_metadata.py +341 -0
  34. ebk/ident.py +89 -0
  35. ebk/library_db.py +1440 -0
  36. ebk/opds.py +748 -0
  37. ebk/plugins/__init__.py +42 -0
  38. ebk/plugins/base.py +502 -0
  39. ebk/plugins/hooks.py +442 -0
  40. ebk/plugins/registry.py +499 -0
  41. ebk/repl/__init__.py +9 -0
  42. ebk/repl/find.py +126 -0
  43. ebk/repl/grep.py +173 -0
  44. ebk/repl/shell.py +1677 -0
  45. ebk/repl/text_utils.py +320 -0
  46. ebk/search_parser.py +413 -0
  47. ebk/server.py +3608 -0
  48. ebk/services/__init__.py +28 -0
  49. ebk/services/annotation_extraction.py +351 -0
  50. ebk/services/annotation_service.py +380 -0
  51. ebk/services/export_service.py +577 -0
  52. ebk/services/import_service.py +447 -0
  53. ebk/services/personal_metadata_service.py +347 -0
  54. ebk/services/queue_service.py +253 -0
  55. ebk/services/tag_service.py +281 -0
  56. ebk/services/text_extraction.py +317 -0
  57. ebk/services/view_service.py +12 -0
  58. ebk/similarity/__init__.py +77 -0
  59. ebk/similarity/base.py +154 -0
  60. ebk/similarity/core.py +471 -0
  61. ebk/similarity/extractors.py +168 -0
  62. ebk/similarity/metrics.py +376 -0
  63. ebk/skills/SKILL.md +182 -0
  64. ebk/skills/__init__.py +1 -0
  65. ebk/vfs/__init__.py +101 -0
  66. ebk/vfs/base.py +298 -0
  67. ebk/vfs/library_vfs.py +122 -0
  68. ebk/vfs/nodes/__init__.py +54 -0
  69. ebk/vfs/nodes/authors.py +196 -0
  70. ebk/vfs/nodes/books.py +480 -0
  71. ebk/vfs/nodes/files.py +155 -0
  72. ebk/vfs/nodes/metadata.py +385 -0
  73. ebk/vfs/nodes/root.py +100 -0
  74. ebk/vfs/nodes/similar.py +165 -0
  75. ebk/vfs/nodes/subjects.py +184 -0
  76. ebk/vfs/nodes/tags.py +371 -0
  77. ebk/vfs/resolver.py +228 -0
  78. ebk/vfs_router.py +275 -0
  79. ebk/views/__init__.py +32 -0
  80. ebk/views/dsl.py +668 -0
  81. ebk/views/service.py +619 -0
  82. ebk-0.4.4.dist-info/METADATA +755 -0
  83. ebk-0.4.4.dist-info/RECORD +87 -0
  84. ebk-0.4.4.dist-info/WHEEL +5 -0
  85. ebk-0.4.4.dist-info/entry_points.txt +2 -0
  86. ebk-0.4.4.dist-info/licenses/LICENSE +21 -0
  87. ebk-0.4.4.dist-info/top_level.txt +1 -0
ebk/repl/grep.py ADDED
@@ -0,0 +1,173 @@
1
+ """grep implementation for REPL shell."""
2
+
3
+ import re
4
+ from typing import List, Tuple
5
+
6
+ from ebk.vfs import LibraryVFS, DirectoryNode, FileNode
7
+
8
+
9
+ class GrepMatcher:
10
+ """Unix-like grep functionality for VFS."""
11
+
12
+ def __init__(self, vfs: LibraryVFS):
13
+ """Initialize grep matcher.
14
+
15
+ Args:
16
+ vfs: VFS instance
17
+ """
18
+ self.vfs = vfs
19
+
20
+ def grep(
21
+ self,
22
+ pattern: str,
23
+ paths: List[str],
24
+ recursive: bool = False,
25
+ ignore_case: bool = False,
26
+ line_numbers: bool = False,
27
+ ) -> List[Tuple[str, int, str]]:
28
+ """Search for pattern in files.
29
+
30
+ Args:
31
+ pattern: Regex pattern to search for
32
+ paths: List of paths to search (files or directories)
33
+ recursive: Search directories recursively
34
+ ignore_case: Case-insensitive matching
35
+ line_numbers: Include line numbers in output
36
+
37
+ Returns:
38
+ List of (file_path, line_number, line_content) tuples
39
+ """
40
+ results = []
41
+
42
+ # Compile regex
43
+ flags = re.IGNORECASE if ignore_case else 0
44
+ try:
45
+ regex = re.compile(pattern, flags)
46
+ except re.error as e:
47
+ raise ValueError(f"Invalid regex pattern: {e}")
48
+
49
+ # Process each path
50
+ for path in paths:
51
+ results.extend(
52
+ self._search_path(path, regex, recursive, line_numbers)
53
+ )
54
+
55
+ return results
56
+
57
+ def _search_path(
58
+ self,
59
+ path: str,
60
+ regex: re.Pattern,
61
+ recursive: bool,
62
+ line_numbers: bool,
63
+ ) -> List[Tuple[str, int, str]]:
64
+ """Search a single path.
65
+
66
+ Args:
67
+ path: Path to search
68
+ regex: Compiled regex pattern
69
+ recursive: Search recursively
70
+ line_numbers: Include line numbers
71
+
72
+ Returns:
73
+ List of matches
74
+ """
75
+ results = []
76
+
77
+ # Resolve path
78
+ node = self.vfs.get_node(path)
79
+ if node is None:
80
+ return results
81
+
82
+ # If it's a file, search it
83
+ if isinstance(node, FileNode):
84
+ results.extend(self._search_file(node, regex, line_numbers))
85
+
86
+ # If it's a directory, search children
87
+ elif isinstance(node, DirectoryNode):
88
+ if recursive:
89
+ results.extend(
90
+ self._search_directory_recursive(node, regex, line_numbers)
91
+ )
92
+ else:
93
+ # Just search immediate children that are files
94
+ for child in node.list_children():
95
+ if isinstance(child, FileNode):
96
+ results.extend(
97
+ self._search_file(child, regex, line_numbers)
98
+ )
99
+
100
+ return results
101
+
102
+ def _search_file(
103
+ self,
104
+ file_node: FileNode,
105
+ regex: re.Pattern,
106
+ line_numbers: bool,
107
+ ) -> List[Tuple[str, int, str]]:
108
+ """Search a single file.
109
+
110
+ Args:
111
+ file_node: File node to search
112
+ regex: Compiled regex pattern
113
+ line_numbers: Include line numbers
114
+
115
+ Returns:
116
+ List of matches
117
+ """
118
+ results = []
119
+
120
+ try:
121
+ content = file_node.read_content()
122
+ lines = content.split("\n")
123
+
124
+ file_path = file_node.get_path()
125
+
126
+ for i, line in enumerate(lines, start=1):
127
+ if regex.search(line):
128
+ line_num = i if line_numbers else 0
129
+ results.append((file_path, line_num, line))
130
+
131
+ except Exception:
132
+ # Skip files that can't be read
133
+ pass
134
+
135
+ return results
136
+
137
+ def _search_directory_recursive(
138
+ self,
139
+ dir_node: DirectoryNode,
140
+ regex: re.Pattern,
141
+ line_numbers: bool,
142
+ ) -> List[Tuple[str, int, str]]:
143
+ """Search directory recursively.
144
+
145
+ Args:
146
+ dir_node: Directory node to search
147
+ regex: Compiled regex pattern
148
+ line_numbers: Include line numbers
149
+
150
+ Returns:
151
+ List of matches
152
+ """
153
+ results = []
154
+
155
+ try:
156
+ children = dir_node.list_children()
157
+
158
+ for child in children:
159
+ if isinstance(child, FileNode):
160
+ results.extend(self._search_file(child, regex, line_numbers))
161
+ elif isinstance(child, DirectoryNode):
162
+ # Recurse into subdirectory
163
+ results.extend(
164
+ self._search_directory_recursive(
165
+ child, regex, line_numbers
166
+ )
167
+ )
168
+
169
+ except Exception:
170
+ # Skip directories that can't be read
171
+ pass
172
+
173
+ return results