ures 2.2.1__tar.gz → 2.3.0__tar.gz
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.
- {ures-2.2.1 → ures-2.3.0}/PKG-INFO +1 -1
- {ures-2.2.1 → ures-2.3.0}/pyproject.toml +1 -1
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/__init__.py +8 -1
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/manager.py +16 -2
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/middlewares.py +1 -1
- ures-2.3.0/ures/markdown/__init__.py +4 -0
- {ures-2.2.1 → ures-2.3.0}/ures/markdown/manipulator.py +144 -0
- {ures-2.2.1 → ures-2.3.0}/ures/markdown/zettelkasten.py +75 -3
- ures-2.2.1/ures/markdown/__init__.py +0 -4
- {ures-2.2.1 → ures-2.3.0}/LICENSE +0 -0
- {ures-2.2.1 → ures-2.3.0}/docs/index.md +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/data_structure/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/data_structure/bi_directional_links.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/data_structure/tree.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/docker/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/docker/cleanup.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/docker/conf.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/docker/container.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/docker/containers.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/docker/image.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/docker/runtime.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/files.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/README.md +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/extractors.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/acm.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/basic.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/data_type.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/search/README.md +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/search/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/search/adapters.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/search/paper.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/search/search.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/literature/search_cli.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/memory/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/memory/allocator.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/memory/blocks.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/memory/smoke_test.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/network.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/secrets.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/string.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/timedate.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/tools/__init__.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/tools/decorator.py +0 -0
- {ures-2.2.1 → ures-2.3.0}/ures/tools/enum.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "ures"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.3.0"
|
|
4
4
|
description = "The project is designed to provide standard utilization functions for improving the reusability of frequently used functions."
|
|
5
5
|
authors = ["stone <github@stone-bo.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -148,7 +148,14 @@ class CitationManager:
|
|
|
148
148
|
def save_bibliography(
|
|
149
149
|
self,
|
|
150
150
|
file_path: str,
|
|
151
|
-
middlewares: Optional[
|
|
151
|
+
middlewares: Optional[
|
|
152
|
+
List[
|
|
153
|
+
Union[
|
|
154
|
+
Type[bibtexparser.middlewares.BlockMiddleware],
|
|
155
|
+
bibtexparser.middlewares.BlockMiddleware,
|
|
156
|
+
]
|
|
157
|
+
]
|
|
158
|
+
] = None,
|
|
152
159
|
) -> None:
|
|
153
160
|
"""Save all bibliography entries to a BibTeX file.
|
|
154
161
|
|
|
@@ -77,7 +77,14 @@ class BibManager:
|
|
|
77
77
|
def load_from_file(
|
|
78
78
|
self,
|
|
79
79
|
file_path: str,
|
|
80
|
-
middlewares: Optional[
|
|
80
|
+
middlewares: Optional[
|
|
81
|
+
List[
|
|
82
|
+
Union[
|
|
83
|
+
Type[bibtexparser.middlewares.BlockMiddleware],
|
|
84
|
+
bibtexparser.middlewares.BlockMiddleware,
|
|
85
|
+
]
|
|
86
|
+
]
|
|
87
|
+
] = None,
|
|
81
88
|
) -> bibtexparser.Library:
|
|
82
89
|
# Implementation for importing bibliography
|
|
83
90
|
_middlewares: List[bibtexparser.middlewares.BlockMiddleware] = [
|
|
@@ -106,7 +113,14 @@ class BibManager:
|
|
|
106
113
|
self,
|
|
107
114
|
file_path: str,
|
|
108
115
|
library: bibtexparser.Library,
|
|
109
|
-
middlewares: Optional[
|
|
116
|
+
middlewares: Optional[
|
|
117
|
+
List[
|
|
118
|
+
Union[
|
|
119
|
+
Type[bibtexparser.middlewares.BlockMiddleware],
|
|
120
|
+
bibtexparser.middlewares.BlockMiddleware,
|
|
121
|
+
]
|
|
122
|
+
]
|
|
123
|
+
] = None,
|
|
110
124
|
) -> None:
|
|
111
125
|
_middlewares: List[bibtexparser.middlewares.BlockMiddleware] = [
|
|
112
126
|
OutputOnlyDesiredFieldsMiddleware(rule_register=self.rules),
|
|
@@ -230,7 +230,7 @@ class OutputCleanupNoneResultMiddleware(CitationMiddleware):
|
|
|
230
230
|
def transform_entry(self, entry: Entry, *args, **kwargs) -> Optional[Entry]:
|
|
231
231
|
need_to_removed = []
|
|
232
232
|
for field in entry.fields:
|
|
233
|
-
if field.value in [None, "", []]:
|
|
233
|
+
if field.value in [None, "", [], "none"]:
|
|
234
234
|
need_to_removed.append(field)
|
|
235
235
|
for field in need_to_removed:
|
|
236
236
|
entry.pop(field.key, None)
|
|
@@ -1,10 +1,108 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import re
|
|
2
3
|
import frontmatter
|
|
4
|
+
from collections import OrderedDict
|
|
3
5
|
from pathlib import Path
|
|
4
6
|
from typing import Any, Dict, Optional, List, AnyStr, Union
|
|
5
7
|
from copy import deepcopy
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
class ContentSection:
|
|
11
|
+
"""Represents a specific section within a note, containing a title, hierarchy level, and text content.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
title (str): The title of the section (e.g., "Introduction").
|
|
15
|
+
level (int): The heading level of the section (e.g., 1 for #, 2 for ##).
|
|
16
|
+
content (List[str]): A list of strings representing the lines of content in this section.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, title: str, level: int):
|
|
20
|
+
"""Initializes a ContentSection with a title and a heading level.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
title (str): The title of the section.
|
|
24
|
+
level (int): The Markdown heading level (e.g., 1, 2, 3).
|
|
25
|
+
"""
|
|
26
|
+
self.title = title
|
|
27
|
+
self.level = level
|
|
28
|
+
self.content: List[str] = []
|
|
29
|
+
|
|
30
|
+
def add_content(self, content: Union[str, List[str]]):
|
|
31
|
+
"""Appends text content to this section.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
content (Union[str, List[str]]): A single string line or a list of string lines to add.
|
|
35
|
+
"""
|
|
36
|
+
if isinstance(content, list):
|
|
37
|
+
self.content.extend(content)
|
|
38
|
+
else:
|
|
39
|
+
self.content.append(content)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Content:
|
|
43
|
+
"""Represents the body of a note, organized into titled sections.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
sections (Dict[str, ContentSection]): A dictionary mapping section titles to ContentSection objects.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self):
|
|
50
|
+
"""Initializes an empty Content container."""
|
|
51
|
+
self.sections: OrderedDict[str, ContentSection] = OrderedDict()
|
|
52
|
+
|
|
53
|
+
def new_section(self, title: str, level: int):
|
|
54
|
+
"""Creates a new empty section if it does not already exist.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
title (str): The unique title of the section.
|
|
58
|
+
level (int): The Markdown heading level for the section.
|
|
59
|
+
"""
|
|
60
|
+
if title not in self.sections:
|
|
61
|
+
self.sections[title] = ContentSection(title, level)
|
|
62
|
+
|
|
63
|
+
def add_section(self, section: ContentSection):
|
|
64
|
+
"""Adds an existing ContentSection object to the content.
|
|
65
|
+
|
|
66
|
+
If a section with the same title already exists, this operation is ignored.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
section (ContentSection): The section object to add.
|
|
70
|
+
"""
|
|
71
|
+
if section.title not in self.sections.keys():
|
|
72
|
+
self.sections[section.title] = section
|
|
73
|
+
|
|
74
|
+
def add_content(
|
|
75
|
+
self,
|
|
76
|
+
content: Union[str, list],
|
|
77
|
+
section_title: str = "default",
|
|
78
|
+
section_level: int = 1,
|
|
79
|
+
):
|
|
80
|
+
"""Adds content to a specific section, creating the section if it does not exist.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
content (str, list): The text content to add.
|
|
84
|
+
section_title (str, optional): The title of the target section. Defaults to "default".
|
|
85
|
+
section_level (int, optional): The heading level if a new section needs to be created. Defaults to 1.
|
|
86
|
+
"""
|
|
87
|
+
if section_title not in self.sections:
|
|
88
|
+
self.new_section(section_title, section_level)
|
|
89
|
+
self.sections[section_title].add_content(content)
|
|
90
|
+
|
|
91
|
+
def to_string(self) -> str:
|
|
92
|
+
"""Serializes the entire content into a Markdown-formatted string.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
str: The complete Markdown content with section headers.
|
|
96
|
+
"""
|
|
97
|
+
markdown_lines: List[str] = []
|
|
98
|
+
for section in self.sections.values():
|
|
99
|
+
if section.title != "default":
|
|
100
|
+
markdown_lines.append(f"{'#' * section.level} {section.title}")
|
|
101
|
+
markdown_lines.extend(section.content)
|
|
102
|
+
markdown_lines.append("") # Add a blank line after each section
|
|
103
|
+
return "\n".join(markdown_lines).strip()
|
|
104
|
+
|
|
105
|
+
|
|
8
106
|
class MarkdownDocument:
|
|
9
107
|
"""
|
|
10
108
|
A low-level class for manipulating Markdown files with front matter.
|
|
@@ -435,6 +533,52 @@ class MarkdownDocument:
|
|
|
435
533
|
self.post.content = post.content
|
|
436
534
|
self.post.metadata = deepcopy(post.metadata)
|
|
437
535
|
|
|
536
|
+
def parse_content(self) -> Content:
|
|
537
|
+
"""Parses the raw note content into a structured Content object.
|
|
538
|
+
|
|
539
|
+
Iterates through the raw text line by line, identifying Markdown headers
|
|
540
|
+
(e.g., '# Title') to delimit sections. Text found before the first header
|
|
541
|
+
is assigned to a 'default' section.
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
Content: An object containing the parsed sections, their hierarchy levels,
|
|
545
|
+
and associated text content.
|
|
546
|
+
"""
|
|
547
|
+
lines = self.content.split("\n")
|
|
548
|
+
current_section = "default"
|
|
549
|
+
current_head_level = 1
|
|
550
|
+
content_buffer: List[str] = []
|
|
551
|
+
new_content = Content()
|
|
552
|
+
|
|
553
|
+
for line in lines:
|
|
554
|
+
header_match = re.match(r"^(#+)\s+(.+)$", line)
|
|
555
|
+
|
|
556
|
+
if header_match:
|
|
557
|
+
if len(content_buffer) > 0:
|
|
558
|
+
new_content.add_content(
|
|
559
|
+
content="\n".join(content_buffer).strip(),
|
|
560
|
+
section_title=current_section,
|
|
561
|
+
section_level=current_head_level,
|
|
562
|
+
)
|
|
563
|
+
content_buffer = []
|
|
564
|
+
|
|
565
|
+
current_head_level = len(header_match.group(1))
|
|
566
|
+
current_section = header_match.group(2).strip()
|
|
567
|
+
|
|
568
|
+
new_content.new_section(title=current_section, level=current_head_level)
|
|
569
|
+
else:
|
|
570
|
+
if line.strip() or len(content_buffer) > 0:
|
|
571
|
+
content_buffer.append(line)
|
|
572
|
+
|
|
573
|
+
if len(content_buffer) > 0:
|
|
574
|
+
new_content.add_content(
|
|
575
|
+
content="\n".join(content_buffer).strip(),
|
|
576
|
+
section_title=current_section,
|
|
577
|
+
section_level=current_head_level,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
return new_content
|
|
581
|
+
|
|
438
582
|
def clear_content(self) -> None:
|
|
439
583
|
"""
|
|
440
584
|
Clears all Markdown content, leaving only the front matter.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional, Union
|
|
3
3
|
from ures.timedate import time_now
|
|
4
4
|
from ures.string import zettelkasten_id
|
|
5
|
-
from .manipulator import MarkdownDocument, frontmatter
|
|
5
|
+
from .manipulator import MarkdownDocument, frontmatter, Content
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Zettelkasten(MarkdownDocument):
|
|
@@ -62,7 +62,7 @@ class Zettelkasten(MarkdownDocument):
|
|
|
62
62
|
super().__init__(metadata=_metadata)
|
|
63
63
|
|
|
64
64
|
@classmethod
|
|
65
|
-
def from_file(cls, file_path: str) -> "MarkdownDocument":
|
|
65
|
+
def from_file(cls, file_path: str) -> Union["MarkdownDocument", "Zettelkasten"]:
|
|
66
66
|
"""
|
|
67
67
|
Creates a MarkdownDocument instance by loading a Markdown file.
|
|
68
68
|
|
|
@@ -163,3 +163,75 @@ class Zettelkasten(MarkdownDocument):
|
|
|
163
163
|
|
|
164
164
|
def remove_alias(self, alias: str):
|
|
165
165
|
self.aliases.remove(alias)
|
|
166
|
+
|
|
167
|
+
def to_llm_friendly_content(
|
|
168
|
+
self, prop_ignores: Optional[list] = None, context: Optional[Content] = None
|
|
169
|
+
) -> Content:
|
|
170
|
+
"""Generates an LLM-friendly representation of the note, filtering metadata and merging context.
|
|
171
|
+
|
|
172
|
+
This method creates a structured content object designed for LLM consumption. It first
|
|
173
|
+
adds a 'Metadata' section (filtering out specified keys), then appends the note's
|
|
174
|
+
main body, and finally merges any external context provided.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
prop_ignores (Optional[List[str]]): A list of metadata keys to exclude from the output.
|
|
178
|
+
Defaults to None. The keys "aliases", "url", "id", and "title" are always ignored
|
|
179
|
+
by default.
|
|
180
|
+
context (Optional[Content]): Additional contextual content to append to the note.
|
|
181
|
+
Defaults to None.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Content: A new Content object organized into 'Metadata', 'Main Content', and
|
|
185
|
+
optionally 'Context' sections.
|
|
186
|
+
"""
|
|
187
|
+
properties = self.metadata
|
|
188
|
+
content = Content()
|
|
189
|
+
# Create a new section for metadata
|
|
190
|
+
properties_ignore_list = ["aliases", "url", "id", "title"]
|
|
191
|
+
if prop_ignores:
|
|
192
|
+
properties_ignore_list.extend(prop_ignores)
|
|
193
|
+
for key, value in properties.items():
|
|
194
|
+
if key not in properties_ignore_list:
|
|
195
|
+
if isinstance(value, list):
|
|
196
|
+
value = ", ".join(value)
|
|
197
|
+
else:
|
|
198
|
+
value = str(value)
|
|
199
|
+
|
|
200
|
+
content.add_content(
|
|
201
|
+
content=f"**{key}**: {value}",
|
|
202
|
+
section_title="Metadata",
|
|
203
|
+
section_level=1,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Merge all existing sections into the main body
|
|
207
|
+
body_title = "Main Content"
|
|
208
|
+
body_level = 1
|
|
209
|
+
for key, value in self.parse_content().sections.items():
|
|
210
|
+
content.add_content(
|
|
211
|
+
content=f"**{key}**:",
|
|
212
|
+
section_title=body_title,
|
|
213
|
+
section_level=body_level,
|
|
214
|
+
)
|
|
215
|
+
content.add_content(
|
|
216
|
+
content=value.content,
|
|
217
|
+
section_title=body_title,
|
|
218
|
+
section_level=body_level,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# If context is provided, merge it
|
|
222
|
+
if context:
|
|
223
|
+
context_title = "Context"
|
|
224
|
+
context_level = 2
|
|
225
|
+
for key, value in context.sections.items():
|
|
226
|
+
content.add_content(
|
|
227
|
+
content=f"**{key}**:",
|
|
228
|
+
section_title=context_title,
|
|
229
|
+
section_level=context_level,
|
|
230
|
+
)
|
|
231
|
+
content.add_content(
|
|
232
|
+
content=value.content,
|
|
233
|
+
section_title=context_title,
|
|
234
|
+
section_level=context_level,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return content
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|