notionary 0.2.10__py3-none-any.whl → 0.2.11__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 (56) hide show
  1. notionary/__init__.py +6 -0
  2. notionary/database/database_discovery.py +1 -1
  3. notionary/database/notion_database.py +5 -4
  4. notionary/database/notion_database_factory.py +10 -5
  5. notionary/elements/audio_element.py +2 -2
  6. notionary/elements/bookmark_element.py +2 -2
  7. notionary/elements/bulleted_list_element.py +2 -2
  8. notionary/elements/callout_element.py +2 -2
  9. notionary/elements/code_block_element.py +2 -2
  10. notionary/elements/column_element.py +51 -44
  11. notionary/elements/divider_element.py +2 -2
  12. notionary/elements/embed_element.py +2 -2
  13. notionary/elements/heading_element.py +3 -3
  14. notionary/elements/image_element.py +2 -2
  15. notionary/elements/mention_element.py +2 -2
  16. notionary/elements/notion_block_element.py +36 -0
  17. notionary/elements/numbered_list_element.py +2 -2
  18. notionary/elements/paragraph_element.py +2 -2
  19. notionary/elements/qoute_element.py +2 -2
  20. notionary/elements/table_element.py +2 -2
  21. notionary/elements/text_inline_formatter.py +23 -1
  22. notionary/elements/todo_element.py +2 -2
  23. notionary/elements/toggle_element.py +2 -2
  24. notionary/elements/toggleable_heading_element.py +2 -2
  25. notionary/elements/video_element.py +2 -2
  26. notionary/notion_client.py +1 -1
  27. notionary/page/content/notion_page_content_chunker.py +1 -1
  28. notionary/page/content/page_content_retriever.py +1 -1
  29. notionary/page/content/page_content_writer.py +3 -3
  30. notionary/page/{markdown_to_notion_converter.py → formatting/markdown_to_notion_converter.py} +44 -140
  31. notionary/page/formatting/spacer_rules.py +483 -0
  32. notionary/page/metadata/metadata_editor.py +1 -1
  33. notionary/page/metadata/notion_icon_manager.py +1 -1
  34. notionary/page/metadata/notion_page_cover_manager.py +1 -1
  35. notionary/page/notion_page.py +1 -1
  36. notionary/page/notion_page_factory.py +161 -22
  37. notionary/page/properites/database_property_service.py +1 -1
  38. notionary/page/properites/page_property_manager.py +1 -1
  39. notionary/page/properites/property_formatter.py +1 -1
  40. notionary/page/properites/property_value_extractor.py +1 -1
  41. notionary/page/relations/notion_page_relation_manager.py +1 -1
  42. notionary/page/relations/notion_page_title_resolver.py +1 -1
  43. notionary/page/relations/page_database_relation.py +1 -1
  44. notionary/prompting/element_prompt_content.py +1 -0
  45. notionary/telemetry/__init__.py +7 -0
  46. notionary/telemetry/telemetry.py +226 -0
  47. notionary/telemetry/track_usage_decorator.py +76 -0
  48. notionary/util/__init__.py +5 -0
  49. notionary/util/logging_mixin.py +3 -0
  50. notionary/util/singleton.py +18 -0
  51. {notionary-0.2.10.dist-info → notionary-0.2.11.dist-info}/METADATA +2 -1
  52. notionary-0.2.11.dist-info/RECORD +67 -0
  53. {notionary-0.2.10.dist-info → notionary-0.2.11.dist-info}/WHEEL +1 -1
  54. notionary-0.2.10.dist-info/RECORD +0 -61
  55. {notionary-0.2.10.dist-info → notionary-0.2.11.dist-info}/licenses/LICENSE +0 -0
  56. {notionary-0.2.10.dist-info → notionary-0.2.11.dist-info}/top_level.txt +0 -0
notionary/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
+ __version__ = "0.2.10"
2
+
1
3
  from .notion_client import NotionClient
2
4
 
3
5
  from .database.notion_database import NotionDatabase
