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.
Files changed (47) hide show
  1. {ures-2.2.1 → ures-2.3.0}/PKG-INFO +1 -1
  2. {ures-2.2.1 → ures-2.3.0}/pyproject.toml +1 -1
  3. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/__init__.py +8 -1
  4. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/manager.py +16 -2
  5. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/middlewares.py +1 -1
  6. ures-2.3.0/ures/markdown/__init__.py +4 -0
  7. {ures-2.2.1 → ures-2.3.0}/ures/markdown/manipulator.py +144 -0
  8. {ures-2.2.1 → ures-2.3.0}/ures/markdown/zettelkasten.py +75 -3
  9. ures-2.2.1/ures/markdown/__init__.py +0 -4
  10. {ures-2.2.1 → ures-2.3.0}/LICENSE +0 -0
  11. {ures-2.2.1 → ures-2.3.0}/docs/index.md +0 -0
  12. {ures-2.2.1 → ures-2.3.0}/ures/__init__.py +0 -0
  13. {ures-2.2.1 → ures-2.3.0}/ures/data_structure/__init__.py +0 -0
  14. {ures-2.2.1 → ures-2.3.0}/ures/data_structure/bi_directional_links.py +0 -0
  15. {ures-2.2.1 → ures-2.3.0}/ures/data_structure/tree.py +0 -0
  16. {ures-2.2.1 → ures-2.3.0}/ures/docker/__init__.py +0 -0
  17. {ures-2.2.1 → ures-2.3.0}/ures/docker/cleanup.py +0 -0
  18. {ures-2.2.1 → ures-2.3.0}/ures/docker/conf.py +0 -0
  19. {ures-2.2.1 → ures-2.3.0}/ures/docker/container.py +0 -0
  20. {ures-2.2.1 → ures-2.3.0}/ures/docker/containers.py +0 -0
  21. {ures-2.2.1 → ures-2.3.0}/ures/docker/image.py +0 -0
  22. {ures-2.2.1 → ures-2.3.0}/ures/docker/runtime.py +0 -0
  23. {ures-2.2.1 → ures-2.3.0}/ures/files.py +0 -0
  24. {ures-2.2.1 → ures-2.3.0}/ures/literature/__init__.py +0 -0
  25. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/README.md +0 -0
  26. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/extractors.py +0 -0
  27. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/__init__.py +0 -0
  28. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/acm.py +0 -0
  29. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/basic.py +0 -0
  30. {ures-2.2.1 → ures-2.3.0}/ures/literature/citation/rules/data_type.py +0 -0
  31. {ures-2.2.1 → ures-2.3.0}/ures/literature/search/README.md +0 -0
  32. {ures-2.2.1 → ures-2.3.0}/ures/literature/search/__init__.py +0 -0
  33. {ures-2.2.1 → ures-2.3.0}/ures/literature/search/adapters.py +0 -0
  34. {ures-2.2.1 → ures-2.3.0}/ures/literature/search/paper.py +0 -0
  35. {ures-2.2.1 → ures-2.3.0}/ures/literature/search/search.py +0 -0
  36. {ures-2.2.1 → ures-2.3.0}/ures/literature/search_cli.py +0 -0
  37. {ures-2.2.1 → ures-2.3.0}/ures/memory/__init__.py +0 -0
  38. {ures-2.2.1 → ures-2.3.0}/ures/memory/allocator.py +0 -0
  39. {ures-2.2.1 → ures-2.3.0}/ures/memory/blocks.py +0 -0
  40. {ures-2.2.1 → ures-2.3.0}/ures/memory/smoke_test.py +0 -0
  41. {ures-2.2.1 → ures-2.3.0}/ures/network.py +0 -0
  42. {ures-2.2.1 → ures-2.3.0}/ures/secrets.py +0 -0
  43. {ures-2.2.1 → ures-2.3.0}/ures/string.py +0 -0
  44. {ures-2.2.1 → ures-2.3.0}/ures/timedate.py +0 -0
  45. {ures-2.2.1 → ures-2.3.0}/ures/tools/__init__.py +0 -0
  46. {ures-2.2.1 → ures-2.3.0}/ures/tools/decorator.py +0 -0
  47. {ures-2.2.1 → ures-2.3.0}/ures/tools/enum.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ures
3
- Version: 2.2.1
3
+ Version: 2.3.0
4
4
  Summary: The project is designed to provide standard utilization functions for improving the reusability of frequently used functions.
5
5
  License: MIT
6
6
  Keywords: utilization,Research Tool,Python
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ures"
3
- version = "2.2.1"
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[List[Type[CitationMiddleware]]] = None,
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[List[Type[CitationMiddleware]]] = None,
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[List[Type[CitationMiddleware]]] = None,
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)
@@ -0,0 +1,4 @@
1
+ from .manipulator import MarkdownDocument, Content, ContentSection
2
+ from .zettelkasten import Zettelkasten
3
+
4
+ __all__ = ["MarkdownDocument", "Zettelkasten", "Content", "ContentSection"]
@@ -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
@@ -1,4 +0,0 @@
1
- from .manipulator import MarkdownDocument
2
- from .zettelkasten import Zettelkasten
3
-
4
- __all__ = ["MarkdownDocument", "Zettelkasten"]
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