@@ -12,6 +14,10 @@ from .elements.registry.block_registry_builder import (
12
14
  BlockRegistryBuilder,
13
15
  )
14
16
 
17
+ from .telemetry.telemetry import NotionaryTelemetry
18
+
19
+ _telemetry = NotionaryTelemetry()
20
+
15
21
  __all__ = [
16
22
  "NotionClient",
17
23
  "NotionDatabase",
@@ -7,7 +7,7 @@ from typing import (
7
7
  Tuple,
8
8
  )
9
9
  from notionary.notion_client import NotionClient
10
- from notionary.util.logging_mixin import LoggingMixin
10
+ from notionary.util import LoggingMixin
11
11
 
12
12
 
13
13
  class DatabaseDiscovery(LoggingMixin):
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
- import json
3
2
  from typing import Any, AsyncGenerator, Dict, List, Optional
4
3
 
5
4
  from notionary.notion_client import NotionClient
6
5
  from notionary.page.notion_page import NotionPage
6
+ from notionary.telemetry import NotionaryTelemetry
7
7
  from notionary.util.warn_direct_constructor_usage import warn_direct_constructor_usage
8
- from notionary.util.logging_mixin import LoggingMixin
8
+ from notionary.util import LoggingMixin
9
9
  from notionary.util.page_id_utils import format_uuid
10
10
 
11
11
 
@@ -26,10 +26,11 @@ class NotionDatabase(LoggingMixin):
26
26
  token: Optional Notion API token
27
27
  """
28
28
  self.database_id = database_id
29
+ self._telemetry = NotionaryTelemetry.get_instance()
29
30
  self._client = NotionClient(token=token)
30
31
 
31
32
  @classmethod
32
- async def from_database_id(
33
+ def from_database_id(
33
34
  cls, database_id: str, token: Optional[str] = None
34
35
  ) -> NotionDatabase:
35
36
  """
@@ -38,7 +39,7 @@ class NotionDatabase(LoggingMixin):
38
39
  """
39
40
  from notionary.database.notion_database_factory import NotionDatabaseFactory
40
41
 
41
- return await NotionDatabaseFactory.from_database_id(database_id, token)
42
+ return NotionDatabaseFactory.from_database_id(database_id, token)
42
43
 
43
44
  @classmethod
44
45
  async def from_database_name(
@@ -1,4 +1,3 @@
1
- import logging
2
1
  from typing import List, Optional, Dict, Any
3
2
  from difflib import SequenceMatcher
4
3
 
@@ -11,10 +10,13 @@ from notionary.exceptions.database_exceptions import (
11
10
  DatabaseParsingError,
12
11
  NotionDatabaseException,
13
12
  )
14
- from notionary.util.logging_mixin import LoggingMixin
15
- from notionary.util.page_id_utils import format_uuid
13
+ from notionary.telemetry import track_usage
14
+ from notionary.util import LoggingMixin
15
+ from notionary.util import format_uuid
16
+ from notionary.util import singleton
16
17
 
17
18
 
19
+ @singleton
18
20
  class NotionDatabaseFactory(LoggingMixin):
19
21
  """
20
22
  Factory class for creating NotionDatabaseManager instances.
@@ -22,7 +24,8 @@ class NotionDatabaseFactory(LoggingMixin):
22
24
  """
23
25
 
24
26
  @classmethod
25
- async def from_database_id(
27
+ @track_usage('page_factory_method_used', {'method': 'from_page_id'})
28
+ def from_database_id(
26
29
  cls, database_id: str, token: Optional[str] = None
27
30
  ) -> NotionDatabase:
28
31
  """
@@ -35,6 +38,7 @@ class NotionDatabaseFactory(LoggingMixin):
35
38
  Returns:
36
39
  An initialized NotionDatabaseManager instance
37
40
  """
41
+
38
42
  try:
39
43
  formatted_id = format_uuid(database_id) or database_id
40
44
 
@@ -55,6 +59,7 @@ class NotionDatabaseFactory(LoggingMixin):
55
59
  raise DatabaseConnectionError(error_msg) from e
56
60
 
57
61
  @classmethod
62
+ @track_usage('page_factory_method_used', {'method': 'from_url'})
58
63
  async def from_database_name(
59
64
  cls, database_name: str, token: Optional[str] = None
60
65
  ) -> NotionDatabase:
@@ -72,7 +77,7 @@ class NotionDatabaseFactory(LoggingMixin):
72
77
  cls.logger.debug("Searching for database with name: %s", database_name)
73
78
 
74
79
  client = NotionClient(token=token)
75
-
80
+
76
81
  try:
77
82
  cls.logger.debug("Using search endpoint to find databases")
78
83
 
@@ -1,12 +1,12 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
 
9
-
9
+ @auto_track_conversions
10
10
  class AudioElement(NotionBlockElement):
11
11
  """
12
12
  Handles conversion between Markdown audio embeds and Notion audio blocks.
@@ -1,13 +1,13 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement
4
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
5
  from notionary.prompting.element_prompt_content import (
6
6
  ElementPromptBuilder,
7
7
  ElementPromptContent,
8
8
  )
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class BookmarkElement(NotionBlockElement):
12
12
  """
13
13
  Handles conversion between Markdown bookmarks and Notion bookmark blocks.
@@ -1,13 +1,13 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
  from notionary.elements.text_inline_formatter import TextInlineFormatter
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class BulletedListElement(NotionBlockElement):
12
12
  """Class for converting between Markdown bullet lists and Notion bulleted list items."""
13
13
 
@@ -6,9 +6,9 @@ from notionary.prompting.element_prompt_content import (
6
6
  ElementPromptContent,
7
7
  )
8
8
  from notionary.elements.text_inline_formatter import TextInlineFormatter
9
- from notionary.elements.notion_block_element import NotionBlockElement
10
-
9
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
11
10
 
11
+ @auto_track_conversions
12
12
  class CalloutElement(NotionBlockElement):
13
13
  """
14
14
  Handles conversion between Markdown callouts and Notion callout blocks.
@@ -1,12 +1,12 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
 
9
-
9
+ @auto_track_conversions
10
10
  class CodeBlockElement(NotionBlockElement):
11
11
  """
12
12
  Handles conversion between Markdown code blocks and Notion code blocks.
@@ -1,10 +1,14 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple, Callable
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement
5
- from notionary.prompting.element_prompt_content import ElementPromptBuilder, ElementPromptContent
6
-
7
-
4
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
+ from notionary.page.formatting.spacer_rules import SPACER_MARKER
6
+ from notionary.prompting.element_prompt_content import (
7
+ ElementPromptBuilder,
8
+ ElementPromptContent,
9
+ )
10
+
11
+ @auto_track_conversions
8
12
  class ColumnElement(NotionBlockElement):
9
13
  """
10
14
  Handles conversion between custom Markdown column syntax and Notion column blocks.
@@ -252,7 +256,7 @@ class ColumnElement(NotionBlockElement):
252
256
  """
253
257
  if not (in_column and column_content):
254
258
  return
255
-
259
+
256
260
  processed_content = ColumnElement._preprocess_column_content(column_content)
257
261
 
258
262
  column_blocks = converter_callback("\n".join(processed_content))
@@ -265,43 +269,45 @@ class ColumnElement(NotionBlockElement):
265
269
  def is_multiline(cls) -> bool:
266
270
  """Column blocks span multiple lines."""
267
271
  return True
268
-
272
+
269
273
  @staticmethod
270
274
  def _preprocess_column_content(lines: List[str]) -> List[str]:
271
275
  """
272
276
  Preprocess column content to handle special cases like first headings.
273
-
277
+
274
278
  This removes any spacer markers that might have been added before the first
275
279
  heading in a column, as each column should have its own heading context.
276
-
280
+
277
281
  Args:
278
282
  lines: The lines of content for the column
279
-
283
+
280
284
  Returns:
281
285
  Processed lines ready for conversion
282
286
  """
283
- from notionary.page.markdown_to_notion_converter import MarkdownToNotionConverter
284
-
285
287
  processed_lines = []
286
288
  found_first_heading = False
287
- spacer_marker = MarkdownToNotionConverter.SPACER_MARKER
288
-
289
+
289
290
  i = 0
290
291
  while i < len(lines):
291
292
  line = lines[i]
292
-
293
+
293
294
  # Check if this is a heading line
294
295
  if re.match(r"^(#{1,6})\s+(.+)$", line.strip()):
295
296
  # If it's the first heading, look ahead to check for spacer
296
- if not found_first_heading and i > 0 and processed_lines and processed_lines[-1] == spacer_marker:
297
+ if (
298
+ not found_first_heading
299
+ and i > 0
300
+ and processed_lines
301
+ and processed_lines[-1] == SPACER_MARKER
302
+ ):
297
303
  # Remove spacer before first heading in column
298
304
  processed_lines.pop()
299
-
305
+
300
306
  found_first_heading = True
301
-
307
+
302
308
  processed_lines.append(line)
303
309
  i += 1
304
-
310
+
305
311
  return processed_lines
306
312
 
307
313
  @classmethod
@@ -331,30 +337,31 @@ class ColumnElement(NotionBlockElement):
331
337
  ":::\n"
332
338
  ":::"
333
339
  )
334
- .with_examples([
335
- "::: columns\n"
336
- "::: column\n"
337
- "## Features\n"
338
- "- Fast response time\n"
339
- "- Intuitive interface\n"
340
- "- Regular updates\n"
341
- ":::\n"
342
- "::: column\n"
343
- "## Benefits\n"
344
- "- Increased productivity\n"
345
- "- Better collaboration\n"
346
- "- Simplified workflows\n"
347
- ":::\n"
348
- ":::",
349
-
350
- "::: columns\n"
351
- "::: column\n"
352
- "![Image placeholder](/api/placeholder/400/320)\n"
353
- ":::\n"
354
- "::: column\n"
355
- "This text appears next to the image, creating a media-with-caption style layout.\n"
356
- ":::\n"
357
- ":::"
358
- ])
340
+ .with_examples(
341
+ [
342
+ "::: columns\n"
343
+ "::: column\n"
344
+ "## Features\n"
345
+ "- Fast response time\n"
346
+ "- Intuitive interface\n"
347
+ "- Regular updates\n"
348
+ ":::\n"
349
+ "::: column\n"
350
+ "## Benefits\n"
351
+ "- Increased productivity\n"
352
+ "- Better collaboration\n"
353
+ "- Simplified workflows\n"
354
+ ":::\n"
355
+ ":::",
356
+ "::: columns\n"
357
+ "::: column\n"
358
+ "![Image placeholder](/api/placeholder/400/320)\n"
359
+ ":::\n"
360
+ "::: column\n"
361
+ "This text appears next to the image, creating a media-with-caption style layout.\n"
362
+ ":::\n"
363
+ ":::",
364
+ ]
365
+ )
359
366
  .build()
360
- )
367
+ )
@@ -1,13 +1,13 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement
4
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
5
  from notionary.prompting.element_prompt_content import (
6
6
  ElementPromptBuilder,
7
7
  ElementPromptContent,
8
8
  )
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class DividerElement(NotionBlockElement):
12
12
  """
13
13
  Handles conversion between Markdown horizontal dividers and Notion divider blocks.
@@ -1,12 +1,12 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
 
9
-
9
+ @auto_track_conversions
10
10
  class EmbedElement(NotionBlockElement):
11
11
  """
12
12
  Handles conversion between Markdown embeds and Notion embed blocks.
@@ -1,14 +1,14 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement
4
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
5
  from notionary.prompting.element_prompt_content import (
6
6
  ElementPromptBuilder,
7
7
  ElementPromptContent,
8
8
  )
9
9
  from notionary.elements.text_inline_formatter import TextInlineFormatter
10
10
 
11
-
11
+ @auto_track_conversions
12
12
  class HeadingElement(NotionBlockElement):
13
13
  """Handles conversion between Markdown headings and Notion heading blocks."""
14
14
 
@@ -91,4 +91,4 @@ class HeadingElement(NotionBlockElement):
91
91
  .with_syntax("## Your Heading Text")
92
92
  .with_standard_markdown()
93
93
  .build()
94
- )
94
+ )
@@ -1,12 +1,12 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
 
9
-
9
+ @auto_track_conversions
10
10
  class ImageElement(NotionBlockElement):
11
11
  """
12
12
  Handles conversion between Markdown images and Notion image blocks.
@@ -2,13 +2,13 @@ import re
2
2
  from typing import Dict, Any, Optional, List
3
3
  from typing_extensions import override
4
4
 
5
- from notionary.elements.notion_block_element import NotionBlockElement
5
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
6
6
  from notionary.prompting.element_prompt_content import (
7
7
  ElementPromptBuilder,
8
8
  ElementPromptContent,
9
9
  )
10
10
 
11
-
11
+ @auto_track_conversions
12
12
  class MentionElement(NotionBlockElement):
13
13
  """
14
14
  Handles conversion between Markdown mentions and Notion mention elements.
@@ -1,7 +1,9 @@
1
+ from functools import wraps
1
2
  from typing import Dict, Any, Optional
2
3
  from abc import ABC
3
4
 
4
5
  from notionary.prompting.element_prompt_content import ElementPromptContent
6
+ from notionary.telemetry import track_usage
5
7
 
6
8
 
7
9
  class NotionBlockElement(ABC):
@@ -32,3 +34,37 @@ class NotionBlockElement(ABC):
32
34
  @classmethod
33
35
  def get_llm_prompt_content(cls) -> ElementPromptContent:
34
36
  """Returns a dictionary with information for LLM prompts about this element."""
37
+
38
+
39
+ def auto_track_conversions(cls):
40
+ """
41
+ Decorator der sich auch auf Subklassen vererbt.
42
+ """
43
+ conversion_methods = ['markdown_to_notion', 'notion_to_markdown']
44
+
45
+ original_init_subclass = getattr(cls, '__init_subclass__', None)
46
+
47
+ @classmethod
48
+ def __init_subclass__(cls_inner, **kwargs):
49
+ # Original __init_subclass__ aufrufen
50
+ if original_init_subclass:
51
+ original_init_subclass(**kwargs)
52
+
53
+ # Tracking für Subklasse hinzufügen
54
+ for method_name in conversion_methods:
55
+ if hasattr(cls_inner, method_name):
56
+ original_method = getattr(cls_inner, method_name)
57
+
58
+ if isinstance(original_method, classmethod):
59
+ func = original_method.__func__
60
+
61
+ @track_usage(f"{cls_inner.__name__.lower()}_{method_name}")
62
+ @classmethod
63
+ @wraps(func)
64
+ def tracked_method(cls_ref, *args, **kwargs):
65
+ return func(cls_ref, *args, **kwargs)
66
+
67
+ setattr(cls_inner, method_name, tracked_method)
68
+
69
+ cls.__init_subclass__ = __init_subclass__
70
+ return cls
@@ -1,13 +1,13 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
  from notionary.elements.text_inline_formatter import TextInlineFormatter
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class NumberedListElement(NotionBlockElement):
12
12
  """Class for converting between Markdown numbered lists and Notion numbered list items."""
13
13
 
@@ -1,13 +1,13 @@
1
1
  from typing import Dict, Any, Optional
2
2
 
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
  from notionary.elements.text_inline_formatter import TextInlineFormatter
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class ParagraphElement(NotionBlockElement):
12
12
  """Handles conversion between Markdown paragraphs and Notion paragraph blocks."""
13
13
 
@@ -1,12 +1,12 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
 
9
-
9
+ @auto_track_conversions
10
10
  class QuoteElement(NotionBlockElement):
11
11
  """Class for converting between Markdown blockquotes and Notion quote blocks."""
12
12
 
@@ -1,13 +1,13 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.elements.text_inline_formatter import TextInlineFormatter
5
5
  from notionary.prompting.element_prompt_content import (
6
6
  ElementPromptBuilder,
7
7
  ElementPromptContent,
8
8
  )
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class TableElement(NotionBlockElement):
12
12
  """
13
13
  Handles conversion between Markdown tables and Notion table blocks.
@@ -29,6 +29,7 @@ class TextInlineFormatter:
29
29
  (r"~~(.+?)~~", {"strikethrough": True}),
30
30
  (r"`(.+?)`", {"code": True}),
31
31
  (r"\[(.+?)\]\((.+?)\)", {"link": True}),
32
+ (r"@\[([0-9a-f-]+)\]", {"mention": True}),
32
33
  ]
33
34
 
34
35
  @classmethod
@@ -87,11 +88,15 @@ class TextInlineFormatter:
87
88
  cls._create_text_element(remaining_text[:earliest_pos], {})
88
89
  )
89
90
 
90
- elif "link" in earliest_format:
91
+ if "link" in earliest_format:
91
92
  content = earliest_match.group(1)
92
93
  url = earliest_match.group(2)
93
94
  segments.append(cls._create_link_element(content, url))
94
95
 
96
+ elif "mention" in earliest_format:
97
+ id = earliest_match.group(1)
98
+ segments.append(cls._create_mention_element(id))
99
+
95
100
  else:
96
101
  content = earliest_match.group(1)
97
102
  segments.append(cls._create_text_element(content, earliest_format))
@@ -152,6 +157,23 @@ class TextInlineFormatter:
152
157
  "plain_text": text,
153
158
  }
154
159
 
160
+ @classmethod
161
+ def _create_mention_element(cls, id: str) -> Dict[str, Any]:
162
+ """
163
+ Create a Notion mention element.
164
+
165
+ Args:
166
+ id: The page ID
167
+
168
+ Returns:
169
+ Notion rich_text element with mention
170
+ """
171
+ return {
172
+ "type": "mention",
173
+ "mention": {"type": "page", "page": {"id": id}},
174
+ "annotations": cls._default_annotations(),
175
+ }
176
+
155
177
  @classmethod
156
178
  def extract_text_with_formatting(cls, rich_text: List[Dict[str, Any]]) -> str:
157
179
  """
@@ -1,13 +1,13 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
  from notionary.elements.text_inline_formatter import TextInlineFormatter
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class TodoElement(NotionBlockElement):
12
12
  """
13
13
  Handles conversion between Markdown todo items and Notion to_do blocks.
@@ -1,13 +1,13 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple, Callable
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement
4
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
5
  from notionary.prompting.element_prompt_content import (
6
6
  ElementPromptBuilder,
7
7
  ElementPromptContent,
8
8
  )
9
9
 
10
-
10
+ @auto_track_conversions
11
11
  class ToggleElement(NotionBlockElement):
12
12
  """
13
13
  Improved ToggleElement class using pipe syntax instead of indentation.
@@ -1,14 +1,14 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple, Callable
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement
4
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
5
  from notionary.prompting.element_prompt_content import (
6
6
  ElementPromptBuilder,
7
7
  ElementPromptContent,
8
8
  )
9
9
  from notionary.elements.text_inline_formatter import TextInlineFormatter
10
10
 
11
-
11
+ @auto_track_conversions
12
12
  class ToggleableHeadingElement(NotionBlockElement):
13
13
  """Handles conversion between Markdown collapsible headings and Notion toggleable heading blocks with pipe syntax."""
14
14
 
@@ -1,12 +1,12 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List
3
- from notionary.elements.notion_block_element import NotionBlockElement
3
+ from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
4
  from notionary.prompting.element_prompt_content import (
5
5
  ElementPromptBuilder,
6
6
  ElementPromptContent,
7
7
  )
8
8
 
9
-
9
+ @auto_track_conversions
10
10
  class VideoElement(NotionBlockElement):
11
11
  """
12
12
  Handles conversion between Markdown video embeds and Notion video blocks.