From 4a23c57b615c4c32ed5d96f3fdc911b463511821 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 21 Aug 2024 19:49:03 +0200 Subject: [PATCH 01/45] Add converter script from Markdownify --- jsondoc/convert/__init__.py | 0 jsondoc/convert/html.py | 470 ++++++++++++++++++++++++++++++++++++ 2 files changed, 470 insertions(+) create mode 100644 jsondoc/convert/__init__.py create mode 100644 jsondoc/convert/html.py diff --git a/jsondoc/convert/__init__.py b/jsondoc/convert/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py new file mode 100644 index 0000000..b96a9ce --- /dev/null +++ b/jsondoc/convert/html.py @@ -0,0 +1,470 @@ +from bs4 import BeautifulSoup, NavigableString, Comment, Doctype +from textwrap import fill +import re +import six + + +convert_heading_re = re.compile(r"convert_h(\d+)") +line_beginning_re = re.compile(r"^", re.MULTILINE) +whitespace_re = re.compile(r"[\t ]+") +all_whitespace_re = re.compile(r"[\s]+") +html_heading_re = re.compile(r"h[1-6]") + + +# Heading styles +ATX = "atx" +ATX_CLOSED = "atx_closed" +UNDERLINED = "underlined" +SETEXT = UNDERLINED + +# Newline style +SPACES = "spaces" +BACKSLASH = "backslash" + +# Strong and emphasis style +ASTERISK = "*" +UNDERSCORE = "_" + + +def chomp(text): + """ + If the text in an inline tag like b, a, or em contains a leading or trailing + space, strip the string and return a space as suffix of prefix, if needed. + This function is used to prevent conversions like + foo => ** foo** + """ + prefix = " " if text and text[0] == " " else "" + suffix = " " if text and text[-1] == " " else "" + text = text.strip() + return (prefix, suffix, text) + + +def abstract_inline_conversion(markup_fn): + """ + This abstracts all simple inline tags like b, em, del, ... + Returns a function that wraps the chomped text in a pair of the string + that is returned by markup_fn, with '/' inserted in the string used after + the text if it looks like an HTML tag. markup_fn is necessary to allow for + references to self.strong_em_symbol etc. + """ + + def implementation(self, el, text, convert_as_inline): + markup_prefix = markup_fn(self) + if markup_prefix.startswith("<") and markup_prefix.endswith(">"): + markup_suffix = "~#=+|-])", r"\\\1", text) + text = re.sub(r"([0-9])([.)])", r"\1\\\2", text) + if self.options["escape_asterisks"]: + text = text.replace("*", r"\*") + if self.options["escape_underscores"]: + text = text.replace("_", r"\_") + return text + + def indent(self, text, level): + return line_beginning_re.sub("\t" * level, text) if text else "" + + def underline(self, text, pad_char): + text = (text or "").rstrip() + return "%s\n%s\n\n" % (text, pad_char * len(text)) if text else "" + + def convert_a(self, el, text, convert_as_inline): + prefix, suffix, text = chomp(text) + if not text: + return "" + href = el.get("href") + title = el.get("title") + # For the replacement see #29: text nodes underscores are escaped + if ( + self.options["autolinks"] + and text.replace(r"\_", "_") == href + and not title + and not self.options["default_title"] + ): + # Shortcut syntax + return "<%s>" % href + if self.options["default_title"] and not title: + title = href + title_part = ' "%s"' % title.replace('"', r"\"") if title else "" + return ( + "%s[%s](%s%s)%s" % (prefix, text, href, title_part, suffix) + if href + else text + ) + + convert_b = abstract_inline_conversion( + lambda self: 2 * self.options["strong_em_symbol"] + ) + + def convert_blockquote(self, el, text, convert_as_inline): + + if convert_as_inline: + return text + + return ( + "\n" + (line_beginning_re.sub("> ", text.strip()) + "\n\n") if text else "" + ) + + def convert_br(self, el, text, convert_as_inline): + if convert_as_inline: + return "" + + if self.options["newline_style"].lower() == BACKSLASH: + return "\\\n" + else: + return " \n" + + def convert_code(self, el, text, convert_as_inline): + if el.parent.name == "pre": + return text + converter = abstract_inline_conversion(lambda self: "`") + return converter(self, el, text, convert_as_inline) + + convert_del = abstract_inline_conversion(lambda self: "~~") + + convert_em = abstract_inline_conversion( + lambda self: self.options["strong_em_symbol"] + ) + + convert_kbd = convert_code + + def convert_hn(self, n, el, text, convert_as_inline): + if convert_as_inline: + return text + + style = self.options["heading_style"].lower() + text = text.strip() + if style == UNDERLINED and n <= 2: + line = "=" if n == 1 else "-" + return self.underline(text, line) + hashes = "#" * n + if style == ATX_CLOSED: + return "%s %s %s\n\n" % (hashes, text, hashes) + return "%s %s\n\n" % (hashes, text) + + def convert_hr(self, el, text, convert_as_inline): + return "\n\n---\n\n" + + convert_i = convert_em + + def convert_img(self, el, text, convert_as_inline): + alt = el.attrs.get("alt", None) or "" + src = el.attrs.get("src", None) or "" + title = el.attrs.get("title", None) or "" + title_part = ' "%s"' % title.replace('"', r"\"") if title else "" + if ( + convert_as_inline + and el.parent.name not in self.options["keep_inline_images_in"] + ): + return alt + + return "![%s](%s%s)" % (alt, src, title_part) + + def convert_list(self, el, text, convert_as_inline): + + # Converting a list to inline is undefined. + # Ignoring convert_to_inline for list. + + nested = False + before_paragraph = False + if el.next_sibling and el.next_sibling.name not in ["ul", "ol"]: + before_paragraph = True + while el: + if el.name == "li": + nested = True + break + el = el.parent + if nested: + # remove trailing newline if nested + return "\n" + self.indent(text, 1).rstrip() + return text + ("\n" if before_paragraph else "") + + convert_ul = convert_list + convert_ol = convert_list + + def convert_li(self, el, text, convert_as_inline): + parent = el.parent + if parent is not None and parent.name == "ol": + if parent.get("start") and str(parent.get("start")).isnumeric(): + start = int(parent.get("start")) + else: + start = 1 + bullet = "%s." % (start + parent.index(el)) + else: + depth = -1 + while el: + if el.name == "ul": + depth += 1 + el = el.parent + bullets = self.options["bullets"] + bullet = bullets[depth % len(bullets)] + return "%s %s\n" % (bullet, (text or "").strip()) + + def convert_p(self, el, text, convert_as_inline): + if convert_as_inline: + return text + if self.options["wrap"]: + text = fill( + text, + width=self.options["wrap_width"], + break_long_words=False, + break_on_hyphens=False, + ) + return "%s\n\n" % text if text else "" + + def convert_pre(self, el, text, convert_as_inline): + if not text: + return "" + code_language = self.options["code_language"] + + if self.options["code_language_callback"]: + code_language = self.options["code_language_callback"](el) or code_language + + return "\n```%s\n%s\n```\n" % (code_language, text) + + def convert_script(self, el, text, convert_as_inline): + return "" + + def convert_style(self, el, text, convert_as_inline): + return "" + + convert_s = convert_del + + convert_strong = convert_b + + convert_samp = convert_code + + convert_sub = abstract_inline_conversion(lambda self: self.options["sub_symbol"]) + + convert_sup = abstract_inline_conversion(lambda self: self.options["sup_symbol"]) + + def convert_table(self, el, text, convert_as_inline): + return "\n\n" + text + "\n" + + def convert_caption(self, el, text, convert_as_inline): + return text + "\n" + + def convert_figcaption(self, el, text, convert_as_inline): + return "\n\n" + text + "\n\n" + + def convert_td(self, el, text, convert_as_inline): + colspan = 1 + if "colspan" in el.attrs and el["colspan"].isdigit(): + colspan = int(el["colspan"]) + return " " + text.strip().replace("\n", " ") + " |" * colspan + + def convert_th(self, el, text, convert_as_inline): + colspan = 1 + if "colspan" in el.attrs and el["colspan"].isdigit(): + colspan = int(el["colspan"]) + return " " + text.strip().replace("\n", " ") + " |" * colspan + + def convert_tr(self, el, text, convert_as_inline): + cells = el.find_all(["td", "th"]) + is_headrow = ( + all([cell.name == "th" for cell in cells]) + or (not el.previous_sibling and not el.parent.name == "tbody") + or ( + not el.previous_sibling + and el.parent.name == "tbody" + and len(el.parent.parent.find_all(["thead"])) < 1 + ) + ) + overline = "" + underline = "" + if is_headrow and not el.previous_sibling: + # first row and is headline: print headline underline + full_colspan = 0 + for cell in cells: + if "colspan" in cell.attrs and cell["colspan"].isdigit(): + full_colspan += int(cell["colspan"]) + else: + full_colspan += 1 + underline += "| " + " | ".join(["---"] * full_colspan) + " |" + "\n" + elif not el.previous_sibling and ( + el.parent.name == "table" + or (el.parent.name == "tbody" and not el.parent.previous_sibling) + ): + # first row, not headline, and: + # - the parent is table or + # - the parent is tbody at the beginning of a table. + # print empty headline above this row + overline += "| " + " | ".join([""] * len(cells)) + " |" + "\n" + overline += "| " + " | ".join(["---"] * len(cells)) + " |" + "\n" + return overline + "|" + text + "\n" + underline + + +def html_to_jsondoc(html, **options): + return HtmlToJsonDocConverter(**options).convert(html) From da4c811f5db08ddb0779729ee5acdb72703d48b2 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:55:57 +0200 Subject: [PATCH 02/45] Add HTML example --- examples/html_all_elements.html | 467 ++++++++++++++++++++++++++++++++ 1 file changed, 467 insertions(+) create mode 100644 examples/html_all_elements.html diff --git a/examples/html_all_elements.html b/examples/html_all_elements.html new file mode 100644 index 0000000..956af06 --- /dev/null +++ b/examples/html_all_elements.html @@ -0,0 +1,467 @@ + + + + + + + + + HTML Patterns + + + + + + + + + + +
+

HTML

+

Every html element in one place. Just waiting to be styled.

+
+ + + + + +
+

Site title

+ +
+ +
+

Headings & Copy

+

First Header h1

+

+ At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis + praesentium voluptatum deleniti atque corrupti quos. +

+

Second header h2

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat + non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

+

Third header h3

+

+ At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis + praesentium voluptatum deleniti atque corrupti quos dolores et quas + molestias excepturi sint occaecati cupiditate non provident, similique sunt + in culpa qui officia deserunt mollitia animi, id est laborum et dolorum + fuga. Et harum quidem rerum facilis est et expedita distinctio. +

+

Fourth header h4

+

+ Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, + consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt + ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima + veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi + ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit + qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum + qui dolorem eum fugiat quo voluptas nulla pariatur?" +

+
Fifth header h5
+

+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium + doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore + veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim + ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia + consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. +

+
Sixth header h6
+

+ At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis + praesentium voluptatum deleniti atque corrupti quos. +

+
+
+
+

Links

+ Sample text link + Sample button link +
+
+
+

Lists

+

Unordered list

+ +

Ordered list

+
    +
  1. First
  2. +
  3. Second
  4. +
  5. Third
  6. +
  7. Fourth
  8. +
  9. Fifth
  10. +
  11. Sixth
  12. +
+

Definition list

+
+
+ Kick +
+
+ 808 +
+
+ Snare +
+
+ 909 +
+
+
+
Maine
+
Augusta
+
California
+
Sacremento
+
Oregon
+
Salem
+
New York
+
Albany
+
+
+
Ascender
+
The part of certain lowercase letters that extends above the x-height of a font.
+
Font
+
Traditionally, a complete set of characters for one typeface at + one particular type size. Often used more loosely as a synonym for + "typeface". +
+
Golden Section
+
+ The ideal proportion according to the ancient Greeks. It is visualized as the + division of a line into two unequal segments in such a way that the ratio of the + smaller segment to the larger segment is equal to the ratio of the larger to the + whole. It is usually defined as 21:34, that is, 21/34 and 34/(21+34) both equal + approximately 0.618. A rectangle whose sides are of this proportion is called a + "golden rectangle". Golden rectangles can be found in the proportions of the + Parthenon and many medieval manuscripts. +
+
+
+ +
+

Forms

+
+
+ + Legend Example + +
+ + +

Helper text if necessary.

+
+ +
+ + +

Error messages when appropriate.

+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
+ +
+ +
    +
  • +
  • +
  • +
+
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+
+

Buttons

+ + + +
+
+
+

An Example Article

+
+

Title

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat + non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

+
+

+ This is a GREAT pull quote. +

+ - Author +
+

+ Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, + consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt + ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima + veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi + ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit + qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum + qui dolorem eum fugiat quo voluptas nulla pariatur?" +

+

+ At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis + praesentium voluptatum deleniti atque corrupti quos dolores et quas + molestias excepturi sint occaecati cupiditate non provident, similique sunt + in culpa qui officia deserunt mollitia animi, id est laborum et dolorum + fuga. Et harum quidem rerum facilis est et expedita distinctio. +

+
+
+
+ +
+

Code examples

+ +
+      sudo ipfw pipe 1 config bw 256KByte/s
+      sudo ipfw add 1 pipe 1 src-port 3000
+    
+
+
+
+
+

Footer

+ +
+ +
+

New hawtness

+ 80 % +

We are this close to the goal: $824.

+
+ +
+
+

Random Stuff

+
+ This is for things like copyright info + Content that isn't accurate or relevant anymore. + Generic span wrapper + HTML How to meet ladies +

This is inline text with subscript and superscript elements.

+

+ f(x) = a0 + a1x + + a2x2, where a2 ≠ 0 +

+ +
+ +
+
+ Figure Example +
+ Photo of the sky at night. Original by @mrmrs +
+
+
+ +
+ + +
+          /Sites/html master  ☠ ☢
+          $  ls -gto
+
+          total 104
+          -rw-r--r--   1   10779 Jun  5 16:24 index.html
+          -rw-r--r--   1    1255 Jun  5 16:00 _config.yml
+          drwxr-xr-x  11     374 Jun  5 15:57 _site
+          -rw-r--r--   1    1597 Jun  5 14:16 README.md
+          drwxr-xr-x   5     170 Jun  5 14:15 _sass
+          -rw-r--r--   1     564 Jun  4 15:59 Rakefile
+          drwxr-xr-x   6     204 Jun  4 15:59 _includes
+          drwxr-xr-x   4     136 Jun  4 15:59 _layouts
+          drwxr-xr-x   3     102 Jun  4 15:59 _resources
+          drwxr-xr-x   3     102 Jun  4 15:59 css
+          -rw-r--r--   1    1977 Jun  4 15:59 favicon.icns
+          -rw-r--r--   1    6518 Jun  4 15:59 favicon.ico
+          -rw-r--r--   1    1250 Jun  4 15:59 touch-icon-ipad-precomposed.png
+          -rw-r--r--   1    2203 Jun  4 15:59 touch-icon-ipad-retina-precomposed.png
+          -rw-r--r--   1    1046 Jun  4 15:59 touch-icon-iphone-precomposed.png
+          -rw-r--r--   1    1779 Jun  4 15:59 touch-icon-iphone-retina-precomposed.png
+        
+
+
+
+

Tables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
This is a caption for a table
IDNameDateAddress
Table footer info
#999-32acFirst Name13 May, 2013999 Spruce Lane, Somewhere, CA 94101
#888-32ddSample Name17 May, 1984999 Spruce Lane, Somewhere, CA 94101
+
+
+

Footer

+ +
+ + + From 665bba58fe6afa5a1378283b4c888a70c75fe9a9 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:59:49 +0200 Subject: [PATCH 03/45] Minor --- examples/html_all_elements.html | 346 +++++++++++++++++--------------- 1 file changed, 182 insertions(+), 164 deletions(-) diff --git a/examples/html_all_elements.html b/examples/html_all_elements.html index 956af06..7536ce3 100644 --- a/examples/html_all_elements.html +++ b/examples/html_all_elements.html @@ -1,16 +1,15 @@ - - - + + - HTML Patterns + HTML Patterns - - - + + + - -
+      
+        
           /Sites/html master  ☠ ☢
           $  ls -gto
 
@@ -406,7 +423,7 @@ 

Random Stuff

Tables

- - + @@ -464,4 +483,3 @@

Footer

- From f5aff3bbfc73c1dcd0f37dc56435dd7a28fcb76c Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:08:23 +0200 Subject: [PATCH 04/45] Add html converstion test, wip --- examples/{ => html}/html_all_elements.html | 0 examples/{ => notion}/notion_example_page1.json | 0 examples/{ => notion}/notion_example_page2.json | 0 examples/{ => notion}/notion_example_page3.json | 0 examples/{ => notion}/notion_fetch_page.py | 0 tests/test_html_to_jsondoc.py | 14 ++++++++++++++ 6 files changed, 14 insertions(+) rename examples/{ => html}/html_all_elements.html (100%) rename examples/{ => notion}/notion_example_page1.json (100%) rename examples/{ => notion}/notion_example_page2.json (100%) rename examples/{ => notion}/notion_example_page3.json (100%) rename examples/{ => notion}/notion_fetch_page.py (100%) create mode 100644 tests/test_html_to_jsondoc.py diff --git a/examples/html_all_elements.html b/examples/html/html_all_elements.html similarity index 100% rename from examples/html_all_elements.html rename to examples/html/html_all_elements.html diff --git a/examples/notion_example_page1.json b/examples/notion/notion_example_page1.json similarity index 100% rename from examples/notion_example_page1.json rename to examples/notion/notion_example_page1.json diff --git a/examples/notion_example_page2.json b/examples/notion/notion_example_page2.json similarity index 100% rename from examples/notion_example_page2.json rename to examples/notion/notion_example_page2.json diff --git a/examples/notion_example_page3.json b/examples/notion/notion_example_page3.json similarity index 100% rename from examples/notion_example_page3.json rename to examples/notion/notion_example_page3.json diff --git a/examples/notion_fetch_page.py b/examples/notion/notion_fetch_page.py similarity index 100% rename from examples/notion_fetch_page.py rename to examples/notion/notion_fetch_page.py diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py new file mode 100644 index 0000000..f0f5cb9 --- /dev/null +++ b/tests/test_html_to_jsondoc.py @@ -0,0 +1,14 @@ +from jsondoc.convert.html import html_to_jsondoc + +def test_convert_html_all_elements(): + path = "examples/html/html_all_elements.html" + + content = open(path, "r").read() + + ret = html_to_jsondoc(content) + + import ipdb; ipdb.set_trace() + + +if __name__ == "__main__": + test_convert_html_all_elements() \ No newline at end of file From 833b623766eed1e5f8dc1daef7fafb3b75cba701 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:26:00 +0200 Subject: [PATCH 05/45] Add nested paragraphs --- examples/notion/notion_example_page3.json | 724 +++- schema/page/ex1_success.json | 4052 ++++++++++++++++++++- schema/page/ex2_success.json | 3363 +---------------- tests/run_serialization_tests.py | 2 +- 4 files changed, 4772 insertions(+), 3369 deletions(-) diff --git a/examples/notion/notion_example_page3.json b/examples/notion/notion_example_page3.json index b9dbf25..b957d3a 100644 --- a/examples/notion/notion_example_page3.json +++ b/examples/notion/notion_example_page3.json @@ -1,6 +1,6 @@ { "id": "8d7dbc6b5c554589826c1352450db04e", - "type": "page", + "object": "page", "properties": { "title": { "id": "title", @@ -80,7 +80,7 @@ "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" }, "created_time": "2024-05-28T20:27:00.000Z", - "last_edited_time": "2024-05-28T20:27:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", "created_by": { "object": "user", "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" @@ -116,6 +116,720 @@ "color": "default" } }, + { + "object": "block", + "id": "8ca44ada-7f26-4f9d-b6dd-f336e64ee714", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-09-03T16:20:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [], + "color": "default" + } + }, + { + "object": "block", + "id": "c4c4d5cf-7358-4f1e-a8d5-0b433b1cce35", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-09-03T16:20:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Top level paragraph", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Top level paragraph", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "91044001-e5e3-4165-bca0-90eae8eedd5e", + "parent": { + "type": "block_id", + "block_id": "c4c4d5cf-7358-4f1e-a8d5-0b433b1cce35" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 1", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "c0302421-6a1f-461d-8a56-91e585fb5e54", + "parent": { + "type": "block_id", + "block_id": "91044001-e5e3-4165-bca0-90eae8eedd5e" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 2", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "37113c94-4284-470c-8177-c1ed49dff264", + "parent": { + "type": "block_id", + "block_id": "c0302421-6a1f-461d-8a56-91e585fb5e54" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 3", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 3", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "87c5c1bb-de05-495c-8e97-86a17906b87d", + "parent": { + "type": "block_id", + "block_id": "37113c94-4284-470c-8177-c1ed49dff264" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 4", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 4", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "42adb363-d88f-4b51-b611-47a2b99157cd", + "parent": { + "type": "block_id", + "block_id": "87c5c1bb-de05-495c-8e97-86a17906b87d" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 5", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 5", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "58acde88-f2ab-400c-8d01-0f24b588b027", + "parent": { + "type": "block_id", + "block_id": "42adb363-d88f-4b51-b611-47a2b99157cd" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 6", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 6", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81", + "parent": { + "type": "block_id", + "block_id": "58acde88-f2ab-400c-8d01-0f24b588b027" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 7", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 7", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3", + "parent": { + "type": "block_id", + "block_id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 8", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 8", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69", + "parent": { + "type": "block_id", + "block_id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:19:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 9", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 9", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "ea4a318c-f324-417b-a2ae-5718408da4a4", + "parent": { + "type": "block_id", + "block_id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 10", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 10", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb", + "parent": { + "type": "block_id", + "block_id": "ea4a318c-f324-417b-a2ae-5718408da4a4" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 11", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 11", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "43f2148e-f780-4e48-a2a3-e38984b02eaa", + "parent": { + "type": "block_id", + "block_id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb" + }, + "created_time": "2024-09-03T16:19:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 12", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 12", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "de6c1aee-ab7c-4787-9c25-72168048365c", + "parent": { + "type": "block_id", + "block_id": "43f2148e-f780-4e48-a2a3-e38984b02eaa" + }, + "created_time": "2024-09-03T16:20:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 13", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 13", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "faf3ccaa-628f-431c-9c85-b752953396fe", + "parent": { + "type": "block_id", + "block_id": "de6c1aee-ab7c-4787-9c25-72168048365c" + }, + "created_time": "2024-09-03T16:20:00.000Z", + "last_edited_time": "2024-09-03T16:20:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 14", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 14", + "href": null + } + ], + "color": "default" + } + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, { "object": "block", "id": "b7ada960-5e53-408f-93cc-c7444dc90ce0", @@ -428,7 +1142,7 @@ }, { "id": "d185db98-63f3-4f54-aa26-a41596e0fc15", - "type": "page", + "object": "page", "properties": { "title": { "id": "title", @@ -2475,8 +3189,8 @@ ], "type": "file", "file": { - "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/d6000e68-0a06-463d-8914-d6dfe33f31b9/3141b657-2f3a-425a-9b44-d61df64d63e0/TextCortex_%282024-08-07_13_08_56%29_Create_a_drawing_of_a_scenic_v.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240809%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240809T121557Z&X-Amz-Expires=3600&X-Amz-Signature=287d787db70873c68fd020453dffd592faa076352b40dabe5bb711e6b0ae5208&X-Amz-SignedHeaders=host&x-id=GetObject", - "expiry_time": "2024-08-09T13:15:57.161Z" + "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/d6000e68-0a06-463d-8914-d6dfe33f31b9/3141b657-2f3a-425a-9b44-d61df64d63e0/TextCortex_%282024-08-07_13_08_56%29_Create_a_drawing_of_a_scenic_v.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240903%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240903T162140Z&X-Amz-Expires=3600&X-Amz-Signature=38921301197444644e5e50976473036491e5ffa78b8b30efb06eb8b3a84f0329&X-Amz-SignedHeaders=host&x-id=GetObject", + "expiry_time": "2024-09-03T17:21:40.606Z" } } }, diff --git a/schema/page/ex1_success.json b/schema/page/ex1_success.json index 0a9bd83..b4a1b21 100644 --- a/schema/page/ex1_success.json +++ b/schema/page/ex1_success.json @@ -1,8 +1,8 @@ { "object": "page", "id": "be633bf1-dfa0-436d-b259-571129a590e5", - "created_time": "2022-10-24T22:54:00.000Z", - "last_edited_time": "2023-03-08T18:25:00.000Z", + "created_time": "2022-10-24T22:54:00Z", + "last_edited_time": "2023-03-08T18:25:00Z", "created_by": { "object": "user", "id": "c2f20311-9e54-4d11-8c79-7398424ae41e" @@ -15,6 +15,8 @@ "type": "emoji", "emoji": "🐞" }, + "archived": false, + "in_trash": false, "properties": { "title": { "id": "title", @@ -40,5 +42,4049 @@ ] } }, - "children": [] + "children": [ + { + "object": "block", + "id": "7bebf9ce-b1ee-4415-b01b-83fe0abce4e4", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-05-28T20:27:00Z", + "last_edited_time": "2024-05-28T20:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_1", + "heading_1": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is heading 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is heading 1", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "7ad2c4a1-a9f7-4fe9-87dd-dede0dddf1ae", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-05-28T20:27:00Z", + "last_edited_time": "2024-05-28T20:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Lorem ipsum dolor sit amet", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Lorem ipsum dolor sit amet", + "href": null + } + ], + "color": "default" + } + }, + + { + "object": "block", + "id": "c4c4d5cf-7358-4f1e-a8d5-0b433b1cce35", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-09-03T16:20:00Z", + "last_edited_time": "2024-09-03T16:20:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Top level paragraph", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Top level paragraph", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "91044001-e5e3-4165-bca0-90eae8eedd5e", + "parent": { + "type": "block_id", + "block_id": "c4c4d5cf-7358-4f1e-a8d5-0b433b1cce35" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:20:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 1", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "c0302421-6a1f-461d-8a56-91e585fb5e54", + "parent": { + "type": "block_id", + "block_id": "91044001-e5e3-4165-bca0-90eae8eedd5e" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 2", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "37113c94-4284-470c-8177-c1ed49dff264", + "parent": { + "type": "block_id", + "block_id": "c0302421-6a1f-461d-8a56-91e585fb5e54" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 3", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 3", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "87c5c1bb-de05-495c-8e97-86a17906b87d", + "parent": { + "type": "block_id", + "block_id": "37113c94-4284-470c-8177-c1ed49dff264" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 4", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 4", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "42adb363-d88f-4b51-b611-47a2b99157cd", + "parent": { + "type": "block_id", + "block_id": "87c5c1bb-de05-495c-8e97-86a17906b87d" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 5", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 5", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "58acde88-f2ab-400c-8d01-0f24b588b027", + "parent": { + "type": "block_id", + "block_id": "42adb363-d88f-4b51-b611-47a2b99157cd" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 6", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 6", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81", + "parent": { + "type": "block_id", + "block_id": "58acde88-f2ab-400c-8d01-0f24b588b027" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 7", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 7", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3", + "parent": { + "type": "block_id", + "block_id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 8", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 8", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69", + "parent": { + "type": "block_id", + "block_id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:19:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 9", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 9", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "ea4a318c-f324-417b-a2ae-5718408da4a4", + "parent": { + "type": "block_id", + "block_id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:20:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 10", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 10", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb", + "parent": { + "type": "block_id", + "block_id": "ea4a318c-f324-417b-a2ae-5718408da4a4" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:20:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 11", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 11", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "43f2148e-f780-4e48-a2a3-e38984b02eaa", + "parent": { + "type": "block_id", + "block_id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb" + }, + "created_time": "2024-09-03T16:19:00Z", + "last_edited_time": "2024-09-03T16:20:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 12", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 12", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "de6c1aee-ab7c-4787-9c25-72168048365c", + "parent": { + "type": "block_id", + "block_id": "43f2148e-f780-4e48-a2a3-e38984b02eaa" + }, + "created_time": "2024-09-03T16:20:00Z", + "last_edited_time": "2024-09-03T16:20:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 13", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 13", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "faf3ccaa-628f-431c-9c85-b752953396fe", + "parent": { + "type": "block_id", + "block_id": "de6c1aee-ab7c-4787-9c25-72168048365c" + }, + "created_time": "2024-09-03T16:20:00Z", + "last_edited_time": "2024-09-03T16:20:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Subparagraph level 14", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Subparagraph level 14", + "href": null + } + ], + "color": "default" + } + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "object": "block", + "id": "b7ada960-5e53-408f-93cc-c7444dc90ce0", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-05-28T20:27:00Z", + "last_edited_time": "2024-05-28T20:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is heading 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is heading 2", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "01c48f37-f565-4966-a801-b9269e6a18ae", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-05-28T20:27:00Z", + "last_edited_time": "2024-08-09T10:29:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "table", + "table": { + "table_width": 2, + "has_column_header": true, + "has_row_header": true + }, + "children": [ + { + "object": "block", + "id": "36a7fd6c-9e14-43f9-b265-7b21cf958668", + "parent": { + "type": "block_id", + "block_id": "01c48f37-f565-4966-a801-b9269e6a18ae" + }, + "created_time": "2024-05-28T20:27:00Z", + "last_edited_time": "2024-08-09T10:28:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "table_row", + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "Col1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Col1", + "href": null + } + ], + [ + { + "type": "text", + "text": { + "content": "Col2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Col2", + "href": null + } + ] + ] + } + }, + { + "object": "block", + "id": "afa18f11-719f-4dcd-bfe0-c45a871a7309", + "parent": { + "type": "block_id", + "block_id": "01c48f37-f565-4966-a801-b9269e6a18ae" + }, + "created_time": "2024-05-28T20:27:00Z", + "last_edited_time": "2024-08-09T10:28:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "table_row", + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "1", + "href": null + } + ], + [ + { + "type": "text", + "text": { + "content": "3", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "3", + "href": null + } + ] + ] + } + }, + { + "object": "block", + "id": "7e257aaf-24e6-4506-a8f4-1ecd7299a8f5", + "parent": { + "type": "block_id", + "block_id": "01c48f37-f565-4966-a801-b9269e6a18ae" + }, + "created_time": "2024-05-28T20:27:00Z", + "last_edited_time": "2024-05-28T20:28:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "table_row", + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "2", + "href": null + } + ], + [ + { + "type": "text", + "text": { + "content": "4", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "4", + "href": null + } + ] + ] + } + } + ] + }, + { + "object": "block", + "id": "e63fbc82-db45-4a17-b788-8ec343f7e898", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-05-28T20:29:00Z", + "last_edited_time": "2024-05-28T20:29:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "New line", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "New line", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "9b45ebeb-cb04-4eb8-a0b6-8ba8a03e31d8", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-05-28T20:29:00Z", + "last_edited_time": "2024-05-28T20:29:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "code", + "code": { + "caption": [], + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is a code block", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is a code block", + "href": null + } + ], + "language": "javascript" + } + }, + { + "object": "block", + "id": "cce2750e-836c-4358-ada9-abffedf7108a", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-05-30T13:20:00Z", + "last_edited_time": "2024-07-31T16:03:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Intersecting blocks example", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Intersecting blocks example", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "5b9377ac-5964-4b1c-84a5-a8bc1e1bf197", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-07-31T16:03:00Z", + "last_edited_time": "2024-07-31T16:17:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This paragraph", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This paragraph", + "href": null + }, + { + "type": "text", + "text": { + "content": " has so", + "link": null + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": " has so", + "href": null + }, + { + "type": "text", + "text": { + "content": "me bold ite", + "link": { + "url": "https://solmaz.io/" + } + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "me bold ite", + "href": "https://solmaz.io/" + }, + { + "type": "text", + "text": { + "content": "ms and lin", + "link": { + "url": "https://solmaz.io/" + } + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "ms and lin", + "href": "https://solmaz.io/" + }, + { + "type": "text", + "text": { + "content": "ks at the same time.", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "ks at the same time.", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "1bd0a650-72a2-4754-be95-c3fe163ca348", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-07-31T16:04:00Z", + "last_edited_time": "2024-07-31T16:04:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [], + "color": "default" + } + }, + { + "object": "block", + "id": "1d266695-2fde-48db-ba15-cb73813f6bdd", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-07-31T16:04:00Z", + "last_edited_time": "2024-07-31T16:16:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here ar", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here ar", + "href": null + }, + { + "type": "text", + "text": { + "content": "e", + "link": null + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "e", + "href": null + }, + { + "type": "text", + "text": { + "content": " ", + "link": null + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": true, + "code": false, + "color": "default" + }, + "plain_text": " ", + "href": null + }, + { + "type": "text", + "text": { + "content": "t", + "link": null + }, + "annotations": { + "bold": true, + "italic": true, + "strikethrough": false, + "underline": true, + "code": false, + "color": "default" + }, + "plain_text": "t", + "href": null + }, + { + "type": "text", + "text": { + "content": "w", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": true, + "code": false, + "color": "default" + }, + "plain_text": "w", + "href": null + }, + { + "type": "text", + "text": { + "content": "o", + "link": null + }, + "annotations": { + "bold": false, + "italic": true, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "o", + "href": null + }, + { + "type": "text", + "text": { + "content": " ", + "link": null + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": true, + "code": false, + "color": "default" + }, + "plain_text": " ", + "href": null + }, + { + "type": "text", + "text": { + "content": "p", + "link": null + }, + "annotations": { + "bold": true, + "italic": true, + "strikethrough": false, + "underline": true, + "code": false, + "color": "default" + }, + "plain_text": "p", + "href": null + }, + { + "type": "text", + "text": { + "content": "ar", + "link": { + "url": "https://solmaz.io/" + } + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": true, + "code": false, + "color": "default" + }, + "plain_text": "ar", + "href": "https://solmaz.io/" + }, + { + "type": "text", + "text": { + "content": "agra", + "link": { + "url": "https://solmaz.io/" + } + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "agra", + "href": "https://solmaz.io/" + }, + { + "type": "text", + "text": { + "content": "phs tha", + "link": { + "url": "https://solmaz.io/" + } + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "phs tha", + "href": "https://solmaz.io/" + }, + { + "type": "text", + "text": { + "content": "t are ", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "t are ", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "95d6cf78-e826-4954-9329-a4431f553aaf", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:26:00Z", + "last_edited_time": "2024-08-01T15:26:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Bulleted list examples", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Bulleted list examples", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "bd15c375-417f-4849-a76d-711b3af6d26b", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:26:00Z", + "last_edited_time": "2024-08-01T15:26:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here is a bulleted list", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here is a bulleted list", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "665fd9b7-5297-4194-b582-07a986150358", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:26:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "bulleted_list_item", + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Item 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Item 1", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "8429547f-873d-4bae-ab3e-d25d0e469f62", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "bulleted_list_item", + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Item 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Item 2", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "b620e239-45f3-4936-a40c-44adaca8a1cb", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "I break the list here", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "I break the list here", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "a8f0a0ec-becc-48bc-ada1-62a32ff7f5ff", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "bulleted_list_item", + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "I continue here", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "I continue here", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "112acb8f-4506-4ef0-b401-90f759a7dc84", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Enumerated list examples", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Enumerated list examples", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "88d00178-8baa-48a0-9345-cb39ef8f4ca5", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here is an enumerated list", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here is an enumerated list", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "caeecb54-2078-46cb-bee2-0db61ad2a2c5", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "numbered_list_item", + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Item 1 (1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Item 1 (1", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "7f62eca2-65f6-4e90-ba41-9df1d0bed101", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "numbered_list_item", + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Item 2 (2)", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Item 2 (2)", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "f6ccb48d-72b2-4194-b8f4-807647a52089", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-09T09:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "I break the list here", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "I break the list here", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "fb7bc77c-bd6f-4064-9c89-0d51d3fd35d2", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T09:27:00Z", + "last_edited_time": "2024-08-09T09:27:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "numbered_list_item", + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "I continue here (3)", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "I continue here (3)", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "e2a8e977-4ba8-4484-8fe1-62eaa172155f", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:27:00Z", + "last_edited_time": "2024-08-01T15:28:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "numbered_list_item", + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "The index continues from the previous (4)", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "The index continues from the previous (4)", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "1981c9ec-75c5-405d-a4b4-c4db8fc705af", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:28:00Z", + "last_edited_time": "2024-08-01T15:28:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "6. I can\u2019t set (6) as the item label", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "6. I can\u2019t set (6) as the item label", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "2768f917-08f4-4e7d-83d0-61844dc8a40e", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T09:36:00Z", + "last_edited_time": "2024-08-09T09:36:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "TODO examples", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "TODO examples", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "eb921750-d7ec-42e5-96b2-c65ca88d6776", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T09:36:00Z", + "last_edited_time": "2024-08-09T09:36:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "to_do", + "to_do": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here is an unchecked todo item", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here is an unchecked todo item", + "href": null + } + ], + "checked": false, + "color": "default" + } + }, + { + "object": "block", + "id": "b694c88f-688f-4a45-b64b-61d65b29e8b0", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T09:36:00Z", + "last_edited_time": "2024-08-09T09:36:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "to_do", + "to_do": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here is a checked todo item", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here is a checked todo item", + "href": null + } + ], + "checked": true, + "color": "default" + } + }, + { + "object": "block", + "id": "8bb29a91-42ae-40a1-b696-1a71d699978f", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-01T15:38:00Z", + "last_edited_time": "2024-08-08T15:47:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Code blocks", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Code blocks", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "bbb22181-4e3d-4f49-956b-9b8d092d9199", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-08T15:47:00Z", + "last_edited_time": "2024-08-08T15:48:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "code", + "code": { + "caption": [ + { + "type": "text", + "text": { + "content": "This is a code ", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is a code ", + "href": null + }, + { + "type": "text", + "text": { + "content": "block", + "link": null + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "block", + "href": null + }, + { + "type": "text", + "text": { + "content": " caption", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": " caption", + "href": null + } + ], + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is a code block\nThis is a new line", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is a code block\nThis is a new line", + "href": null + } + ], + "language": "bash" + } + }, + { + "object": "block", + "id": "439e9657-0303-473c-8991-b2c837b1c3aa", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T07:33:00Z", + "last_edited_time": "2024-08-09T07:33:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Equations", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Equations", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "f0545305-a8ab-449a-aa23-e9617febf965", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T07:33:00Z", + "last_edited_time": "2024-08-09T07:34:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is an ", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is an ", + "href": null + }, + { + "type": "equation", + "equation": { + "expression": "\\int_0^1\\sin(x)\\,dx" + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "\\int_0^1\\sin(x)\\,dx", + "href": null + }, + { + "type": "text", + "text": { + "content": " inline equation. Below is a block equation:", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": " inline equation. Below is a block equation:", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "a0ea53e7-ca42-4d5d-8345-481bb74c8c2a", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T07:34:00Z", + "last_edited_time": "2024-08-09T07:34:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "equation", + "equation": { + "expression": "\\int_0^1\\sin(x)\\,dx" + } + }, + { + "object": "block", + "id": "665dc9ee-ea74-46cf-9001-d07d4020b060", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T08:08:00Z", + "last_edited_time": "2024-08-09T08:08:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Image blocks", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Image blocks", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "98c78199-ef1c-497a-8e44-e1f3b511c3a9", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T08:08:00Z", + "last_edited_time": "2024-08-09T08:11:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "image", + "image": { + "caption": [ + { + "type": "text", + "text": { + "content": "This is a caption for the image", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "This is a caption for the image", + "href": null + } + ], + "type": "file", + "file": { + "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/d6000e68-0a06-463d-8914-d6dfe33f31b9/3141b657-2f3a-425a-9b44-d61df64d63e0/TextCortex_%282024-08-07_13_08_56%29_Create_a_drawing_of_a_scenic_v.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240809%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240809T121557Z&X-Amz-Expires=3600&X-Amz-Signature=287d787db70873c68fd020453dffd592faa076352b40dabe5bb711e6b0ae5208&X-Amz-SignedHeaders=host&x-id=GetObject", + "expiry_time": "2024-08-09T13:15:57Z" + } + } + }, + { + "object": "block", + "id": "fdc1aa4c-b0d7-4636-bdee-0a56cfc7c54a", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T10:09:00Z", + "last_edited_time": "2024-08-09T10:09:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Quotes", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Quotes", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "23d455c3-7469-4f02-97b8-e260bc02f940", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T10:09:00Z", + "last_edited_time": "2024-08-09T10:10:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "quote", + "quote": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here is a quote\nSome ", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here is a quote\nSome ", + "href": null + }, + { + "type": "text", + "text": { + "content": "formatted text", + "link": null + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "formatted text", + "href": null + }, + { + "type": "text", + "text": { + "content": " inside the quote", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": " inside the quote", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "f3308f29-797c-47f1-989c-ec823cfbda03", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:00:00Z", + "last_edited_time": "2024-08-09T11:05:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Divider", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Divider", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "f48c507b-46eb-4b92-a714-3cba4982fec2", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:05:00Z", + "last_edited_time": "2024-08-09T11:05:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here is a divider:", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here is a divider:", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "df320310-a087-4039-b3ef-28d0460f8664", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:05:00Z", + "last_edited_time": "2024-08-09T11:05:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "divider", + "divider": {} + }, + { + "object": "block", + "id": "3378e979-29d3-4d53-b2dd-a4e2df80fef3", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:05:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Columns", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Columns", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + }, + { + "object": "block", + "id": "175e5d39-7d7c-4428-81a7-b16824a78f6b", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Below is a 2 column example", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Below is a 2 column example", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "32b17815-92d0-4fa3-8d72-ff65ca40c1dc", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column_list", + "column_list": {}, + "children": [ + { + "object": "block", + "id": "9f75c2d7-b90d-4b51-841d-47543b4a8097", + "parent": { + "type": "block_id", + "block_id": "32b17815-92d0-4fa3-8d72-ff65ca40c1dc" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column", + "column": {}, + "children": [ + { + "object": "block", + "id": "99dd06e9-8faf-46e2-a84f-5476a2e8fb72", + "parent": { + "type": "block_id", + "block_id": "9f75c2d7-b90d-4b51-841d-47543b4a8097" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "First column", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "First column", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "c50858c0-4722-4fdb-9f24-0cb83be4fc71", + "parent": { + "type": "block_id", + "block_id": "9f75c2d7-b90d-4b51-841d-47543b4a8097" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "to_do", + "to_do": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "something", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "something", + "href": null + } + ], + "checked": false, + "color": "default" + } + } + ] + }, + { + "object": "block", + "id": "ab7c74d6-e601-4627-9374-3a034d1389c2", + "parent": { + "type": "block_id", + "block_id": "32b17815-92d0-4fa3-8d72-ff65ca40c1dc" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column", + "column": {}, + "children": [ + { + "object": "block", + "id": "37c2f36a-8f95-46be-ae1e-190c3f98c559", + "parent": { + "type": "block_id", + "block_id": "ab7c74d6-e601-4627-9374-3a034d1389c2" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Second column", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Second column", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b", + "parent": { + "type": "block_id", + "block_id": "ab7c74d6-e601-4627-9374-3a034d1389c2" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "table", + "table": { + "table_width": 2, + "has_column_header": false, + "has_row_header": false + }, + "children": [ + { + "object": "block", + "id": "5cb7a042-70af-472b-b410-451afb723d2a", + "parent": { + "type": "block_id", + "block_id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "table_row", + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "1", + "href": null + } + ], + [ + { + "type": "text", + "text": { + "content": "2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "2", + "href": null + } + ] + ] + } + }, + { + "object": "block", + "id": "92e59441-bef1-46fa-8e1b-a807131695d3", + "parent": { + "type": "block_id", + "block_id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "table_row", + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "3", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "3", + "href": null + } + ], + [ + { + "type": "text", + "text": { + "content": "4", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "4", + "href": null + } + ] + ] + } + }, + { + "object": "block", + "id": "bb47ea02-ea18-47d2-9432-0b4acc27538e", + "parent": { + "type": "block_id", + "block_id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b" + }, + "created_time": "2024-08-09T11:58:00Z", + "last_edited_time": "2024-08-09T11:58:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "table_row", + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "5", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "5", + "href": null + } + ], + [ + { + "type": "text", + "text": { + "content": "6", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "6", + "href": null + } + ] + ] + } + } + ] + } + ] + } + ] + }, + { + "object": "block", + "id": "65dde59c-db9c-46a2-9e98-4a1b99ba644f", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Below is a 4 column example", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Below is a 4 column example", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column_list", + "column_list": {}, + "children": [ + { + "object": "block", + "id": "46e295c3-cf75-4434-9947-8af0833bb1bb", + "parent": { + "type": "block_id", + "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column", + "column": {}, + "children": [ + { + "object": "block", + "id": "9deb01c3-339f-44de-873d-4f51b97355e2", + "parent": { + "type": "block_id", + "block_id": "46e295c3-cf75-4434-9947-8af0833bb1bb" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Column 1", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Column 1", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "bfcbb0ee-9a18-45ac-bfda-54f4c58f9f87", + "parent": { + "type": "block_id", + "block_id": "46e295c3-cf75-4434-9947-8af0833bb1bb" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "bulleted_list_item", + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "A list", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "A list", + "href": null + } + ], + "color": "default" + } + } + ] + }, + { + "object": "block", + "id": "ac0925fe-2403-4482-8672-e816f49520b6", + "parent": { + "type": "block_id", + "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column", + "column": {}, + "children": [ + { + "object": "block", + "id": "09ef16e6-6822-4487-81dd-6de5efb88725", + "parent": { + "type": "block_id", + "block_id": "ac0925fe-2403-4482-8672-e816f49520b6" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Column 2", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Column 2", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "da439007-8b00-49b6-866c-40db7bc79024", + "parent": { + "type": "block_id", + "block_id": "ac0925fe-2403-4482-8672-e816f49520b6" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "equation", + "equation": { + "expression": "a=b" + } + } + ] + }, + { + "object": "block", + "id": "30f487ba-df8e-483f-95e5-4362f59cd1bb", + "parent": { + "type": "block_id", + "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T12:00:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column", + "column": {}, + "children": [ + { + "object": "block", + "id": "8f685774-d528-49c9-a4ed-f35f7e0c1e48", + "parent": { + "type": "block_id", + "block_id": "30f487ba-df8e-483f-95e5-4362f59cd1bb" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Column 3", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Column 3", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "33a3dfd2-6172-4c12-a4c1-b43e6c22a16b", + "parent": { + "type": "block_id", + "block_id": "30f487ba-df8e-483f-95e5-4362f59cd1bb" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "divider", + "divider": {} + }, + { + "object": "block", + "id": "cbcf740b-c1e0-4267-9f87-0553b02693f2", + "parent": { + "type": "block_id", + "block_id": "30f487ba-df8e-483f-95e5-4362f59cd1bb" + }, + "created_time": "2024-08-09T12:00:00Z", + "last_edited_time": "2024-08-09T12:00:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "heading in column", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "heading in column", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + } + ] + }, + { + "object": "block", + "id": "8eb1b342-acf0-44be-b813-4f8348b385df", + "parent": { + "type": "block_id", + "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T12:00:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "column", + "column": {}, + "children": [ + { + "object": "block", + "id": "5b9f4e4b-e6d0-4f11-b4b9-2b6fa19e4c4f", + "parent": { + "type": "block_id", + "block_id": "8eb1b342-acf0-44be-b813-4f8348b385df" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Column 4", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Column 4", + "href": null + } + ], + "color": "default" + } + } + ] + } + ] + }, + { + "object": "block", + "id": "e208aed6-048e-4114-b645-50c0a5b394fb", + "parent": { + "type": "page_id", + "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" + }, + "created_time": "2024-08-09T11:59:00Z", + "last_edited_time": "2024-08-09T11:59:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [], + "color": "default" + } + }, + { + "object": "block", + "id": "e4c47a53-eea7-42cd-b51a-16fcbbaf5572", + "parent": { + "type": "block_id", + "block_id": "8eb1b342-acf0-44be-b813-4f8348b385df" + }, + "created_time": "2024-08-09T12:00:00Z", + "last_edited_time": "2024-08-09T12:00:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "toggle", + "toggle": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "toggle", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "toggle", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "f79d961f-7b00-464f-b3b2-a745041cea50", + "parent": { + "type": "block_id", + "block_id": "e4c47a53-eea7-42cd-b51a-16fcbbaf5572" + }, + "created_time": "2024-08-09T12:00:00Z", + "last_edited_time": "2024-08-09T12:00:00Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "asdfasdfafd", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "asdfasdfafd", + "href": null + } + ], + "color": "default" + } + } + ] + } + ] } diff --git a/schema/page/ex2_success.json b/schema/page/ex2_success.json index 885be29..0a9bd83 100644 --- a/schema/page/ex2_success.json +++ b/schema/page/ex2_success.json @@ -1,8 +1,8 @@ { "object": "page", "id": "be633bf1-dfa0-436d-b259-571129a590e5", - "created_time": "2022-10-24T22:54:00Z", - "last_edited_time": "2023-03-08T18:25:00Z", + "created_time": "2022-10-24T22:54:00.000Z", + "last_edited_time": "2023-03-08T18:25:00.000Z", "created_by": { "object": "user", "id": "c2f20311-9e54-4d11-8c79-7398424ae41e" @@ -15,8 +15,6 @@ "type": "emoji", "emoji": "🐞" }, - "archived": false, - "in_trash": false, "properties": { "title": { "id": "title", @@ -42,3360 +40,5 @@ ] } }, - "children": [ - { - "object": "block", - "id": "7bebf9ce-b1ee-4415-b01b-83fe0abce4e4", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-05-28T20:27:00Z", - "last_edited_time": "2024-05-28T20:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_1", - "heading_1": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "This is heading 1", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This is heading 1", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "7ad2c4a1-a9f7-4fe9-87dd-dede0dddf1ae", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-05-28T20:27:00Z", - "last_edited_time": "2024-05-28T20:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Lorem ipsum dolor sit amet", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Lorem ipsum dolor sit amet", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "b7ada960-5e53-408f-93cc-c7444dc90ce0", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-05-28T20:27:00Z", - "last_edited_time": "2024-05-28T20:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "This is heading 2", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This is heading 2", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "01c48f37-f565-4966-a801-b9269e6a18ae", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-05-28T20:27:00Z", - "last_edited_time": "2024-08-09T10:29:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "table", - "table": { - "table_width": 2, - "has_column_header": true, - "has_row_header": true - }, - "children": [ - { - "object": "block", - "id": "36a7fd6c-9e14-43f9-b265-7b21cf958668", - "parent": { - "type": "block_id", - "block_id": "01c48f37-f565-4966-a801-b9269e6a18ae" - }, - "created_time": "2024-05-28T20:27:00Z", - "last_edited_time": "2024-08-09T10:28:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "table_row", - "table_row": { - "cells": [ - [ - { - "type": "text", - "text": { - "content": "Col1", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Col1", - "href": null - } - ], - [ - { - "type": "text", - "text": { - "content": "Col2", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Col2", - "href": null - } - ] - ] - } - }, - { - "object": "block", - "id": "afa18f11-719f-4dcd-bfe0-c45a871a7309", - "parent": { - "type": "block_id", - "block_id": "01c48f37-f565-4966-a801-b9269e6a18ae" - }, - "created_time": "2024-05-28T20:27:00Z", - "last_edited_time": "2024-08-09T10:28:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "table_row", - "table_row": { - "cells": [ - [ - { - "type": "text", - "text": { - "content": "1", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "1", - "href": null - } - ], - [ - { - "type": "text", - "text": { - "content": "3", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "3", - "href": null - } - ] - ] - } - }, - { - "object": "block", - "id": "7e257aaf-24e6-4506-a8f4-1ecd7299a8f5", - "parent": { - "type": "block_id", - "block_id": "01c48f37-f565-4966-a801-b9269e6a18ae" - }, - "created_time": "2024-05-28T20:27:00Z", - "last_edited_time": "2024-05-28T20:28:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "table_row", - "table_row": { - "cells": [ - [ - { - "type": "text", - "text": { - "content": "2", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "2", - "href": null - } - ], - [ - { - "type": "text", - "text": { - "content": "4", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "4", - "href": null - } - ] - ] - } - } - ] - }, - { - "object": "block", - "id": "e63fbc82-db45-4a17-b788-8ec343f7e898", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-05-28T20:29:00Z", - "last_edited_time": "2024-05-28T20:29:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "New line", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "New line", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "9b45ebeb-cb04-4eb8-a0b6-8ba8a03e31d8", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-05-28T20:29:00Z", - "last_edited_time": "2024-05-28T20:29:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "code", - "code": { - "caption": [], - "rich_text": [ - { - "type": "text", - "text": { - "content": "This is a code block", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This is a code block", - "href": null - } - ], - "language": "javascript" - } - }, - { - "object": "block", - "id": "cce2750e-836c-4358-ada9-abffedf7108a", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-05-30T13:20:00Z", - "last_edited_time": "2024-07-31T16:03:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Intersecting blocks example", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Intersecting blocks example", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "5b9377ac-5964-4b1c-84a5-a8bc1e1bf197", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-07-31T16:03:00Z", - "last_edited_time": "2024-07-31T16:17:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "This paragraph", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This paragraph", - "href": null - }, - { - "type": "text", - "text": { - "content": " has so", - "link": null - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": " has so", - "href": null - }, - { - "type": "text", - "text": { - "content": "me bold ite", - "link": { - "url": "https://solmaz.io/" - } - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "me bold ite", - "href": "https://solmaz.io/" - }, - { - "type": "text", - "text": { - "content": "ms and lin", - "link": { - "url": "https://solmaz.io/" - } - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "ms and lin", - "href": "https://solmaz.io/" - }, - { - "type": "text", - "text": { - "content": "ks at the same time.", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "ks at the same time.", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "1bd0a650-72a2-4754-be95-c3fe163ca348", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-07-31T16:04:00Z", - "last_edited_time": "2024-07-31T16:04:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [], - "color": "default" - } - }, - { - "object": "block", - "id": "1d266695-2fde-48db-ba15-cb73813f6bdd", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-07-31T16:04:00Z", - "last_edited_time": "2024-07-31T16:16:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Here ar", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Here ar", - "href": null - }, - { - "type": "text", - "text": { - "content": "e", - "link": null - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "e", - "href": null - }, - { - "type": "text", - "text": { - "content": " ", - "link": null - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": true, - "code": false, - "color": "default" - }, - "plain_text": " ", - "href": null - }, - { - "type": "text", - "text": { - "content": "t", - "link": null - }, - "annotations": { - "bold": true, - "italic": true, - "strikethrough": false, - "underline": true, - "code": false, - "color": "default" - }, - "plain_text": "t", - "href": null - }, - { - "type": "text", - "text": { - "content": "w", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": true, - "code": false, - "color": "default" - }, - "plain_text": "w", - "href": null - }, - { - "type": "text", - "text": { - "content": "o", - "link": null - }, - "annotations": { - "bold": false, - "italic": true, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "o", - "href": null - }, - { - "type": "text", - "text": { - "content": " ", - "link": null - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": true, - "code": false, - "color": "default" - }, - "plain_text": " ", - "href": null - }, - { - "type": "text", - "text": { - "content": "p", - "link": null - }, - "annotations": { - "bold": true, - "italic": true, - "strikethrough": false, - "underline": true, - "code": false, - "color": "default" - }, - "plain_text": "p", - "href": null - }, - { - "type": "text", - "text": { - "content": "ar", - "link": { - "url": "https://solmaz.io/" - } - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": true, - "code": false, - "color": "default" - }, - "plain_text": "ar", - "href": "https://solmaz.io/" - }, - { - "type": "text", - "text": { - "content": "agra", - "link": { - "url": "https://solmaz.io/" - } - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "agra", - "href": "https://solmaz.io/" - }, - { - "type": "text", - "text": { - "content": "phs tha", - "link": { - "url": "https://solmaz.io/" - } - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "phs tha", - "href": "https://solmaz.io/" - }, - { - "type": "text", - "text": { - "content": "t are ", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "t are ", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "95d6cf78-e826-4954-9329-a4431f553aaf", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:26:00Z", - "last_edited_time": "2024-08-01T15:26:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Bulleted list examples", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Bulleted list examples", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "bd15c375-417f-4849-a76d-711b3af6d26b", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:26:00Z", - "last_edited_time": "2024-08-01T15:26:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Here is a bulleted list", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Here is a bulleted list", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "665fd9b7-5297-4194-b582-07a986150358", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:26:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "bulleted_list_item", - "bulleted_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Item 1", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Item 1", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "8429547f-873d-4bae-ab3e-d25d0e469f62", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "bulleted_list_item", - "bulleted_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Item 2", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Item 2", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "b620e239-45f3-4936-a40c-44adaca8a1cb", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "I break the list here", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "I break the list here", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "a8f0a0ec-becc-48bc-ada1-62a32ff7f5ff", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "bulleted_list_item", - "bulleted_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "I continue here", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "I continue here", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "112acb8f-4506-4ef0-b401-90f759a7dc84", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Enumerated list examples", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Enumerated list examples", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "88d00178-8baa-48a0-9345-cb39ef8f4ca5", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Here is an enumerated list", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Here is an enumerated list", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "caeecb54-2078-46cb-bee2-0db61ad2a2c5", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "numbered_list_item", - "numbered_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Item 1 (1", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Item 1 (1", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "7f62eca2-65f6-4e90-ba41-9df1d0bed101", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "numbered_list_item", - "numbered_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Item 2 (2)", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Item 2 (2)", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "f6ccb48d-72b2-4194-b8f4-807647a52089", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-09T09:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "I break the list here", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "I break the list here", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "fb7bc77c-bd6f-4064-9c89-0d51d3fd35d2", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T09:27:00Z", - "last_edited_time": "2024-08-09T09:27:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "numbered_list_item", - "numbered_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "I continue here (3)", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "I continue here (3)", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "e2a8e977-4ba8-4484-8fe1-62eaa172155f", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:27:00Z", - "last_edited_time": "2024-08-01T15:28:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "numbered_list_item", - "numbered_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "The index continues from the previous (4)", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "The index continues from the previous (4)", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "1981c9ec-75c5-405d-a4b4-c4db8fc705af", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:28:00Z", - "last_edited_time": "2024-08-01T15:28:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "6. I can\u2019t set (6) as the item label", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "6. I can\u2019t set (6) as the item label", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "2768f917-08f4-4e7d-83d0-61844dc8a40e", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T09:36:00Z", - "last_edited_time": "2024-08-09T09:36:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "TODO examples", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "TODO examples", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "eb921750-d7ec-42e5-96b2-c65ca88d6776", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T09:36:00Z", - "last_edited_time": "2024-08-09T09:36:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "to_do", - "to_do": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Here is an unchecked todo item", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Here is an unchecked todo item", - "href": null - } - ], - "checked": false, - "color": "default" - } - }, - { - "object": "block", - "id": "b694c88f-688f-4a45-b64b-61d65b29e8b0", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T09:36:00Z", - "last_edited_time": "2024-08-09T09:36:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "to_do", - "to_do": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Here is a checked todo item", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Here is a checked todo item", - "href": null - } - ], - "checked": true, - "color": "default" - } - }, - { - "object": "block", - "id": "8bb29a91-42ae-40a1-b696-1a71d699978f", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-01T15:38:00Z", - "last_edited_time": "2024-08-08T15:47:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Code blocks", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Code blocks", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "bbb22181-4e3d-4f49-956b-9b8d092d9199", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-08T15:47:00Z", - "last_edited_time": "2024-08-08T15:48:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "code", - "code": { - "caption": [ - { - "type": "text", - "text": { - "content": "This is a code ", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This is a code ", - "href": null - }, - { - "type": "text", - "text": { - "content": "block", - "link": null - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "block", - "href": null - }, - { - "type": "text", - "text": { - "content": " caption", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": " caption", - "href": null - } - ], - "rich_text": [ - { - "type": "text", - "text": { - "content": "This is a code block\nThis is a new line", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This is a code block\nThis is a new line", - "href": null - } - ], - "language": "bash" - } - }, - { - "object": "block", - "id": "439e9657-0303-473c-8991-b2c837b1c3aa", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T07:33:00Z", - "last_edited_time": "2024-08-09T07:33:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Equations", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Equations", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "f0545305-a8ab-449a-aa23-e9617febf965", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T07:33:00Z", - "last_edited_time": "2024-08-09T07:34:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "This is an ", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This is an ", - "href": null - }, - { - "type": "equation", - "equation": { - "expression": "\\int_0^1\\sin(x)\\,dx" - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "\\int_0^1\\sin(x)\\,dx", - "href": null - }, - { - "type": "text", - "text": { - "content": " inline equation. Below is a block equation:", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": " inline equation. Below is a block equation:", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "a0ea53e7-ca42-4d5d-8345-481bb74c8c2a", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T07:34:00Z", - "last_edited_time": "2024-08-09T07:34:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "equation", - "equation": { - "expression": "\\int_0^1\\sin(x)\\,dx" - } - }, - { - "object": "block", - "id": "665dc9ee-ea74-46cf-9001-d07d4020b060", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T08:08:00Z", - "last_edited_time": "2024-08-09T08:08:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Image blocks", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Image blocks", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "98c78199-ef1c-497a-8e44-e1f3b511c3a9", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T08:08:00Z", - "last_edited_time": "2024-08-09T08:11:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "image", - "image": { - "caption": [ - { - "type": "text", - "text": { - "content": "This is a caption for the image", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "This is a caption for the image", - "href": null - } - ], - "type": "file", - "file": { - "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/d6000e68-0a06-463d-8914-d6dfe33f31b9/3141b657-2f3a-425a-9b44-d61df64d63e0/TextCortex_%282024-08-07_13_08_56%29_Create_a_drawing_of_a_scenic_v.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240809%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240809T121557Z&X-Amz-Expires=3600&X-Amz-Signature=287d787db70873c68fd020453dffd592faa076352b40dabe5bb711e6b0ae5208&X-Amz-SignedHeaders=host&x-id=GetObject", - "expiry_time": "2024-08-09T13:15:57Z" - } - } - }, - { - "object": "block", - "id": "fdc1aa4c-b0d7-4636-bdee-0a56cfc7c54a", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T10:09:00Z", - "last_edited_time": "2024-08-09T10:09:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Quotes", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Quotes", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "23d455c3-7469-4f02-97b8-e260bc02f940", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T10:09:00Z", - "last_edited_time": "2024-08-09T10:10:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "quote", - "quote": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Here is a quote\nSome ", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Here is a quote\nSome ", - "href": null - }, - { - "type": "text", - "text": { - "content": "formatted text", - "link": null - }, - "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "formatted text", - "href": null - }, - { - "type": "text", - "text": { - "content": " inside the quote", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": " inside the quote", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "f3308f29-797c-47f1-989c-ec823cfbda03", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:00:00Z", - "last_edited_time": "2024-08-09T11:05:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Divider", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Divider", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "f48c507b-46eb-4b92-a714-3cba4982fec2", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:05:00Z", - "last_edited_time": "2024-08-09T11:05:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Here is a divider:", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Here is a divider:", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "df320310-a087-4039-b3ef-28d0460f8664", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:05:00Z", - "last_edited_time": "2024-08-09T11:05:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "divider", - "divider": {} - }, - { - "object": "block", - "id": "3378e979-29d3-4d53-b2dd-a4e2df80fef3", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:05:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Columns", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Columns", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - }, - { - "object": "block", - "id": "175e5d39-7d7c-4428-81a7-b16824a78f6b", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Below is a 2 column example", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Below is a 2 column example", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "32b17815-92d0-4fa3-8d72-ff65ca40c1dc", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column_list", - "column_list": {}, - "children": [ - { - "object": "block", - "id": "9f75c2d7-b90d-4b51-841d-47543b4a8097", - "parent": { - "type": "block_id", - "block_id": "32b17815-92d0-4fa3-8d72-ff65ca40c1dc" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column", - "column": {}, - "children": [ - { - "object": "block", - "id": "99dd06e9-8faf-46e2-a84f-5476a2e8fb72", - "parent": { - "type": "block_id", - "block_id": "9f75c2d7-b90d-4b51-841d-47543b4a8097" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "First column", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "First column", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "c50858c0-4722-4fdb-9f24-0cb83be4fc71", - "parent": { - "type": "block_id", - "block_id": "9f75c2d7-b90d-4b51-841d-47543b4a8097" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "to_do", - "to_do": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "something", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "something", - "href": null - } - ], - "checked": false, - "color": "default" - } - } - ] - }, - { - "object": "block", - "id": "ab7c74d6-e601-4627-9374-3a034d1389c2", - "parent": { - "type": "block_id", - "block_id": "32b17815-92d0-4fa3-8d72-ff65ca40c1dc" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column", - "column": {}, - "children": [ - { - "object": "block", - "id": "37c2f36a-8f95-46be-ae1e-190c3f98c559", - "parent": { - "type": "block_id", - "block_id": "ab7c74d6-e601-4627-9374-3a034d1389c2" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Second column", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Second column", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b", - "parent": { - "type": "block_id", - "block_id": "ab7c74d6-e601-4627-9374-3a034d1389c2" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "table", - "table": { - "table_width": 2, - "has_column_header": false, - "has_row_header": false - }, - "children": [ - { - "object": "block", - "id": "5cb7a042-70af-472b-b410-451afb723d2a", - "parent": { - "type": "block_id", - "block_id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "table_row", - "table_row": { - "cells": [ - [ - { - "type": "text", - "text": { - "content": "1", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "1", - "href": null - } - ], - [ - { - "type": "text", - "text": { - "content": "2", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "2", - "href": null - } - ] - ] - } - }, - { - "object": "block", - "id": "92e59441-bef1-46fa-8e1b-a807131695d3", - "parent": { - "type": "block_id", - "block_id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "table_row", - "table_row": { - "cells": [ - [ - { - "type": "text", - "text": { - "content": "3", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "3", - "href": null - } - ], - [ - { - "type": "text", - "text": { - "content": "4", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "4", - "href": null - } - ] - ] - } - }, - { - "object": "block", - "id": "bb47ea02-ea18-47d2-9432-0b4acc27538e", - "parent": { - "type": "block_id", - "block_id": "7af2cdff-d4fc-44d2-ac60-e47f3da4761b" - }, - "created_time": "2024-08-09T11:58:00Z", - "last_edited_time": "2024-08-09T11:58:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "table_row", - "table_row": { - "cells": [ - [ - { - "type": "text", - "text": { - "content": "5", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "5", - "href": null - } - ], - [ - { - "type": "text", - "text": { - "content": "6", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "6", - "href": null - } - ] - ] - } - } - ] - } - ] - } - ] - }, - { - "object": "block", - "id": "65dde59c-db9c-46a2-9e98-4a1b99ba644f", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Below is a 4 column example", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Below is a 4 column example", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column_list", - "column_list": {}, - "children": [ - { - "object": "block", - "id": "46e295c3-cf75-4434-9947-8af0833bb1bb", - "parent": { - "type": "block_id", - "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column", - "column": {}, - "children": [ - { - "object": "block", - "id": "9deb01c3-339f-44de-873d-4f51b97355e2", - "parent": { - "type": "block_id", - "block_id": "46e295c3-cf75-4434-9947-8af0833bb1bb" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Column 1", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Column 1", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "bfcbb0ee-9a18-45ac-bfda-54f4c58f9f87", - "parent": { - "type": "block_id", - "block_id": "46e295c3-cf75-4434-9947-8af0833bb1bb" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "bulleted_list_item", - "bulleted_list_item": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "A list", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "A list", - "href": null - } - ], - "color": "default" - } - } - ] - }, - { - "object": "block", - "id": "ac0925fe-2403-4482-8672-e816f49520b6", - "parent": { - "type": "block_id", - "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column", - "column": {}, - "children": [ - { - "object": "block", - "id": "09ef16e6-6822-4487-81dd-6de5efb88725", - "parent": { - "type": "block_id", - "block_id": "ac0925fe-2403-4482-8672-e816f49520b6" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Column 2", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Column 2", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "da439007-8b00-49b6-866c-40db7bc79024", - "parent": { - "type": "block_id", - "block_id": "ac0925fe-2403-4482-8672-e816f49520b6" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "equation", - "equation": { - "expression": "a=b" - } - } - ] - }, - { - "object": "block", - "id": "30f487ba-df8e-483f-95e5-4362f59cd1bb", - "parent": { - "type": "block_id", - "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T12:00:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column", - "column": {}, - "children": [ - { - "object": "block", - "id": "8f685774-d528-49c9-a4ed-f35f7e0c1e48", - "parent": { - "type": "block_id", - "block_id": "30f487ba-df8e-483f-95e5-4362f59cd1bb" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Column 3", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Column 3", - "href": null - } - ], - "color": "default" - } - }, - { - "object": "block", - "id": "33a3dfd2-6172-4c12-a4c1-b43e6c22a16b", - "parent": { - "type": "block_id", - "block_id": "30f487ba-df8e-483f-95e5-4362f59cd1bb" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "divider", - "divider": {} - }, - { - "object": "block", - "id": "cbcf740b-c1e0-4267-9f87-0553b02693f2", - "parent": { - "type": "block_id", - "block_id": "30f487ba-df8e-483f-95e5-4362f59cd1bb" - }, - "created_time": "2024-08-09T12:00:00Z", - "last_edited_time": "2024-08-09T12:00:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "heading_2", - "heading_2": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "heading in column", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "heading in column", - "href": null - } - ], - "is_toggleable": false, - "color": "default" - } - } - ] - }, - { - "object": "block", - "id": "8eb1b342-acf0-44be-b813-4f8348b385df", - "parent": { - "type": "block_id", - "block_id": "ba1c4998-31d2-4fc6-a6ec-f8101752e41a" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T12:00:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "column", - "column": {}, - "children": [ - { - "object": "block", - "id": "5b9f4e4b-e6d0-4f11-b4b9-2b6fa19e4c4f", - "parent": { - "type": "block_id", - "block_id": "8eb1b342-acf0-44be-b813-4f8348b385df" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Column 4", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Column 4", - "href": null - } - ], - "color": "default" - } - } - ] - } - ] - }, - { - "object": "block", - "id": "e208aed6-048e-4114-b645-50c0a5b394fb", - "parent": { - "type": "page_id", - "page_id": "8d7dbc6b-5c55-4589-826c-1352450db04e" - }, - "created_time": "2024-08-09T11:59:00Z", - "last_edited_time": "2024-08-09T11:59:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [], - "color": "default" - } - }, - { - "object": "block", - "id": "e4c47a53-eea7-42cd-b51a-16fcbbaf5572", - "parent": { - "type": "block_id", - "block_id": "8eb1b342-acf0-44be-b813-4f8348b385df" - }, - "created_time": "2024-08-09T12:00:00Z", - "last_edited_time": "2024-08-09T12:00:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "toggle", - "toggle": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "toggle", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "toggle", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "f79d961f-7b00-464f-b3b2-a745041cea50", - "parent": { - "type": "block_id", - "block_id": "e4c47a53-eea7-42cd-b51a-16fcbbaf5572" - }, - "created_time": "2024-08-09T12:00:00Z", - "last_edited_time": "2024-08-09T12:00:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "asdfasdfafd", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "asdfasdfafd", - "href": null - } - ], - "color": "default" - } - } - ] - } - ] + "children": [] } diff --git a/tests/run_serialization_tests.py b/tests/run_serialization_tests.py index 648fdf1..7fef358 100644 --- a/tests/run_serialization_tests.py +++ b/tests/run_serialization_tests.py @@ -3,7 +3,7 @@ from jsondoc.serialize import load_page from jsondoc.utils import load_json_file -PAGE_PATH = "schema/page/ex2_success.json" +PAGE_PATH = "schema/page/ex1_success.json" def diff_strings(string1, string2): From 852918d6509db62fbd7cf669cba53ea38b41deab Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:12:53 +0200 Subject: [PATCH 06/45] Correct children relationships --- docs/children-blocks.md | 20 +++++++++++++++++++ schema/block/types/code/code_schema.json | 13 ++++++------ .../types/heading_1/heading_1_schema.json | 13 ++++++------ .../types/heading_2/heading_2_schema.json | 13 ++++++------ .../types/heading_3/heading_3_schema.json | 13 ++++++------ 5 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 docs/children-blocks.md diff --git a/docs/children-blocks.md b/docs/children-blocks.md new file mode 100644 index 0000000..fb0346a --- /dev/null +++ b/docs/children-blocks.md @@ -0,0 +1,20 @@ + +Blocks that can have children + +## Block types with no restrictions on children types + +- `type: paragraph` +- `type: toggle` +- `type: bulleted_list_item` +- `type: numbered_list_item` +- `type: quote` +- `type: synced_block` +- `type: to_do` +- `type: column` + +## Block types where children are restricted to a specific type + +- `type: column_list` + - Children type: `type: column` +- `type: table` + - Children type: `type: table_row` \ No newline at end of file diff --git a/schema/block/types/code/code_schema.json b/schema/block/types/code/code_schema.json index 5bf093d..a74442d 100644 --- a/schema/block/types/code/code_schema.json +++ b/schema/block/types/code/code_schema.json @@ -103,13 +103,14 @@ }, "required": ["rich_text"], "additionalProperties": false - }, - "children": { - "type": "array", - "items": { - "$ref": "/block/block_schema.json" - } } + // Code block can't have children + // "children": { + // "type": "array", + // "items": { + // "$ref": "/block/block_schema.json" + // } + // } }, "required": ["type", "code"], "unevaluatedProperties": false diff --git a/schema/block/types/heading_1/heading_1_schema.json b/schema/block/types/heading_1/heading_1_schema.json index 9f2a8cf..3ae08d4 100644 --- a/schema/block/types/heading_1/heading_1_schema.json +++ b/schema/block/types/heading_1/heading_1_schema.json @@ -26,13 +26,14 @@ }, "required": ["rich_text"], "additionalProperties": false - }, - "children": { - "type": "array", - "items": { - "$ref": "/block/block_schema.json" - } } + // Headings can't have children + // "children": { + // "type": "array", + // "items": { + // "$ref": "/block/block_schema.json" + // } + // } }, "required": ["type", "heading_1"], "unevaluatedProperties": false diff --git a/schema/block/types/heading_2/heading_2_schema.json b/schema/block/types/heading_2/heading_2_schema.json index b7cc4e9..b623b1f 100644 --- a/schema/block/types/heading_2/heading_2_schema.json +++ b/schema/block/types/heading_2/heading_2_schema.json @@ -25,13 +25,14 @@ } }, "required": ["rich_text"] - }, - "children": { - "type": "array", - "items": { - "$ref": "/block/block_schema.json" - } } + // Headings can't have children + // "children": { + // "type": "array", + // "items": { + // "$ref": "/block/block_schema.json" + // } + // } }, "required": ["type", "heading_2"], "unevaluatedProperties": false diff --git a/schema/block/types/heading_3/heading_3_schema.json b/schema/block/types/heading_3/heading_3_schema.json index 533d281..227591f 100644 --- a/schema/block/types/heading_3/heading_3_schema.json +++ b/schema/block/types/heading_3/heading_3_schema.json @@ -25,13 +25,14 @@ } }, "required": ["rich_text"] - }, - "children": { - "type": "array", - "items": { - "$ref": "/block/block_schema.json" - } } + // Headings can't have children + // "children": { + // "type": "array", + // "items": { + // "$ref": "/block/block_schema.json" + // } + // } }, "required": ["type", "heading_3"], "unevaluatedProperties": false From 0e6ee4437aa3a76a2176e927d376f4ab7f026a18 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:26:55 +0200 Subject: [PATCH 07/45] Minor --- docs/differences-from-notion.md | 3 ++- jsondoc/validate.py | 6 +++++- tests/run_validation_tests.py | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/differences-from-notion.md b/docs/differences-from-notion.md index 8371f3c..f5a6f09 100644 --- a/docs/differences-from-notion.md +++ b/docs/differences-from-notion.md @@ -1,4 +1,5 @@ - Certain block types are not supported. - TBD: List which ones - Certain fields can be omitted, which would fall back to default values. -- Metadata field is present in blocks. \ No newline at end of file +- Metadata field is present in blocks. +- grep `// notion-diverge` \ No newline at end of file diff --git a/jsondoc/validate.py b/jsondoc/validate.py index 24070aa..d1aa175 100644 --- a/jsondoc/validate.py +++ b/jsondoc/validate.py @@ -1,6 +1,7 @@ import argparse import os import sys +import time from jsonschema import Draft202012Validator, ValidationError, validate from referencing import Registry, Resource @@ -49,8 +50,11 @@ def retrieve(uri: str): validator = Draft202012Validator(schema, registry=registry) try: + start = time.time() validator.validate(data) - print(f"{data_path} is valid") + end = time.time() + elapsed_ms = (end - start) * 1000 + print(f"{data_path} is valid (took {elapsed_ms:.3f}ms)") sys.exit(0) except ValidationError as e: print(f"Validation error: {e}") diff --git a/tests/run_validation_tests.py b/tests/run_validation_tests.py index 5e0a6ac..355f5c3 100644 --- a/tests/run_validation_tests.py +++ b/tests/run_validation_tests.py @@ -15,6 +15,8 @@ def run_validation(schema_path, data_path, root=None): if root is not None: cmd.extend(["--root", root]) + # print(" ".join(cmd)) + result = subprocess.run( cmd, capture_output=True, From 61c2dc31ddbe880ea0b196b1841e8e0133ecd293 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 4 Sep 2024 16:39:20 +0200 Subject: [PATCH 08/45] Time tests --- jsondoc/utils.py | 14 + jsondoc/validate.py | 2 +- schema/page/ex1_success.json | 732 +++++++++++++++---------------- tests/run_serialization_tests.py | 7 +- 4 files changed, 386 insertions(+), 369 deletions(-) diff --git a/jsondoc/utils.py b/jsondoc/utils.py index 40a4599..43fb1f3 100644 --- a/jsondoc/utils.py +++ b/jsondoc/utils.py @@ -1,4 +1,6 @@ import json +import time +from contextlib import contextmanager ARBITRARY_JSON_SCHEMA_OBJECT = { "type": "object", @@ -61,3 +63,15 @@ def set_nested_value(obj: dict | object, coordinates: str, value: any) -> None: current[keys[-1]] = value else: setattr(current, keys[-1], value) + + +@contextmanager +def timer(name, unit="s"): + start_time = time.time() + yield + end_time = time.time() + elapsed = end_time - start_time + if unit == "ms": + print(f"{name} took {elapsed * 1000:.4f} milliseconds") + else: + print(f"{name} took {end_time - start_time:.4f} seconds") diff --git a/jsondoc/validate.py b/jsondoc/validate.py index d1aa175..bbeb6f1 100644 --- a/jsondoc/validate.py +++ b/jsondoc/validate.py @@ -53,7 +53,7 @@ def retrieve(uri: str): start = time.time() validator.validate(data) end = time.time() - elapsed_ms = (end - start) * 1000 + elapsed_ms = (end - start) * 1000 print(f"{data_path} is valid (took {elapsed_ms:.3f}ms)") sys.exit(0) except ValidationError as e: diff --git a/schema/page/ex1_success.json b/schema/page/ex1_success.json index b4a1b21..1104b4b 100644 --- a/schema/page/ex1_success.json +++ b/schema/page/ex1_success.json @@ -441,372 +441,372 @@ "color": "default" }, "children": [ - { - "object": "block", - "id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81", - "parent": { - "type": "block_id", - "block_id": "58acde88-f2ab-400c-8d01-0f24b588b027" - }, - "created_time": "2024-09-03T16:19:00Z", - "last_edited_time": "2024-09-03T16:19:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 7", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 7", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3", - "parent": { - "type": "block_id", - "block_id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81" - }, - "created_time": "2024-09-03T16:19:00Z", - "last_edited_time": "2024-09-03T16:19:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 8", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 8", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69", - "parent": { - "type": "block_id", - "block_id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3" - }, - "created_time": "2024-09-03T16:19:00Z", - "last_edited_time": "2024-09-03T16:19:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 9", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 9", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "ea4a318c-f324-417b-a2ae-5718408da4a4", - "parent": { - "type": "block_id", - "block_id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69" - }, - "created_time": "2024-09-03T16:19:00Z", - "last_edited_time": "2024-09-03T16:20:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 10", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 10", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb", - "parent": { - "type": "block_id", - "block_id": "ea4a318c-f324-417b-a2ae-5718408da4a4" - }, - "created_time": "2024-09-03T16:19:00Z", - "last_edited_time": "2024-09-03T16:20:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 11", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 11", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "43f2148e-f780-4e48-a2a3-e38984b02eaa", - "parent": { - "type": "block_id", - "block_id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb" - }, - "created_time": "2024-09-03T16:19:00Z", - "last_edited_time": "2024-09-03T16:20:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 12", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 12", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "de6c1aee-ab7c-4787-9c25-72168048365c", - "parent": { - "type": "block_id", - "block_id": "43f2148e-f780-4e48-a2a3-e38984b02eaa" - }, - "created_time": "2024-09-03T16:20:00Z", - "last_edited_time": "2024-09-03T16:20:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": true, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 13", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 13", - "href": null - } - ], - "color": "default" - }, - "children": [ - { - "object": "block", - "id": "faf3ccaa-628f-431c-9c85-b752953396fe", - "parent": { - "type": "block_id", - "block_id": "de6c1aee-ab7c-4787-9c25-72168048365c" - }, - "created_time": "2024-09-03T16:20:00Z", - "last_edited_time": "2024-09-03T16:20:00Z", - "created_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "last_edited_by": { - "object": "user", - "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" - }, - "has_children": false, - "archived": false, - "in_trash": false, - "type": "paragraph", - "paragraph": { - "rich_text": [ - { - "type": "text", - "text": { - "content": "Subparagraph level 14", - "link": null - }, - "annotations": { - "bold": false, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" - }, - "plain_text": "Subparagraph level 14", - "href": null - } - ], - "color": "default" - } - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } - ] - } + // { + // "object": "block", + // "id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81", + // "parent": { + // "type": "block_id", + // "block_id": "58acde88-f2ab-400c-8d01-0f24b588b027" + // }, + // "created_time": "2024-09-03T16:19:00Z", + // "last_edited_time": "2024-09-03T16:19:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": true, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 7", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 7", + // "href": null + // } + // ], + // "color": "default" + // }, + // "children": [ + // { + // "object": "block", + // "id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3", + // "parent": { + // "type": "block_id", + // "block_id": "bda4b6b0-cc82-49ed-81c1-2da3fe441c81" + // }, + // "created_time": "2024-09-03T16:19:00Z", + // "last_edited_time": "2024-09-03T16:19:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": true, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 8", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 8", + // "href": null + // } + // ], + // "color": "default" + // }, + // "children": [ + // { + // "object": "block", + // "id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69", + // "parent": { + // "type": "block_id", + // "block_id": "6560eb26-64a3-42b3-adfe-598a9d70b4d3" + // }, + // "created_time": "2024-09-03T16:19:00Z", + // "last_edited_time": "2024-09-03T16:19:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": true, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 9", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 9", + // "href": null + // } + // ], + // "color": "default" + // }, + // "children": [ + // { + // "object": "block", + // "id": "ea4a318c-f324-417b-a2ae-5718408da4a4", + // "parent": { + // "type": "block_id", + // "block_id": "0158d0ea-5a3d-4a77-aa88-10aed4fc5d69" + // }, + // "created_time": "2024-09-03T16:19:00Z", + // "last_edited_time": "2024-09-03T16:20:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": true, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 10", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 10", + // "href": null + // } + // ], + // "color": "default" + // }, + // "children": [ + // { + // "object": "block", + // "id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb", + // "parent": { + // "type": "block_id", + // "block_id": "ea4a318c-f324-417b-a2ae-5718408da4a4" + // }, + // "created_time": "2024-09-03T16:19:00Z", + // "last_edited_time": "2024-09-03T16:20:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": true, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 11", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 11", + // "href": null + // } + // ], + // "color": "default" + // }, + // "children": [ + // { + // "object": "block", + // "id": "43f2148e-f780-4e48-a2a3-e38984b02eaa", + // "parent": { + // "type": "block_id", + // "block_id": "9f200f8f-586c-43a9-9f01-e6614dc65ccb" + // }, + // "created_time": "2024-09-03T16:19:00Z", + // "last_edited_time": "2024-09-03T16:20:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": true, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 12", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 12", + // "href": null + // } + // ], + // "color": "default" + // }, + // "children": [ + // { + // "object": "block", + // "id": "de6c1aee-ab7c-4787-9c25-72168048365c", + // "parent": { + // "type": "block_id", + // "block_id": "43f2148e-f780-4e48-a2a3-e38984b02eaa" + // }, + // "created_time": "2024-09-03T16:20:00Z", + // "last_edited_time": "2024-09-03T16:20:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": true, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 13", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 13", + // "href": null + // } + // ], + // "color": "default" + // }, + // "children": [ + // { + // "object": "block", + // "id": "faf3ccaa-628f-431c-9c85-b752953396fe", + // "parent": { + // "type": "block_id", + // "block_id": "de6c1aee-ab7c-4787-9c25-72168048365c" + // }, + // "created_time": "2024-09-03T16:20:00Z", + // "last_edited_time": "2024-09-03T16:20:00Z", + // "created_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "last_edited_by": { + // "object": "user", + // "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + // }, + // "has_children": false, + // "archived": false, + // "in_trash": false, + // "type": "paragraph", + // "paragraph": { + // "rich_text": [ + // { + // "type": "text", + // "text": { + // "content": "Subparagraph level 14", + // "link": null + // }, + // "annotations": { + // "bold": false, + // "italic": false, + // "strikethrough": false, + // "underline": false, + // "code": false, + // "color": "default" + // }, + // "plain_text": "Subparagraph level 14", + // "href": null + // } + // ], + // "color": "default" + // } + // } + // ] + // } + // ] + // } + // ] + // } + // ] + // } + // ] + // } + // ] + // } + // ] + // } ] } ] diff --git a/tests/run_serialization_tests.py b/tests/run_serialization_tests.py index 7fef358..5176d29 100644 --- a/tests/run_serialization_tests.py +++ b/tests/run_serialization_tests.py @@ -1,7 +1,8 @@ import difflib import json +import time from jsondoc.serialize import load_page -from jsondoc.utils import load_json_file +from jsondoc.utils import load_json_file, timer PAGE_PATH = "schema/page/ex1_success.json" @@ -37,7 +38,9 @@ def test_load_page(): content = load_json_file(PAGE_PATH) # This should not raise any errors - page = load_page(content) + with timer("load_page", unit="ms"): + page = load_page(content) + assert page is not None # Serialize it again From 4c127df294d3094638f9b70abdcde1ed2c1a9746 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:12:54 +0200 Subject: [PATCH 09/45] Implement ALLOWED_CHILDREN_BLOCK_TYPES --- docs/children-blocks.md | 4 +- examples/html/html_nested_elements.html | 57 ++++++++++++++++++ jsondoc/convert/html.py | 35 ++++++----- jsondoc/rules.py | 60 +++++++++++++++++++ jsondoc/{validate.py => validate/__init__.py} | 0 5 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 examples/html/html_nested_elements.html create mode 100644 jsondoc/rules.py rename jsondoc/{validate.py => validate/__init__.py} (100%) diff --git a/docs/children-blocks.md b/docs/children-blocks.md index fb0346a..b60b1bb 100644 --- a/docs/children-blocks.md +++ b/docs/children-blocks.md @@ -1,5 +1,7 @@ -Blocks that can have children +# Children blocks + +See `jsondoc.validate.rules` for more details. ## Block types with no restrictions on children types diff --git a/examples/html/html_nested_elements.html b/examples/html/html_nested_elements.html new file mode 100644 index 0000000..dd81161 --- /dev/null +++ b/examples/html/html_nested_elements.html @@ -0,0 +1,57 @@ + + + + + + + + HTML Patterns + + + + + + + + +
+

Nested HTML Elements

+

An example of nested HTML elements

+
+ + + +
+

Site title

+ +
+ +
+
+

+ This is an example of a nested paragraph. +

This is a nested paragraph within the main paragraph.

+ This span is nested inside the paragraph. + This emphasized text is also nested. + And here's some strong text, nested as well. +

+
+
+ + diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index b96a9ce..3b98342 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -112,6 +112,12 @@ def convert_soup(self, soup): return self.process_tag(soup, convert_as_inline=False, children_only=True) def process_tag(self, node, convert_as_inline, children_only=False): + """ + Convert a BeautifulSoup node to JSON-DOC. Recurses through the children + nodes and converts them to JSON-DOC corresponding current block type + can have children or not. + children_only: To be called while inputting + """ text = "" # markdown headings or cells can't include @@ -122,7 +128,8 @@ def process_tag(self, node, convert_as_inline, children_only=False): if not children_only and (isHeading or isCell): convert_children_as_inline = True - + if node.name == "span": + import ipdb; ipdb.set_trace() # Remove whitespace-only textnodes in purely nested nodes def is_nested_node(el): return el and el.name in [ @@ -182,8 +189,8 @@ def process_text(self, el): text = whitespace_re.sub(" ", text) # escape special characters if we're not inside a preformatted or code element - if not el.find_parent(["pre", "code", "kbd", "samp"]): - text = self.escape(text) + # if not el.find_parent(["pre", "code", "kbd", "samp"]): + # text = self.escape(text) # remove trailing whitespaces if any of the following condition is true: # - current text node is the last node in li @@ -221,17 +228,17 @@ def should_convert_tag(self, tag): else: return True - def escape(self, text): - if not text: - return "" - if self.options["escape_misc"]: - text = re.sub(r"([\\&<`[>~#=+|-])", r"\\\1", text) - text = re.sub(r"([0-9])([.)])", r"\1\\\2", text) - if self.options["escape_asterisks"]: - text = text.replace("*", r"\*") - if self.options["escape_underscores"]: - text = text.replace("_", r"\_") - return text + # def escape(self, text): + # if not text: + # return "" + # if self.options["escape_misc"]: + # text = re.sub(r"([\\&<`[>~#=+|-])", r"\\\1", text) + # text = re.sub(r"([0-9])([.)])", r"\1\\\2", text) + # if self.options["escape_asterisks"]: + # text = text.replace("*", r"\*") + # if self.options["escape_underscores"]: + # text = text.replace("_", r"\_") + # return text def indent(self, text, level): return line_beginning_re.sub("\t" * level, text) if text else "" diff --git a/jsondoc/rules.py b/jsondoc/rules.py new file mode 100644 index 0000000..4c75491 --- /dev/null +++ b/jsondoc/rules.py @@ -0,0 +1,60 @@ +from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock +from jsondoc.models.block.types.code import CodeBlock +from jsondoc.models.block.types.column import ColumnBlock +from jsondoc.models.block.types.column_list import ColumnListBlock +from jsondoc.models.block.types.divider import DividerBlock +from jsondoc.models.block.types.equation import EquationBlock +from jsondoc.models.block.types.heading_1 import Heading1Block +from jsondoc.models.block.types.heading_2 import Heading2Block +from jsondoc.models.block.types.heading_3 import Heading3Block +from jsondoc.models.block.types.image import ImageBlock +from jsondoc.models.block.types.numbered_list_item import NumberedListItemBlock +from jsondoc.models.block.types.paragraph import ParagraphBlock +from jsondoc.models.block.types.quote import QuoteBlock +from jsondoc.models.block.types.table import TableBlock +from jsondoc.models.block.types.table_row import TableRowBlock +from jsondoc.models.block.types.to_do import ToDoBlock +from jsondoc.models.block.types.toggle import ToggleBlock + +# TODO: +# - If type: synced_block is added, add it down below as well + +ALL_BLOCK_TYPES = [ + BulletedListItemBlock, + CodeBlock, + ColumnBlock, + ColumnListBlock, + DividerBlock, + EquationBlock, + Heading1Block, + Heading2Block, + Heading3Block, + ImageBlock, + NumberedListItemBlock, + ParagraphBlock, + QuoteBlock, + TableBlock, + TableRowBlock, + ToDoBlock, + ToggleBlock, +] + +ALLOWED_CHILDREN_BLOCK_TYPES = { + BulletedListItemBlock: ALL_BLOCK_TYPES, + CodeBlock: [], + ColumnBlock: ALL_BLOCK_TYPES, + ColumnListBlock: [ColumnBlock], + DividerBlock: [], + EquationBlock: [], + Heading1Block: [], + Heading2Block: [], + Heading3Block: [], + ImageBlock: [], + NumberedListItemBlock: ALL_BLOCK_TYPES, + ParagraphBlock: ALL_BLOCK_TYPES, + QuoteBlock: ALL_BLOCK_TYPES, + TableBlock: [TableRowBlock], + TableRowBlock: [], + ToDoBlock: ALL_BLOCK_TYPES, + ToggleBlock: ALL_BLOCK_TYPES, +} diff --git a/jsondoc/validate.py b/jsondoc/validate/__init__.py similarity index 100% rename from jsondoc/validate.py rename to jsondoc/validate/__init__.py From 06a105ba8d42cf3f5fd680dbe5f47385e2961da9 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:53:06 +0200 Subject: [PATCH 10/45] wip --- jsondoc/convert/html.py | 56 +++++++++++++---- jsondoc/convert/utils.py | 61 +++++++++++++++++++ jsondoc/models/block/__init__.py | 2 +- jsondoc/models/block/base/__init__.py | 4 +- .../types/bulleted_list_item/__init__.py | 2 +- jsondoc/models/block/types/code/__init__.py | 6 +- jsondoc/models/block/types/column/__init__.py | 2 +- .../block/types/column_list/__init__.py | 2 +- .../models/block/types/divider/__init__.py | 2 +- .../models/block/types/equation/__init__.py | 2 +- .../models/block/types/heading_1/__init__.py | 6 +- .../models/block/types/heading_2/__init__.py | 6 +- .../models/block/types/heading_3/__init__.py | 6 +- jsondoc/models/block/types/image/__init__.py | 2 +- .../types/image/external_image/__init__.py | 2 +- .../block/types/image/file_image/__init__.py | 2 +- .../types/numbered_list_item/__init__.py | 2 +- .../models/block/types/paragraph/__init__.py | 2 +- jsondoc/models/block/types/quote/__init__.py | 2 +- .../models/block/types/rich_text/__init__.py | 2 +- .../block/types/rich_text/base/__init__.py | 2 +- .../types/rich_text/equation/__init__.py | 15 ++--- .../block/types/rich_text/text/__init__.py | 17 ++---- jsondoc/models/block/types/table/__init__.py | 2 +- .../models/block/types/table_row/__init__.py | 2 +- jsondoc/models/block/types/to_do/__init__.py | 2 +- jsondoc/models/block/types/toggle/__init__.py | 2 +- jsondoc/models/file/__init__.py | 2 +- jsondoc/models/file/base/__init__.py | 2 +- jsondoc/models/file/external/__init__.py | 2 +- jsondoc/models/file/file/__init__.py | 2 +- jsondoc/models/page/__init__.py | 2 +- jsondoc/models/shared_definitions/__init__.py | 15 ++++- jsondoc/utils.py | 5 ++ schema/block/base/base_schema.json | 3 +- .../rich_text/equation/equation_schema.json | 30 +-------- .../types/rich_text/text/text_schema.json | 30 +-------- .../shared_definitions_schema.json | 41 +++++++++++++ 38 files changed, 206 insertions(+), 141 deletions(-) create mode 100644 jsondoc/convert/utils.py diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 3b98342..81ce683 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -3,6 +3,26 @@ import re import six +from jsondoc.convert.utils import create_paragraph_block +from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock +from jsondoc.models.block.types.code import CodeBlock +from jsondoc.models.block.types.column import ColumnBlock +from jsondoc.models.block.types.column_list import ColumnListBlock +from jsondoc.models.block.types.divider import DividerBlock +from jsondoc.models.block.types.equation import EquationBlock +from jsondoc.models.block.types.heading_1 import Heading1Block +from jsondoc.models.block.types.heading_2 import Heading2Block +from jsondoc.models.block.types.heading_3 import Heading3Block +from jsondoc.models.block.types.image import ImageBlock +from jsondoc.models.block.types.numbered_list_item import NumberedListItemBlock +from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock +from jsondoc.models.block.types.quote import QuoteBlock +from jsondoc.models.block.types.rich_text.text import RichTextText +from jsondoc.models.block.types.table import TableBlock +from jsondoc.models.block.types.table_row import TableRowBlock +from jsondoc.models.block.types.to_do import ToDoBlock +from jsondoc.models.block.types.toggle import ToggleBlock + convert_heading_re = re.compile(r"convert_h(\d+)") line_beginning_re = re.compile(r"^", re.MULTILINE) @@ -116,20 +136,23 @@ def process_tag(self, node, convert_as_inline, children_only=False): Convert a BeautifulSoup node to JSON-DOC. Recurses through the children nodes and converts them to JSON-DOC corresponding current block type can have children or not. - children_only: To be called while inputting + children_only: Is true only at the top level entry point. """ text = "" # markdown headings or cells can't include # block elements (elements w/newlines) - isHeading = html_heading_re.match(node.name) is not None - isCell = node.name in ["td", "th"] + is_heading = html_heading_re.match(node.name) is not None + is_cell = node.name in ["td", "th"] convert_children_as_inline = convert_as_inline - if not children_only and (isHeading or isCell): + if not children_only and (is_heading or is_cell): convert_children_as_inline = True if node.name == "span": - import ipdb; ipdb.set_trace() + import ipdb + + ipdb.set_trace() + # Remove whitespace-only textnodes in purely nested nodes def is_nested_node(el): return el and el.name in [ @@ -382,14 +405,21 @@ def convert_li(self, el, text, convert_as_inline): def convert_p(self, el, text, convert_as_inline): if convert_as_inline: return text - if self.options["wrap"]: - text = fill( - text, - width=self.options["wrap_width"], - break_long_words=False, - break_on_hyphens=False, - ) - return "%s\n\n" % text if text else "" + + # if self.options["wrap"]: + # text = fill( + # text, + # width=self.options["wrap_width"], + # break_long_words=False, + # break_on_hyphens=False, + # ) + # return "%s\n\n" % text if text else "" + # return ParagraphBlock( + # paragraph=Paragraph( + # rich_text=[RichTextText(text=text)], + # ) + # ) + return create_paragraph_block(text=text) def convert_pre(self, el, text, convert_as_inline): if not text: diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py new file mode 100644 index 0000000..b843569 --- /dev/null +++ b/jsondoc/convert/utils.py @@ -0,0 +1,61 @@ +from datetime import datetime, timezone +from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock +from jsondoc.models.block.types.rich_text.text import Link, RichTextText, Text +from jsondoc.models.shared_definitions import Annotations +from jsondoc.utils import generate_id + + +def create_rich_text( + text: str, + url: str = None, + bold: bool = False, + italic: bool = False, + strikethrough: bool = False, + underline: bool = False, + code: bool = False, + color: str = "default", +) -> RichTextText: + return RichTextText( + type="text", + text=Text( + content=text, + link=Link(url=url) if url else None, + ), + annotations=Annotations( + bold=bold, + italic=italic, + strikethrough=strikethrough, + underline=underline, + code=code, + color=color, + ), + plain_text=text, + ) + + +def create_paragraph_block( + text: str, + id=None, + created_time=None, + **kwargs, +) -> ParagraphBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + return ParagraphBlock( + id=id, + object="block", + created_time=created_time, + type="paragraph", + paragraph=Paragraph( + rich_text=[ + create_rich_text(text, **kwargs), + ] + ), + has_children=False, + ) + + +# print(create_paragraph_block("Hello, world!")) diff --git a/jsondoc/models/block/__init__.py b/jsondoc/models/block/__init__.py index d585287..571066f 100644 --- a/jsondoc/models/block/__init__.py +++ b/jsondoc/models/block/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/base/__init__.py b/jsondoc/models/block/base/__init__.py index 0ea8f87..f109713 100644 --- a/jsondoc/models/block/base/__init__.py +++ b/jsondoc/models/block/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations @@ -44,7 +44,7 @@ class BlockBase(BaseModel): created_by: Optional[CreatedBy] = None last_edited_time: Optional[AwareDatetime] = None last_edited_by: Optional[LastEditedBy] = None - archived: bool + archived: Optional[bool] = None in_trash: Optional[bool] = None has_children: bool metadata: Optional[Dict[str, Any]] = None diff --git a/jsondoc/models/block/types/bulleted_list_item/__init__.py b/jsondoc/models/block/types/bulleted_list_item/__init__.py index 991dbc3..4c865e8 100644 --- a/jsondoc/models/block/types/bulleted_list_item/__init__.py +++ b/jsondoc/models/block/types/bulleted_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/code/__init__.py b/jsondoc/models/block/types/code/__init__.py index 18c2031..cacd763 100644 --- a/jsondoc/models/block/types/code/__init__.py +++ b/jsondoc/models/block/types/code/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations @@ -99,9 +99,5 @@ class Code(BaseModel): class CodeBlock(BlockBase): - model_config = ConfigDict( - arbitrary_types_allowed=True, - ) type: Literal['code'] code: Code - children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/column/__init__.py b/jsondoc/models/block/types/column/__init__.py index 61f5c20..c31b4d5 100644 --- a/jsondoc/models/block/types/column/__init__.py +++ b/jsondoc/models/block/types/column/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column_list/__init__.py b/jsondoc/models/block/types/column_list/__init__.py index 693306a..49a5ac0 100644 --- a/jsondoc/models/block/types/column_list/__init__.py +++ b/jsondoc/models/block/types/column_list/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/divider/__init__.py b/jsondoc/models/block/types/divider/__init__.py index 8e167f6..98373fc 100644 --- a/jsondoc/models/block/types/divider/__init__.py +++ b/jsondoc/models/block/types/divider/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/equation/__init__.py b/jsondoc/models/block/types/equation/__init__.py index e5f10c0..09e992a 100644 --- a/jsondoc/models/block/types/equation/__init__.py +++ b/jsondoc/models/block/types/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_1/__init__.py b/jsondoc/models/block/types/heading_1/__init__.py index 5bcf594..1397767 100644 --- a/jsondoc/models/block/types/heading_1/__init__.py +++ b/jsondoc/models/block/types/heading_1/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations @@ -24,9 +24,5 @@ class Heading1(BaseModel): class Heading1Block(BlockBase): - model_config = ConfigDict( - arbitrary_types_allowed=True, - ) type: Literal['heading_1'] heading_1: Heading1 - children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/heading_2/__init__.py b/jsondoc/models/block/types/heading_2/__init__.py index bfaeb95..35fcc82 100644 --- a/jsondoc/models/block/types/heading_2/__init__.py +++ b/jsondoc/models/block/types/heading_2/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations @@ -23,9 +23,5 @@ class Heading2(BaseModel): class Heading2Block(BlockBase): - model_config = ConfigDict( - arbitrary_types_allowed=True, - ) type: Literal['heading_2'] heading_2: Heading2 - children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/heading_3/__init__.py b/jsondoc/models/block/types/heading_3/__init__.py index a5d5c79..b912381 100644 --- a/jsondoc/models/block/types/heading_3/__init__.py +++ b/jsondoc/models/block/types/heading_3/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations @@ -23,9 +23,5 @@ class Heading3(BaseModel): class Heading3Block(BlockBase): - model_config = ConfigDict( - arbitrary_types_allowed=True, - ) type: Literal['heading_3'] heading_3: Heading3 - children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/image/__init__.py b/jsondoc/models/block/types/image/__init__.py index a202c7a..79c8f5f 100644 --- a/jsondoc/models/block/types/image/__init__.py +++ b/jsondoc/models/block/types/image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/external_image/__init__.py b/jsondoc/models/block/types/image/external_image/__init__.py index 6c620c7..2fa7816 100644 --- a/jsondoc/models/block/types/image/external_image/__init__.py +++ b/jsondoc/models/block/types/image/external_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/file_image/__init__.py b/jsondoc/models/block/types/image/file_image/__init__.py index d8939b4..3464307 100644 --- a/jsondoc/models/block/types/image/file_image/__init__.py +++ b/jsondoc/models/block/types/image/file_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/numbered_list_item/__init__.py b/jsondoc/models/block/types/numbered_list_item/__init__.py index 9ab0557..15a89f6 100644 --- a/jsondoc/models/block/types/numbered_list_item/__init__.py +++ b/jsondoc/models/block/types/numbered_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/paragraph/__init__.py b/jsondoc/models/block/types/paragraph/__init__.py index 47c81f8..6cedfde 100644 --- a/jsondoc/models/block/types/paragraph/__init__.py +++ b/jsondoc/models/block/types/paragraph/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/quote/__init__.py b/jsondoc/models/block/types/quote/__init__.py index 406ee5d..c262b78 100644 --- a/jsondoc/models/block/types/quote/__init__.py +++ b/jsondoc/models/block/types/quote/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/__init__.py b/jsondoc/models/block/types/rich_text/__init__.py index 1527263..0be0b8b 100644 --- a/jsondoc/models/block/types/rich_text/__init__.py +++ b/jsondoc/models/block/types/rich_text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/base/__init__.py b/jsondoc/models/block/types/rich_text/base/__init__.py index 1291b8d..c546728 100644 --- a/jsondoc/models/block/types/rich_text/base/__init__.py +++ b/jsondoc/models/block/types/rich_text/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/equation/__init__.py b/jsondoc/models/block/types/rich_text/equation/__init__.py index 42a3663..44b4cf7 100644 --- a/jsondoc/models/block/types/rich_text/equation/__init__.py +++ b/jsondoc/models/block/types/rich_text/equation/__init__.py @@ -1,12 +1,13 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations from typing import Optional from jsondoc.models.block.types.rich_text.base import RichTextBase +from jsondoc.models.shared_definitions import Annotations from pydantic import BaseModel, ConfigDict from typing_extensions import Literal @@ -18,16 +19,10 @@ class Equation(BaseModel): expression: str -class Annotations(BaseModel): - bold: bool - italic: bool - strikethrough: bool - underline: bool - code: bool - color: str - - class RichTextEquation(RichTextBase): + model_config = ConfigDict( + arbitrary_types_allowed=True, + ) type: Literal['equation'] equation: Equation annotations: Annotations diff --git a/jsondoc/models/block/types/rich_text/text/__init__.py b/jsondoc/models/block/types/rich_text/text/__init__.py index dcd4594..b28584b 100644 --- a/jsondoc/models/block/types/rich_text/text/__init__.py +++ b/jsondoc/models/block/types/rich_text/text/__init__.py @@ -1,13 +1,14 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations from typing import Optional from jsondoc.models.block.types.rich_text.base import RichTextBase -from pydantic import BaseModel +from jsondoc.models.shared_definitions import Annotations +from pydantic import BaseModel, ConfigDict from typing_extensions import Literal @@ -20,16 +21,10 @@ class Text(BaseModel): link: Optional[Link] = None -class Annotations(BaseModel): - bold: bool - italic: bool - strikethrough: bool - underline: bool - code: bool - color: str - - class RichTextText(RichTextBase): + model_config = ConfigDict( + arbitrary_types_allowed=True, + ) type: Literal['text'] text: Text annotations: Annotations diff --git a/jsondoc/models/block/types/table/__init__.py b/jsondoc/models/block/types/table/__init__.py index a9861f2..c1130ca 100644 --- a/jsondoc/models/block/types/table/__init__.py +++ b/jsondoc/models/block/types/table/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table_row/__init__.py b/jsondoc/models/block/types/table_row/__init__.py index e9e6243..c58953c 100644 --- a/jsondoc/models/block/types/table_row/__init__.py +++ b/jsondoc/models/block/types/table_row/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/to_do/__init__.py b/jsondoc/models/block/types/to_do/__init__.py index 4e75f0d..0175983 100644 --- a/jsondoc/models/block/types/to_do/__init__.py +++ b/jsondoc/models/block/types/to_do/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/toggle/__init__.py b/jsondoc/models/block/types/toggle/__init__.py index d062e76..d14b19e 100644 --- a/jsondoc/models/block/types/toggle/__init__.py +++ b/jsondoc/models/block/types/toggle/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/__init__.py b/jsondoc/models/file/__init__.py index a453a48..5b16fa8 100644 --- a/jsondoc/models/file/__init__.py +++ b/jsondoc/models/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/base/__init__.py b/jsondoc/models/file/base/__init__.py index 89efdc1..c704e5c 100644 --- a/jsondoc/models/file/base/__init__.py +++ b/jsondoc/models/file/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/external/__init__.py b/jsondoc/models/file/external/__init__.py index 90bec60..07a3654 100644 --- a/jsondoc/models/file/external/__init__.py +++ b/jsondoc/models/file/external/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/file/__init__.py b/jsondoc/models/file/file/__init__.py index 44d562d..52e0896 100644 --- a/jsondoc/models/file/file/__init__.py +++ b/jsondoc/models/file/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/page/__init__.py b/jsondoc/models/page/__init__.py index dfa9327..e1fd4d9 100644 --- a/jsondoc/models/page/__init__.py +++ b/jsondoc/models/page/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/shared_definitions/__init__.py b/jsondoc/models/shared_definitions/__init__.py index 51c1464..0c57e50 100644 --- a/jsondoc/models/shared_definitions/__init__.py +++ b/jsondoc/models/shared_definitions/__init__.py @@ -1,13 +1,13 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-08-21T17:19:54+00:00 +# timestamp: 2024-09-04T22:43:27+00:00 from __future__ import annotations from enum import Enum -from typing import Any +from typing import Any, Optional -from pydantic import RootModel +from pydantic import BaseModel, RootModel class Model(RootModel[Any]): @@ -34,3 +34,12 @@ class Color(Enum): red = 'red' red_background = 'red_background' yellow_background = 'yellow_background' + + +class Annotations(BaseModel): + bold: Optional[bool] = False + italic: Optional[bool] = False + strikethrough: Optional[bool] = False + underline: Optional[bool] = False + code: Optional[bool] = False + color: Optional[str] = 'default' diff --git a/jsondoc/utils.py b/jsondoc/utils.py index 43fb1f3..966cab8 100644 --- a/jsondoc/utils.py +++ b/jsondoc/utils.py @@ -1,6 +1,7 @@ import json import time from contextlib import contextmanager +import uuid ARBITRARY_JSON_SCHEMA_OBJECT = { "type": "object", @@ -9,6 +10,10 @@ } +def generate_id() -> str: + return uuid.uuid4().hex + + def replace_refs_with_arbitrary_object(data): if isinstance(data, dict): if "$ref" in data: diff --git a/schema/block/base/base_schema.json b/schema/block/base/base_schema.json index 229843e..fa40c04 100644 --- a/schema/block/base/base_schema.json +++ b/schema/block/base/base_schema.json @@ -123,10 +123,11 @@ "id", "type", "created_time", + // notion-diverge // "created_by", // These are optional since we can be out of Notion context // "last_edited_time", // "last_edited_by", - "archived", + // "archived", "has_children" ] // This was meant to refer to the object that contains type-specific block diff --git a/schema/block/types/rich_text/equation/equation_schema.json b/schema/block/types/rich_text/equation/equation_schema.json index c893a22..b9e56a2 100644 --- a/schema/block/types/rich_text/equation/equation_schema.json +++ b/schema/block/types/rich_text/equation/equation_schema.json @@ -19,35 +19,7 @@ "additionalProperties": false }, "annotations": { - "type": "object", - "properties": { - "bold": { - "type": "boolean" - }, - "italic": { - "type": "boolean" - }, - "strikethrough": { - "type": "boolean" - }, - "underline": { - "type": "boolean" - }, - "code": { - "type": "boolean" - }, - "color": { - "type": "string" - } - }, - "required": [ - "bold", - "italic", - "strikethrough", - "underline", - "code", - "color" - ] + "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/annotations" }, "plain_text": { "type": "string" diff --git a/schema/block/types/rich_text/text/text_schema.json b/schema/block/types/rich_text/text/text_schema.json index d6751c5..70db841 100644 --- a/schema/block/types/rich_text/text/text_schema.json +++ b/schema/block/types/rich_text/text/text_schema.json @@ -27,35 +27,7 @@ "required": ["content", "link"] }, "annotations": { - "type": "object", - "properties": { - "bold": { - "type": "boolean" - }, - "italic": { - "type": "boolean" - }, - "strikethrough": { - "type": "boolean" - }, - "underline": { - "type": "boolean" - }, - "code": { - "type": "boolean" - }, - "color": { - "type": "string" - } - }, - "required": [ - "bold", - "italic", - "strikethrough", - "underline", - "code", - "color" - ] + "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/annotations" }, "plain_text": { "type": "string" diff --git a/schema/shared_definitions/shared_definitions_schema.json b/schema/shared_definitions/shared_definitions_schema.json index a83312c..8ad6195 100644 --- a/schema/shared_definitions/shared_definitions_schema.json +++ b/schema/shared_definitions/shared_definitions_schema.json @@ -25,6 +25,47 @@ "red_background", "yellow_background" ] + }, + "annotations": { + "type": "object", + "customTypePath": "jsondoc.models.shared_definitions.Annotations", + "properties": { + "bold": { + "type": "boolean", + "default": false + }, + "italic": { + "type": "boolean", + "default": false + }, + "strikethrough": { + "type": "boolean", + "default": false + }, + "underline": { + "type": "boolean", + "default": false + }, + "code": { + "type": "boolean", + "default": false + }, + "color": { + "type": "string", + "default": "default" + // TODO: Fix resolution of this reference + // "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" + } + } + // notion-diverge + // "required": [ + // "bold", + // "italic", + // "strikethrough", + // "underline", + // "code", + // "color" + // ] } } } From d1144dcb7cffb3205de02442876c7af4272eed2b Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:39:35 +0200 Subject: [PATCH 11/45] Default const values, create mermaid diagram for HTML --- docs/html-conversion.md | 2 + examples/html/html_nested_elements.html | 1 + examples/html/html_to_mermaid.py | 97 +++++++++++++++++++ jsondoc/convert/utils.py | 75 +++++++++----- jsondoc/models/block/__init__.py | 2 +- jsondoc/models/block/base/__init__.py | 10 +- .../types/bulleted_list_item/__init__.py | 4 +- jsondoc/models/block/types/code/__init__.py | 4 +- jsondoc/models/block/types/column/__init__.py | 4 +- .../block/types/column_list/__init__.py | 4 +- .../models/block/types/divider/__init__.py | 4 +- .../models/block/types/equation/__init__.py | 4 +- .../models/block/types/heading_1/__init__.py | 4 +- .../models/block/types/heading_2/__init__.py | 4 +- .../models/block/types/heading_3/__init__.py | 4 +- jsondoc/models/block/types/image/__init__.py | 4 +- .../types/image/external_image/__init__.py | 2 +- .../block/types/image/file_image/__init__.py | 2 +- .../types/numbered_list_item/__init__.py | 4 +- .../models/block/types/paragraph/__init__.py | 4 +- jsondoc/models/block/types/quote/__init__.py | 4 +- .../models/block/types/rich_text/__init__.py | 2 +- .../block/types/rich_text/base/__init__.py | 2 +- .../types/rich_text/equation/__init__.py | 4 +- .../block/types/rich_text/text/__init__.py | 4 +- jsondoc/models/block/types/table/__init__.py | 4 +- .../models/block/types/table_row/__init__.py | 4 +- jsondoc/models/block/types/to_do/__init__.py | 4 +- jsondoc/models/block/types/toggle/__init__.py | 4 +- jsondoc/models/file/__init__.py | 2 +- jsondoc/models/file/base/__init__.py | 2 +- jsondoc/models/file/external/__init__.py | 2 +- jsondoc/models/file/file/__init__.py | 2 +- jsondoc/models/page/__init__.py | 8 +- jsondoc/models/shared_definitions/__init__.py | 2 +- schema/block/base/base_schema.json | 16 +-- .../bulleted_list_item_schema.json | 4 +- schema/block/types/code/code_schema.json | 4 +- schema/block/types/column/column_schema.json | 4 +- .../types/column_list/column_list_schema.json | 4 +- .../block/types/divider/divider_schema.json | 4 +- .../block/types/equation/equation_schema.json | 4 +- .../types/heading_1/heading_1_schema.json | 4 +- .../types/heading_2/heading_2_schema.json | 4 +- .../types/heading_3/heading_3_schema.json | 4 +- schema/block/types/image/image_schema.json | 4 +- .../numbered_list_item_schema.json | 4 +- .../types/paragraph/paragraph_schema.json | 4 +- schema/block/types/quote/quote_schema.json | 4 +- .../rich_text/equation/equation_schema.json | 3 +- .../types/rich_text/text/text_schema.json | 3 +- schema/block/types/table/table_schema.json | 4 +- .../types/table_row/table_row_schema.json | 4 +- schema/block/types/to_do/to_do_schema.json | 4 +- schema/block/types/toggle/toggle_schema.json | 4 +- schema/file/external/external_schema.json | 4 +- schema/file/file/file_schema.json | 4 +- schema/page/page_schema.json | 12 ++- scripts/autogen_pydantic.py | 1 + 59 files changed, 286 insertions(+), 114 deletions(-) create mode 100644 docs/html-conversion.md create mode 100644 examples/html/html_to_mermaid.py diff --git a/docs/html-conversion.md b/docs/html-conversion.md new file mode 100644 index 0000000..6edca55 --- /dev/null +++ b/docs/html-conversion.md @@ -0,0 +1,2 @@ + +- Terminal text nodes are to be converted to rich text blocks. diff --git a/examples/html/html_nested_elements.html b/examples/html/html_nested_elements.html index dd81161..c48829d 100644 --- a/examples/html/html_nested_elements.html +++ b/examples/html/html_nested_elements.html @@ -51,6 +51,7 @@

An example of nested HTML elements

This emphasized text is also nested.And here's some strong text, nested as well.

+

This is a bold word and this is an emphasized word.

" diff --git a/examples/html/html_to_mermaid.py b/examples/html/html_to_mermaid.py new file mode 100644 index 0000000..56b0758 --- /dev/null +++ b/examples/html/html_to_mermaid.py @@ -0,0 +1,97 @@ +from bs4 import BeautifulSoup + + +def escape(text): + return text.replace('"', """) + + +def traverse_node(node, parent_id, mermaid_lines, node_counter): + """ + Recursively traverses an HTML node and builds Mermaid lines for a tree diagram. + + Args: + node (BeautifulSoup element): The current HTML node. + parent_id (str): The ID of the parent node. + mermaid_lines (list): List of Mermaid lines describing the tree structure. + node_counter (list): Counter to keep track of node IDs. + """ + # Generate a unique ID for the current node + node_id = f"node{node_counter[0]}" + node_counter[0] += 1 + + # Determine the label for the current node + if node.name: # Element node + label = f"<{node.name}>" + else: # Text node + # label = node.strip() if node.strip() else "Text" + text_ = node.strip() + truncated_text = text_[:5] + # Escape double quotes in the text + + if len(text_) > 10: + label = escape(truncated_text) + f"... ({len(text_)} chars)" + else: + label = escape(text_) + + # Add the Mermaid line for the current node + mermaid_lines.append(f'{parent_id} --> {node_id}["{label}"]') + + # Recursively process child nodes + if hasattr(node, "children"): # Check if the node has children + for child in node.children: + if child.name or (child.string and child.string.strip()): + traverse_node(child, node_id, mermaid_lines, node_counter) + + +def generate_mermaid_diagram(html_content): + """ + Generates a Mermaid diagram for the given HTML content. + + Args: + html_content (str): The HTML content to parse. + + Returns: + str: The Mermaid diagram code. + """ + # Parse HTML with BeautifulSoup + soup = BeautifulSoup(html_content, "html.parser") + + # Initialize Mermaid diagram lines and node counter + mermaid_lines = ["graph TD;"] + node_counter = [1] # Using a list to pass by reference + + # Start traversal from the root element (usually ) + root = soup.find() # Get the first root element + root_id = "root" + mermaid_lines.append(f'{root_id}["<{root.name}>"]') + + # Traverse the HTML tree starting from the root + if hasattr(root, "children"): + for child in root.children: + if child.name or (child.string and child.string.strip()): + traverse_node(child, root_id, mermaid_lines, node_counter) + + # Join all lines into a single Mermaid diagram string + return "\n".join(mermaid_lines) + + +if __name__ == "__main__": + # # Example usage + # html_content = ( + # "

This is a bold word and this is an emphasized word.

" + # ) + # mermaid_diagram = generate_mermaid_diagram(html_content) + # print(mermaid_diagram) + import argparse + + parser = argparse.ArgumentParser( + description="Convert HTML content to a Mermaid diagram" + ) + parser.add_argument("html_file", help="Path to the HTML file to convert") + + args = parser.parse_args() + + with open(args.html_file, "r") as file: + html_content = file.read() + mermaid_diagram = generate_mermaid_diagram(html_content) + print(mermaid_diagram) diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index b843569..4da1e64 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -1,37 +1,62 @@ from datetime import datetime, timezone +import logging from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock +from jsondoc.models.block.types.rich_text.equation import ( + RichTextEquation, + Equation as EquationObj, +) from jsondoc.models.block.types.rich_text.text import Link, RichTextText, Text from jsondoc.models.shared_definitions import Annotations from jsondoc.utils import generate_id def create_rich_text( - text: str, - url: str = None, - bold: bool = False, - italic: bool = False, - strikethrough: bool = False, - underline: bool = False, - code: bool = False, - color: str = "default", + text: str | None = None, + url: str | None = None, + equation: str | None = None, + bold: bool | None = None, + italic: bool | None = None, + strikethrough: bool | None = None, + underline: bool | None = None, + code: bool | None = None, + color: str | None = None, ) -> RichTextText: - return RichTextText( - type="text", - text=Text( - content=text, - link=Link(url=url) if url else None, - ), - annotations=Annotations( - bold=bold, - italic=italic, - strikethrough=strikethrough, - underline=underline, - code=code, - color=color, - ), - plain_text=text, + if text is None and equation is None: + raise ValueError("Either text or equation must be provided") + + if text is not None and equation is not None: + raise ValueError("Only one of text or equation must be provided") + + annotations = Annotations( + bold=bold, + italic=italic, + strikethrough=strikethrough, + underline=underline, + code=code, + color=color, ) + if equation is not None: + if url is not None: + logging.warning("URL is not supported for equations, ignoring the URL") + + ret = RichTextEquation( + equation=RichTextEquation(expression=equation), + annotations=annotations, + plain_text=equation, + ) + elif text is not None: + ret = RichTextText( + text=Text( + content=text, + link=Link(url=url) if url else None, + ), + annotations=annotations, + plain_text=text, + ) + + return ret + def create_paragraph_block( text: str, @@ -46,9 +71,7 @@ def create_paragraph_block( return ParagraphBlock( id=id, - object="block", created_time=created_time, - type="paragraph", paragraph=Paragraph( rich_text=[ create_rich_text(text, **kwargs), @@ -58,4 +81,4 @@ def create_paragraph_block( ) -# print(create_paragraph_block("Hello, world!")) +print(create_paragraph_block(text="Hello, world!")) diff --git a/jsondoc/models/block/__init__.py b/jsondoc/models/block/__init__.py index 571066f..d08211a 100644 --- a/jsondoc/models/block/__init__.py +++ b/jsondoc/models/block/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/base/__init__.py b/jsondoc/models/block/base/__init__.py index f109713..e3a39f9 100644 --- a/jsondoc/models/block/base/__init__.py +++ b/jsondoc/models/block/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -36,7 +36,7 @@ class LastEditedBy(BaseModel): class BlockBase(BaseModel): - object: Literal['block'] + object: Literal['block'] = 'block' id: str parent: Optional[Parent] = None type: str @@ -44,7 +44,7 @@ class BlockBase(BaseModel): created_by: Optional[CreatedBy] = None last_edited_time: Optional[AwareDatetime] = None last_edited_by: Optional[LastEditedBy] = None - archived: Optional[bool] = None - in_trash: Optional[bool] = None - has_children: bool + archived: Optional[bool] = False + in_trash: Optional[bool] = False + has_children: Optional[bool] = False metadata: Optional[Dict[str, Any]] = None diff --git a/jsondoc/models/block/types/bulleted_list_item/__init__.py b/jsondoc/models/block/types/bulleted_list_item/__init__.py index 4c865e8..6ff3819 100644 --- a/jsondoc/models/block/types/bulleted_list_item/__init__.py +++ b/jsondoc/models/block/types/bulleted_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -26,6 +26,6 @@ class BulletedListItemBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['bulleted_list_item'] + type: Literal['bulleted_list_item'] = 'bulleted_list_item' bulleted_list_item: BulletedListItem children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/code/__init__.py b/jsondoc/models/block/types/code/__init__.py index cacd763..42de72e 100644 --- a/jsondoc/models/block/types/code/__init__.py +++ b/jsondoc/models/block/types/code/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -99,5 +99,5 @@ class Code(BaseModel): class CodeBlock(BlockBase): - type: Literal['code'] + type: Literal['code'] = 'code' code: Code diff --git a/jsondoc/models/block/types/column/__init__.py b/jsondoc/models/block/types/column/__init__.py index c31b4d5..56759d1 100644 --- a/jsondoc/models/block/types/column/__init__.py +++ b/jsondoc/models/block/types/column/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -15,6 +15,6 @@ class ColumnBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['column'] + type: Literal['column'] = 'column' column: Dict[str, Any] children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/column_list/__init__.py b/jsondoc/models/block/types/column_list/__init__.py index 49a5ac0..f946a7a 100644 --- a/jsondoc/models/block/types/column_list/__init__.py +++ b/jsondoc/models/block/types/column_list/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -16,6 +16,6 @@ class ColumnListBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['column_list'] + type: Literal['column_list'] = 'column_list' column_list: Dict[str, Any] children: Optional[List[ColumnBlock]] = None diff --git a/jsondoc/models/block/types/divider/__init__.py b/jsondoc/models/block/types/divider/__init__.py index 98373fc..a5eb80b 100644 --- a/jsondoc/models/block/types/divider/__init__.py +++ b/jsondoc/models/block/types/divider/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -11,5 +11,5 @@ class DividerBlock(BlockBase): - type: Literal['divider'] + type: Literal['divider'] = 'divider' divider: Dict[str, Any] diff --git a/jsondoc/models/block/types/equation/__init__.py b/jsondoc/models/block/types/equation/__init__.py index 09e992a..e1e686c 100644 --- a/jsondoc/models/block/types/equation/__init__.py +++ b/jsondoc/models/block/types/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -17,5 +17,5 @@ class Equation(BaseModel): class EquationBlock(BlockBase): - type: Literal['equation'] + type: Literal['equation'] = 'equation' equation: Equation diff --git a/jsondoc/models/block/types/heading_1/__init__.py b/jsondoc/models/block/types/heading_1/__init__.py index 1397767..cb510cf 100644 --- a/jsondoc/models/block/types/heading_1/__init__.py +++ b/jsondoc/models/block/types/heading_1/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -24,5 +24,5 @@ class Heading1(BaseModel): class Heading1Block(BlockBase): - type: Literal['heading_1'] + type: Literal['heading_1'] = 'heading_1' heading_1: Heading1 diff --git a/jsondoc/models/block/types/heading_2/__init__.py b/jsondoc/models/block/types/heading_2/__init__.py index 35fcc82..273e750 100644 --- a/jsondoc/models/block/types/heading_2/__init__.py +++ b/jsondoc/models/block/types/heading_2/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -23,5 +23,5 @@ class Heading2(BaseModel): class Heading2Block(BlockBase): - type: Literal['heading_2'] + type: Literal['heading_2'] = 'heading_2' heading_2: Heading2 diff --git a/jsondoc/models/block/types/heading_3/__init__.py b/jsondoc/models/block/types/heading_3/__init__.py index b912381..9b507d7 100644 --- a/jsondoc/models/block/types/heading_3/__init__.py +++ b/jsondoc/models/block/types/heading_3/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -23,5 +23,5 @@ class Heading3(BaseModel): class Heading3Block(BlockBase): - type: Literal['heading_3'] + type: Literal['heading_3'] = 'heading_3' heading_3: Heading3 diff --git a/jsondoc/models/block/types/image/__init__.py b/jsondoc/models/block/types/image/__init__.py index 79c8f5f..a244a4c 100644 --- a/jsondoc/models/block/types/image/__init__.py +++ b/jsondoc/models/block/types/image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -14,5 +14,5 @@ class ImageBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['image'] + type: Literal['image'] = 'image' image: FileBase diff --git a/jsondoc/models/block/types/image/external_image/__init__.py b/jsondoc/models/block/types/image/external_image/__init__.py index 2fa7816..d4e3241 100644 --- a/jsondoc/models/block/types/image/external_image/__init__.py +++ b/jsondoc/models/block/types/image/external_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/file_image/__init__.py b/jsondoc/models/block/types/image/file_image/__init__.py index 3464307..53779d3 100644 --- a/jsondoc/models/block/types/image/file_image/__init__.py +++ b/jsondoc/models/block/types/image/file_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/numbered_list_item/__init__.py b/jsondoc/models/block/types/numbered_list_item/__init__.py index 15a89f6..f5f30ea 100644 --- a/jsondoc/models/block/types/numbered_list_item/__init__.py +++ b/jsondoc/models/block/types/numbered_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -26,6 +26,6 @@ class NumberedListItemBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['numbered_list_item'] + type: Literal['numbered_list_item'] = 'numbered_list_item' numbered_list_item: NumberedListItem children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/paragraph/__init__.py b/jsondoc/models/block/types/paragraph/__init__.py index 6cedfde..6e4a0ba 100644 --- a/jsondoc/models/block/types/paragraph/__init__.py +++ b/jsondoc/models/block/types/paragraph/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -26,6 +26,6 @@ class ParagraphBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['paragraph'] + type: Literal['paragraph'] = 'paragraph' paragraph: Paragraph children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/quote/__init__.py b/jsondoc/models/block/types/quote/__init__.py index c262b78..6f50bcc 100644 --- a/jsondoc/models/block/types/quote/__init__.py +++ b/jsondoc/models/block/types/quote/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -26,6 +26,6 @@ class QuoteBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['quote'] + type: Literal['quote'] = 'quote' quote: Quote children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/rich_text/__init__.py b/jsondoc/models/block/types/rich_text/__init__.py index 0be0b8b..2f57ea3 100644 --- a/jsondoc/models/block/types/rich_text/__init__.py +++ b/jsondoc/models/block/types/rich_text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/base/__init__.py b/jsondoc/models/block/types/rich_text/base/__init__.py index c546728..984c43d 100644 --- a/jsondoc/models/block/types/rich_text/base/__init__.py +++ b/jsondoc/models/block/types/rich_text/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/equation/__init__.py b/jsondoc/models/block/types/rich_text/equation/__init__.py index 44b4cf7..4b6a0ab 100644 --- a/jsondoc/models/block/types/rich_text/equation/__init__.py +++ b/jsondoc/models/block/types/rich_text/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -23,7 +23,7 @@ class RichTextEquation(RichTextBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['equation'] + type: Literal['equation'] = 'equation' equation: Equation annotations: Annotations plain_text: str diff --git a/jsondoc/models/block/types/rich_text/text/__init__.py b/jsondoc/models/block/types/rich_text/text/__init__.py index b28584b..0bf8350 100644 --- a/jsondoc/models/block/types/rich_text/text/__init__.py +++ b/jsondoc/models/block/types/rich_text/text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -25,7 +25,7 @@ class RichTextText(RichTextBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['text'] + type: Literal['text'] = 'text' text: Text annotations: Annotations plain_text: str diff --git a/jsondoc/models/block/types/table/__init__.py b/jsondoc/models/block/types/table/__init__.py index c1130ca..1cebed5 100644 --- a/jsondoc/models/block/types/table/__init__.py +++ b/jsondoc/models/block/types/table/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -25,6 +25,6 @@ class TableBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['table'] + type: Literal['table'] = 'table' table: Table children: Optional[List[TableRowBlock]] = None diff --git a/jsondoc/models/block/types/table_row/__init__.py b/jsondoc/models/block/types/table_row/__init__.py index c58953c..62b5d83 100644 --- a/jsondoc/models/block/types/table_row/__init__.py +++ b/jsondoc/models/block/types/table_row/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -21,5 +21,5 @@ class TableRow(BaseModel): class TableRowBlock(BlockBase): - type: Literal['table_row'] + type: Literal['table_row'] = 'table_row' table_row: TableRow diff --git a/jsondoc/models/block/types/to_do/__init__.py b/jsondoc/models/block/types/to_do/__init__.py index 0175983..e8925b8 100644 --- a/jsondoc/models/block/types/to_do/__init__.py +++ b/jsondoc/models/block/types/to_do/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -27,6 +27,6 @@ class ToDoBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['to_do'] + type: Literal['to_do'] = 'to_do' to_do: ToDo children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/block/types/toggle/__init__.py b/jsondoc/models/block/types/toggle/__init__.py index d14b19e..cf71f44 100644 --- a/jsondoc/models/block/types/toggle/__init__.py +++ b/jsondoc/models/block/types/toggle/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -25,6 +25,6 @@ class ToggleBlock(BlockBase): model_config = ConfigDict( arbitrary_types_allowed=True, ) - type: Literal['toggle'] + type: Literal['toggle'] = 'toggle' toggle: Toggle children: Optional[List[BlockBase]] = None diff --git a/jsondoc/models/file/__init__.py b/jsondoc/models/file/__init__.py index 5b16fa8..5312252 100644 --- a/jsondoc/models/file/__init__.py +++ b/jsondoc/models/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/base/__init__.py b/jsondoc/models/file/base/__init__.py index c704e5c..6ec1a9a 100644 --- a/jsondoc/models/file/base/__init__.py +++ b/jsondoc/models/file/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/external/__init__.py b/jsondoc/models/file/external/__init__.py index 07a3654..3de34ca 100644 --- a/jsondoc/models/file/external/__init__.py +++ b/jsondoc/models/file/external/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/file/__init__.py b/jsondoc/models/file/file/__init__.py index 52e0896..deec346 100644 --- a/jsondoc/models/file/file/__init__.py +++ b/jsondoc/models/file/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/jsondoc/models/page/__init__.py b/jsondoc/models/page/__init__.py index e1fd4d9..3442f11 100644 --- a/jsondoc/models/page/__init__.py +++ b/jsondoc/models/page/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations @@ -25,7 +25,7 @@ class CreatedBy(BaseModel): model_config = ConfigDict( extra='forbid', ) - object: Literal['user'] + object: Literal['user'] = 'user' id: str @@ -33,7 +33,7 @@ class LastEditedBy(BaseModel): model_config = ConfigDict( extra='forbid', ) - object: Literal['user'] + object: Literal['user'] = 'user' id: str @@ -70,7 +70,7 @@ class Page(BaseModel): extra='forbid', arbitrary_types_allowed=True, ) - object: Literal['page'] + object: Literal['page'] = 'page' id: str parent: Optional[Parent] = None created_time: AwareDatetime diff --git a/jsondoc/models/shared_definitions/__init__.py b/jsondoc/models/shared_definitions/__init__.py index 0c57e50..1d9d7a9 100644 --- a/jsondoc/models/shared_definitions/__init__.py +++ b/jsondoc/models/shared_definitions/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-04T22:43:27+00:00 +# timestamp: 2024-09-05T14:27:19+00:00 from __future__ import annotations diff --git a/schema/block/base/base_schema.json b/schema/block/base/base_schema.json index fa40c04..ccbd9f9 100644 --- a/schema/block/base/base_schema.json +++ b/schema/block/base/base_schema.json @@ -6,7 +6,8 @@ "properties": { "object": { "type": "string", - "const": "block" + "const": "block", + "default": "block" }, "id": { "type": "string" @@ -104,13 +105,16 @@ "additionalProperties": false }, "archived": { - "type": "boolean" + "type": "boolean", + "default": false }, "in_trash": { - "type": "boolean" + "type": "boolean", + "default": false }, "has_children": { - "type": "boolean" + "type": "boolean", + "default": false }, // notion-diverge "metadata": { @@ -122,13 +126,13 @@ "object", "id", "type", - "created_time", + "created_time" // notion-diverge // "created_by", // These are optional since we can be out of Notion context // "last_edited_time", // "last_edited_by", // "archived", - "has_children" + // "has_children", ] // This was meant to refer to the object that contains type-specific block // "additionalProperties": { diff --git a/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json b/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json index dccd10b..633af45 100644 --- a/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json +++ b/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json @@ -7,7 +7,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "bulleted_list_item" + "type": "string", + "const": "bulleted_list_item", + "default": "bulleted_list_item" }, "bulleted_list_item": { "type": "object", diff --git a/schema/block/types/code/code_schema.json b/schema/block/types/code/code_schema.json index a74442d..c3bdb29 100644 --- a/schema/block/types/code/code_schema.json +++ b/schema/block/types/code/code_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "code" + "type": "string", + "const": "code", + "default": "code" }, "code": { "type": "object", diff --git a/schema/block/types/column/column_schema.json b/schema/block/types/column/column_schema.json index f8ce176..c1575fe 100644 --- a/schema/block/types/column/column_schema.json +++ b/schema/block/types/column/column_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "column" + "type": "string", + "const": "column", + "default": "column" }, "column": { "type": "object", diff --git a/schema/block/types/column_list/column_list_schema.json b/schema/block/types/column_list/column_list_schema.json index 98dece6..d287a17 100644 --- a/schema/block/types/column_list/column_list_schema.json +++ b/schema/block/types/column_list/column_list_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "column_list" + "type": "string", + "const": "column_list", + "default": "column_list" }, "column_list": { "type": "object", diff --git a/schema/block/types/divider/divider_schema.json b/schema/block/types/divider/divider_schema.json index 54a382d..90df9fe 100644 --- a/schema/block/types/divider/divider_schema.json +++ b/schema/block/types/divider/divider_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "divider" + "type": "string", + "const": "divider", + "default": "divider" }, "divider": { "type": "object", diff --git a/schema/block/types/equation/equation_schema.json b/schema/block/types/equation/equation_schema.json index 871fa2c..6aaf71a 100644 --- a/schema/block/types/equation/equation_schema.json +++ b/schema/block/types/equation/equation_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "equation" + "type": "string", + "const": "equation", + "default": "equation" }, "equation": { "type": "object", diff --git a/schema/block/types/heading_1/heading_1_schema.json b/schema/block/types/heading_1/heading_1_schema.json index 3ae08d4..a973e84 100644 --- a/schema/block/types/heading_1/heading_1_schema.json +++ b/schema/block/types/heading_1/heading_1_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "heading_1" + "type": "string", + "const": "heading_1", + "default": "heading_1" }, "heading_1": { "type": "object", diff --git a/schema/block/types/heading_2/heading_2_schema.json b/schema/block/types/heading_2/heading_2_schema.json index b623b1f..abdfa61 100644 --- a/schema/block/types/heading_2/heading_2_schema.json +++ b/schema/block/types/heading_2/heading_2_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "heading_2" + "type": "string", + "const": "heading_2", + "default": "heading_2" }, "heading_2": { "type": "object", diff --git a/schema/block/types/heading_3/heading_3_schema.json b/schema/block/types/heading_3/heading_3_schema.json index 227591f..52bab87 100644 --- a/schema/block/types/heading_3/heading_3_schema.json +++ b/schema/block/types/heading_3/heading_3_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "heading_3" + "type": "string", + "const": "heading_3", + "default": "heading_3" }, "heading_3": { "type": "object", diff --git a/schema/block/types/image/image_schema.json b/schema/block/types/image/image_schema.json index a5ffb74..4c0b8fe 100644 --- a/schema/block/types/image/image_schema.json +++ b/schema/block/types/image/image_schema.json @@ -10,7 +10,9 @@ ], "properties": { "type": { - "const": "image" + "type": "string", + "const": "image", + "default": "image" }, "image": { "type": "object", diff --git a/schema/block/types/numbered_list_item/numbered_list_item_schema.json b/schema/block/types/numbered_list_item/numbered_list_item_schema.json index fa9cbfc..25c4d24 100644 --- a/schema/block/types/numbered_list_item/numbered_list_item_schema.json +++ b/schema/block/types/numbered_list_item/numbered_list_item_schema.json @@ -7,7 +7,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "numbered_list_item" + "type": "string", + "const": "numbered_list_item", + "default": "numbered_list_item" }, "numbered_list_item": { "type": "object", diff --git a/schema/block/types/paragraph/paragraph_schema.json b/schema/block/types/paragraph/paragraph_schema.json index 1c3fd21..f9c570d 100644 --- a/schema/block/types/paragraph/paragraph_schema.json +++ b/schema/block/types/paragraph/paragraph_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "paragraph" + "type": "string", + "const": "paragraph", + "default": "paragraph" }, "paragraph": { "type": "object", diff --git a/schema/block/types/quote/quote_schema.json b/schema/block/types/quote/quote_schema.json index 4a6e718..a2fcd89 100644 --- a/schema/block/types/quote/quote_schema.json +++ b/schema/block/types/quote/quote_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "quote" + "type": "string", + "const": "quote", + "default": "quote" }, "quote": { "type": "object", diff --git a/schema/block/types/rich_text/equation/equation_schema.json b/schema/block/types/rich_text/equation/equation_schema.json index b9e56a2..8e47a6f 100644 --- a/schema/block/types/rich_text/equation/equation_schema.json +++ b/schema/block/types/rich_text/equation/equation_schema.json @@ -6,7 +6,8 @@ "properties": { "type": { "type": "string", - "const": "equation" + "const": "equation", + "default": "equation" }, "equation": { "type": "object", diff --git a/schema/block/types/rich_text/text/text_schema.json b/schema/block/types/rich_text/text/text_schema.json index 70db841..678ac58 100644 --- a/schema/block/types/rich_text/text/text_schema.json +++ b/schema/block/types/rich_text/text/text_schema.json @@ -6,7 +6,8 @@ "properties": { "type": { "type": "string", - "const": "text" + "const": "text", + "default": "text" }, "text": { "type": "object", diff --git a/schema/block/types/table/table_schema.json b/schema/block/types/table/table_schema.json index 79220cf..1b23320 100644 --- a/schema/block/types/table/table_schema.json +++ b/schema/block/types/table/table_schema.json @@ -7,7 +7,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "table" + "type": "string", + "const": "table", + "default": "table" }, "table": { "type": "object", diff --git a/schema/block/types/table_row/table_row_schema.json b/schema/block/types/table_row/table_row_schema.json index c246a35..c299af4 100644 --- a/schema/block/types/table_row/table_row_schema.json +++ b/schema/block/types/table_row/table_row_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "table_row" + "type": "string", + "const": "table_row", + "default": "table_row" }, "table_row": { "type": "object", diff --git a/schema/block/types/to_do/to_do_schema.json b/schema/block/types/to_do/to_do_schema.json index 232faa6..5f02b54 100644 --- a/schema/block/types/to_do/to_do_schema.json +++ b/schema/block/types/to_do/to_do_schema.json @@ -7,7 +7,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "to_do" + "type": "string", + "const": "to_do", + "default": "to_do" }, "to_do": { "type": "object", diff --git a/schema/block/types/toggle/toggle_schema.json b/schema/block/types/toggle/toggle_schema.json index c745af2..d0180f7 100644 --- a/schema/block/types/toggle/toggle_schema.json +++ b/schema/block/types/toggle/toggle_schema.json @@ -6,7 +6,9 @@ "allOf": [{ "$ref": "/block/base/base_schema.json" }], "properties": { "type": { - "const": "toggle" + "type": "string", + "const": "toggle", + "default": "toggle" }, "toggle": { "type": "object", diff --git a/schema/file/external/external_schema.json b/schema/file/external/external_schema.json index e8a2662..490737b 100644 --- a/schema/file/external/external_schema.json +++ b/schema/file/external/external_schema.json @@ -5,7 +5,9 @@ "type": "object", "properties": { "type": { - "const": "external" + "type": "string", + "const": "external", + "default": "external" }, "external": { "type": "object", diff --git a/schema/file/file/file_schema.json b/schema/file/file/file_schema.json index 337489e..213ef65 100644 --- a/schema/file/file/file_schema.json +++ b/schema/file/file/file_schema.json @@ -5,7 +5,9 @@ "type": "object", "properties": { "type": { - "const": "file" + "type": "string", + "const": "file", + "default": "file" }, "file": { "type": "object", diff --git a/schema/page/page_schema.json b/schema/page/page_schema.json index a7aa2f2..9c516d0 100644 --- a/schema/page/page_schema.json +++ b/schema/page/page_schema.json @@ -6,7 +6,8 @@ "properties": { "object": { "type": "string", - "const": "page" + "const": "page", + "default": "page" }, "id": { "type": "string" @@ -34,7 +35,8 @@ "properties": { "object": { "type": "string", - "const": "user" + "const": "user", + "default": "user" }, "id": { "type": "string" @@ -52,7 +54,8 @@ "properties": { "object": { "type": "string", - "const": "user" + "const": "user", + "default": "user" }, "id": { "type": "string" @@ -93,7 +96,8 @@ }, "type": { "type": "string", - "const": "title" + "const": "title", + "default": "title" }, "title": { "type": "array", diff --git a/scripts/autogen_pydantic.py b/scripts/autogen_pydantic.py index 9295129..54f12d8 100644 --- a/scripts/autogen_pydantic.py +++ b/scripts/autogen_pydantic.py @@ -116,6 +116,7 @@ def resolve_refs(obj): input_filename="example.json", output=output, output_model_type=DataModelType.PydanticV2BaseModel, + apply_default_values_for_required_fields=True, ) model: str = output.read_text() From 5ba5a4ecc1671eebe9875fe9f04abe86c2a3246b Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Thu, 5 Sep 2024 21:22:13 +0200 Subject: [PATCH 12/45] wip --- jsondoc/convert/html.py | 597 ++++++++++++++++++++++------------ jsondoc/convert/utils.py | 232 +++++++++++-- tests/test_html_to_jsondoc.py | 3 +- 3 files changed, 605 insertions(+), 227 deletions(-) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 81ce683..78e1558 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -3,7 +3,18 @@ import re import six -from jsondoc.convert.utils import create_paragraph_block +from jsondoc.convert.utils import ( + create_code_block, + create_divider_block, + create_h1_block, + create_h2_block, + create_h3_block, + create_image_block, + create_paragraph_block, + create_quote_block, + create_rich_text, +) +from jsondoc.models.block.base import BlockBase from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock from jsondoc.models.block.types.code import CodeBlock from jsondoc.models.block.types.column import ColumnBlock @@ -17,11 +28,13 @@ from jsondoc.models.block.types.numbered_list_item import NumberedListItemBlock from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock from jsondoc.models.block.types.quote import QuoteBlock +from jsondoc.models.block.types.rich_text.base import RichTextBase from jsondoc.models.block.types.rich_text.text import RichTextText from jsondoc.models.block.types.table import TableBlock from jsondoc.models.block.types.table_row import TableRowBlock from jsondoc.models.block.types.to_do import ToDoBlock from jsondoc.models.block.types.toggle import ToggleBlock +from jsondoc.models.shared_definitions import Annotations convert_heading_re = re.compile(r"convert_h(\d+)") @@ -68,18 +81,33 @@ def abstract_inline_conversion(markup_fn): references to self.strong_em_symbol etc. """ - def implementation(self, el, text, convert_as_inline): - markup_prefix = markup_fn(self) - if markup_prefix.startswith("<") and markup_prefix.endswith(">"): - markup_suffix = ""): + # markup_suffix = "" % href - if self.options["default_title"] and not title: - title = href - title_part = ' "%s"' % title.replace('"', r"\"") if title else "" - return ( - "%s[%s](%s%s)%s" % (prefix, text, href, title_part, suffix) - if href - else text - ) + # if ( + # self.options["autolinks"] + # and text.replace(r"\_", "_") == href + # and not title + # and not self.options["default_title"] + # ): + # # Shortcut syntax + # return "<%s>" % href + + # if self.options["default_title"] and not title: + # title = href + + # title_part = ' "%s"' % title.replace('"', r"\"") if title else "" + return [ + create_rich_text(text=prefix), + create_rich_text(text=text, url=href), + create_rich_text(text=suffix), + ] + # return ( + # "%s[%s](%s%s)%s" % (prefix, text, href, title_part, suffix) + # if href + # else text + # ) convert_b = abstract_inline_conversion( - lambda self: 2 * self.options["strong_em_symbol"] + lambda self: Annotations(bold=True) # 2 * self.options["strong_em_symbol"] ) - def convert_blockquote(self, el, text, convert_as_inline): + def convert_blockquote(self, el, convert_as_inline): + text = el.get_text() + # if convert_as_inline: + # return text + # return ( + # "\n" + (line_beginning_re.sub("> ", text.strip()) + "\n\n") if text else "" + # ) + if not text: + return None + + # if convert_as_inline: + # return create_rich_text(text=text) + + # TODO: If text has newlines, split them and add 2, 3, ... lines as children + return create_quote_block() + + def convert_br(self, el, convert_as_inline): + # if convert_as_inline: + # return "" + + # if self.options["newline_style"].lower() == BACKSLASH: + # return "\\\n" + # else: + # return " \n" + return None # TBD + + def convert_code(self, el, convert_as_inline): + text = el.get_text() + # if el.parent.name == "pre": + # return text + # converter = abstract_inline_conversion( + # "`", + # ) + # return converter(self, el, convert_as_inline) + if el.parent.name == "pre": + return create_rich_text() - if convert_as_inline: - return text + converter = abstract_inline_conversion(lambda self: Annotations(code=True)) + return converter(self, el, convert_as_inline) + + convert_del = abstract_inline_conversion( + lambda self: Annotations(strikethrough=True) + # "~~" + ) + + convert_em = abstract_inline_conversion( + lambda self: Annotations(italic=True) + # self.options["strong_em_symbol"] + ) - return ( - "\n" + (line_beginning_re.sub("> ", text.strip()) + "\n\n") if text else "" - ) + convert_kbd = convert_code - def convert_br(self, el, text, convert_as_inline): + # def convert_hn(self, n, el, text, convert_as_inline): + # if convert_as_inline: + # return text + + # style = self.options["heading_style"].lower() + # text = text.strip() + # if style == UNDERLINED and n <= 2: + # line = "=" if n == 1 else "-" + # return self.underline(text, line) + # hashes = "#" * n + # if style == ATX_CLOSED: + # return "%s %s %s\n\n" % (hashes, text, hashes) + # return "%s %s\n\n" % (hashes, text) + def convert_h1(self, el, convert_as_inline): + text = el.get_text() if convert_as_inline: - return "" + return create_rich_text() - if self.options["newline_style"].lower() == BACKSLASH: - return "\\\n" - else: - return " \n" + return create_h1_block() - def convert_code(self, el, text, convert_as_inline): - if el.parent.name == "pre": - return text - converter = abstract_inline_conversion(lambda self: "`") - return converter(self, el, text, convert_as_inline) + def convert_h2(self, el, convert_as_inline): + text = el.get_text() + if convert_as_inline: + return create_rich_text() - convert_del = abstract_inline_conversion(lambda self: "~~") + return create_h2_block() - convert_em = abstract_inline_conversion( - lambda self: self.options["strong_em_symbol"] - ) + def convert_h3(self, el, convert_as_inline): + text = el.get_text() + if convert_as_inline: + return create_rich_text() - convert_kbd = convert_code + return create_h3_block() - def convert_hn(self, n, el, text, convert_as_inline): + def convert_h4(self, el, convert_as_inline): + text = el.get_text() if convert_as_inline: - return text + return create_rich_text() - style = self.options["heading_style"].lower() - text = text.strip() - if style == UNDERLINED and n <= 2: - line = "=" if n == 1 else "-" - return self.underline(text, line) - hashes = "#" * n - if style == ATX_CLOSED: - return "%s %s %s\n\n" % (hashes, text, hashes) - return "%s %s\n\n" % (hashes, text) + return create_paragraph_block() - def convert_hr(self, el, text, convert_as_inline): - return "\n\n---\n\n" + def convert_h5(self, el, convert_as_inline): + text = el.get_text() + if convert_as_inline: + return create_rich_text() - convert_i = convert_em + return create_paragraph_block() - def convert_img(self, el, text, convert_as_inline): - alt = el.attrs.get("alt", None) or "" - src = el.attrs.get("src", None) or "" - title = el.attrs.get("title", None) or "" - title_part = ' "%s"' % title.replace('"', r"\"") if title else "" - if ( - convert_as_inline - and el.parent.name not in self.options["keep_inline_images_in"] - ): - return alt + def convert_h6(self, el, convert_as_inline): + text = el.get_text() + if convert_as_inline: + return create_rich_text() - return "![%s](%s%s)" % (alt, src, title_part) + return create_paragraph_block() - def convert_list(self, el, text, convert_as_inline): + def convert_hr(self, el, convert_as_inline): + # return "\n\n---\n\n" + return create_divider_block() - # Converting a list to inline is undefined. - # Ignoring convert_to_inline for list. + convert_i = convert_em - nested = False - before_paragraph = False - if el.next_sibling and el.next_sibling.name not in ["ul", "ol"]: - before_paragraph = True - while el: - if el.name == "li": - nested = True - break - el = el.parent - if nested: - # remove trailing newline if nested - return "\n" + self.indent(text, 1).rstrip() - return text + ("\n" if before_paragraph else "") + def convert_img(self, el, convert_as_inline): + alt = el.attrs.get("alt", None) + src = el.attrs.get("src", None) + if not src: + return None + + # title = el.attrs.get("title", None) or "" + # title_part = ' "%s"' % title.replace('"', r"\"") if title else "" + # if ( + # convert_as_inline + # and el.parent.name not in self.options["keep_inline_images_in"] + # ): + # return alt + + # return "![%s](%s%s)" % (alt, src, title_part) + return create_image_block(url=src, caption=alt) + + def convert_list(self, el, convert_as_inline): + + # # Converting a list to inline is undefined. + # # Ignoring convert_to_inline for list. + + # nested = False + # before_paragraph = False + # if el.next_sibling and el.next_sibling.name not in ["ul", "ol"]: + # before_paragraph = True + # while el: + # if el.name == "li": + # nested = True + # break + # el = el.parent + # if nested: + # # remove trailing newline if nested + # return "\n" + self.indent(text, 1).rstrip() + # return text + ("\n" if before_paragraph else "") + return None # TBD convert_ul = convert_list convert_ol = convert_list - def convert_li(self, el, text, convert_as_inline): - parent = el.parent - if parent is not None and parent.name == "ol": - if parent.get("start") and str(parent.get("start")).isnumeric(): - start = int(parent.get("start")) - else: - start = 1 - bullet = "%s." % (start + parent.index(el)) - else: - depth = -1 - while el: - if el.name == "ul": - depth += 1 - el = el.parent - bullets = self.options["bullets"] - bullet = bullets[depth % len(bullets)] - return "%s %s\n" % (bullet, (text or "").strip()) - - def convert_p(self, el, text, convert_as_inline): - if convert_as_inline: - return text + def convert_li(self, el, convert_as_inline): + # parent = el.parent + # if parent is not None and parent.name == "ol": + # if parent.get("start") and str(parent.get("start")).isnumeric(): + # start = int(parent.get("start")) + # else: + # start = 1 + # bullet = "%s." % (start + parent.index(el)) + # else: + # depth = -1 + # while el: + # if el.name == "ul": + # depth += 1 + # el = el.parent + # bullets = self.options["bullets"] + # bullet = bullets[depth % len(bullets)] + # return "%s %s\n" % (bullet, (text or "").strip()) + return None # TBD + + def convert_p(self, el, convert_as_inline): + # text = el.get_text() + # if convert_as_inline: + # # return text + # return create_rich_text() # if self.options["wrap"]: # text = fill( @@ -416,26 +578,38 @@ def convert_p(self, el, text, convert_as_inline): # return "%s\n\n" % text if text else "" # return ParagraphBlock( # paragraph=Paragraph( - # rich_text=[RichTextText(text=text)], + # rich_text=[RichTextText()], # ) # ) - return create_paragraph_block(text=text) + return create_paragraph_block() + + def convert_pre(self, el, convert_as_inline): + text = el.get_text() + # if not text: + # return "" + # code_language = self.options["code_language"] - def convert_pre(self, el, text, convert_as_inline): + # if self.options["code_language_callback"]: + # code_language = self.options["code_language_callback"](el) or code_language + + # return "\n```%s\n%s\n```\n" % (code_language, text) if not text: - return "" + return None + code_language = self.options["code_language"] if self.options["code_language_callback"]: code_language = self.options["code_language_callback"](el) or code_language - return "\n```%s\n%s\n```\n" % (code_language, text) + return create_code_block(code=text, language=code_language) - def convert_script(self, el, text, convert_as_inline): - return "" + def convert_script(self, el, convert_as_inline): + # return "" + return None - def convert_style(self, el, text, convert_as_inline): - return "" + def convert_style(self, el, convert_as_inline): + # return "" + return None convert_s = convert_del @@ -443,64 +617,77 @@ def convert_style(self, el, text, convert_as_inline): convert_samp = convert_code - convert_sub = abstract_inline_conversion(lambda self: self.options["sub_symbol"]) - - convert_sup = abstract_inline_conversion(lambda self: self.options["sup_symbol"]) - - def convert_table(self, el, text, convert_as_inline): - return "\n\n" + text + "\n" - - def convert_caption(self, el, text, convert_as_inline): - return text + "\n" - - def convert_figcaption(self, el, text, convert_as_inline): - return "\n\n" + text + "\n\n" - - def convert_td(self, el, text, convert_as_inline): - colspan = 1 - if "colspan" in el.attrs and el["colspan"].isdigit(): - colspan = int(el["colspan"]) - return " " + text.strip().replace("\n", " ") + " |" * colspan + # Notion does not have an alternative for sub and sup tags + convert_sub = abstract_inline_conversion( + lambda self: Annotations() + # self.options["sub_symbol"], + ) - def convert_th(self, el, text, convert_as_inline): - colspan = 1 - if "colspan" in el.attrs and el["colspan"].isdigit(): - colspan = int(el["colspan"]) - return " " + text.strip().replace("\n", " ") + " |" * colspan + convert_sup = abstract_inline_conversion( + lambda self: Annotations() + # self.options["sup_symbol"], + ) - def convert_tr(self, el, text, convert_as_inline): - cells = el.find_all(["td", "th"]) - is_headrow = ( - all([cell.name == "th" for cell in cells]) - or (not el.previous_sibling and not el.parent.name == "tbody") - or ( - not el.previous_sibling - and el.parent.name == "tbody" - and len(el.parent.parent.find_all(["thead"])) < 1 - ) - ) - overline = "" - underline = "" - if is_headrow and not el.previous_sibling: - # first row and is headline: print headline underline - full_colspan = 0 - for cell in cells: - if "colspan" in cell.attrs and cell["colspan"].isdigit(): - full_colspan += int(cell["colspan"]) - else: - full_colspan += 1 - underline += "| " + " | ".join(["---"] * full_colspan) + " |" + "\n" - elif not el.previous_sibling and ( - el.parent.name == "table" - or (el.parent.name == "tbody" and not el.parent.previous_sibling) - ): - # first row, not headline, and: - # - the parent is table or - # - the parent is tbody at the beginning of a table. - # print empty headline above this row - overline += "| " + " | ".join([""] * len(cells)) + " |" + "\n" - overline += "| " + " | ".join(["---"] * len(cells)) + " |" + "\n" - return overline + "|" + text + "\n" + underline + def convert_table(self, el, convert_as_inline): + # return "\n\n" + text + "\n" + return None # TBD + + def convert_caption(self, el, convert_as_inline): + # return text + "\n" + return None # TBD + + def convert_figcaption(self, el, convert_as_inline): + # return "\n\n" + text + "\n\n" + return None # TBD + + def convert_td(self, el, convert_as_inline): + # colspan = 1 + # if "colspan" in el.attrs and el["colspan"].isdigit(): + # colspan = int(el["colspan"]) + # return " " + text.strip().replace("\n", " ") + " |" * colspan + return None # TBD + + def convert_th(self, el, convert_as_inline): + # colspan = 1 + # if "colspan" in el.attrs and el["colspan"].isdigit(): + # colspan = int(el["colspan"]) + # return " " + text.strip().replace("\n", " ") + " |" * colspan + return None # TBD + + def convert_tr(self, el, convert_as_inline): + # cells = el.find_all(["td", "th"]) + # is_headrow = ( + # all([cell.name == "th" for cell in cells]) + # or (not el.previous_sibling and not el.parent.name == "tbody") + # or ( + # not el.previous_sibling + # and el.parent.name == "tbody" + # and len(el.parent.parent.find_all(["thead"])) < 1 + # ) + # ) + # overline = "" + # underline = "" + # if is_headrow and not el.previous_sibling: + # # first row and is headline: print headline underline + # full_colspan = 0 + # for cell in cells: + # if "colspan" in cell.attrs and cell["colspan"].isdigit(): + # full_colspan += int(cell["colspan"]) + # else: + # full_colspan += 1 + # underline += "| " + " | ".join(["---"] * full_colspan) + " |" + "\n" + # elif not el.previous_sibling and ( + # el.parent.name == "table" + # or (el.parent.name == "tbody" and not el.parent.previous_sibling) + # ): + # # first row, not headline, and: + # # - the parent is table or + # # - the parent is tbody at the beginning of a table. + # # print empty headline above this row + # overline += "| " + " | ".join([""] * len(cells)) + " |" + "\n" + # overline += "| " + " | ".join(["---"] * len(cells)) + " |" + "\n" + # return overline + "|" + text + "\n" + underline + return None # TBD def html_to_jsondoc(html, **options): diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 4da1e64..e1ff942 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -1,11 +1,29 @@ -from datetime import datetime, timezone import logging +from datetime import datetime, timezone + +from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock +from jsondoc.models.block.types.code import Code, CodeBlock, Language +from jsondoc.models.block.types.column import ColumnBlock +from jsondoc.models.block.types.column_list import ColumnListBlock +from jsondoc.models.block.types.divider import DividerBlock +from jsondoc.models.block.types.equation import EquationBlock +from jsondoc.models.block.types.heading_1 import Heading1, Heading1Block +from jsondoc.models.block.types.heading_2 import Heading2, Heading2Block +from jsondoc.models.block.types.heading_3 import Heading3, Heading3Block +from jsondoc.models.block.types.image import ImageBlock +from jsondoc.models.block.types.image.external_image import ExternalImage +from jsondoc.models.block.types.image.file_image import FileImage +from jsondoc.models.block.types.numbered_list_item import NumberedListItemBlock from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock -from jsondoc.models.block.types.rich_text.equation import ( - RichTextEquation, - Equation as EquationObj, -) +from jsondoc.models.block.types.quote import Quote, QuoteBlock +from jsondoc.models.block.types.rich_text.equation import Equation as EquationObj +from jsondoc.models.block.types.rich_text.equation import RichTextEquation from jsondoc.models.block.types.rich_text.text import Link, RichTextText, Text +from jsondoc.models.block.types.table import TableBlock +from jsondoc.models.block.types.table_row import TableRowBlock +from jsondoc.models.block.types.to_do import ToDoBlock +from jsondoc.models.block.types.toggle import ToggleBlock +from jsondoc.models.file.external import External from jsondoc.models.shared_definitions import Annotations from jsondoc.utils import generate_id @@ -20,21 +38,23 @@ def create_rich_text( underline: bool | None = None, code: bool | None = None, color: str | None = None, -) -> RichTextText: + annotations: Annotations | None = None, +) -> RichTextText | RichTextEquation: if text is None and equation is None: raise ValueError("Either text or equation must be provided") if text is not None and equation is not None: raise ValueError("Only one of text or equation must be provided") - annotations = Annotations( - bold=bold, - italic=italic, - strikethrough=strikethrough, - underline=underline, - code=code, - color=color, - ) + if annotations is None: + annotations = Annotations( + bold=bold, + italic=italic, + strikethrough=strikethrough, + underline=underline, + code=code, + color=color, + ) if equation is not None: if url is not None: @@ -59,8 +79,8 @@ def create_rich_text( def create_paragraph_block( - text: str, - id=None, + text: str | None = None, + id: str | None = None, created_time=None, **kwargs, ) -> ParagraphBlock: @@ -69,16 +89,186 @@ def create_paragraph_block( if created_time is None: created_time = datetime.now(tz=timezone.utc) + rich_text = [] + if text is not None: + rich_text.append(create_rich_text(text, **kwargs)) + return ParagraphBlock( id=id, created_time=created_time, - paragraph=Paragraph( - rich_text=[ - create_rich_text(text, **kwargs), - ] + paragraph=Paragraph(rich_text=rich_text), + has_children=False, + ) + + +def create_code_block( + code: str | None = None, + language: str | None = None, + id: str | None = None, + created_time=None, + **kwargs, +) -> CodeBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + language_ = None + try: + language_ = Language(language) + except ValueError: + logging.warning(f"Unsupported language: {language}") + + rich_text = [] + if code is not None: + rich_text.append(create_rich_text(code, **kwargs)) + + return CodeBlock( + id=id, + created_time=created_time, + code=Code( + rich_text=rich_text, + language=language_, ), has_children=False, ) -print(create_paragraph_block(text="Hello, world!")) +def create_divider_block( + id: str | None = None, + created_time=None, +) -> DividerBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + return DividerBlock( + id=id, + created_time=created_time, + has_children=False, + ) + + +def create_h1_block( + text: str | None = None, + id: str | None = None, + created_time=None, + **kwargs, +) -> Heading1Block: + if id is None: + id = generate_id() + + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + rich_text = [] + if text is not None: + rich_text.append(create_rich_text(text, **kwargs)) + + return Heading1Block( + id=id, + created_time=created_time, + heading_1=Heading1(rich_text=rich_text), + has_children=False, + ) + + +def create_h2_block( + text: str | None = None, + id: str | None = None, + created_time=None, + **kwargs, +) -> Heading2Block: + if id is None: + id = generate_id() + + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + rich_text = [] + if text is not None: + rich_text.append(create_rich_text(text, **kwargs)) + + return Heading2Block( + id=id, + created_time=created_time, + heading_2=Heading2(rich_text=rich_text), + has_children=False, + ) + + +def create_h3_block( + text: str | None = None, + id: str | None = None, + created_time=None, + **kwargs, +) -> Heading3Block: + if id is None: + id = generate_id() + + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + rich_text = [] + if text is not None: + rich_text.append(create_rich_text(text, **kwargs)) + + return Heading3Block( + id=id, + created_time=created_time, + heading_3=Heading3(rich_text=rich_text), + has_children=False, + ) + + +def create_image_block( + url: str, + caption: str | None = None, + id: str | None = None, + created_time=None, +) -> ImageBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + caption_ = None + if caption is not None: + caption_ = [create_rich_text(caption)] + + ret = ImageBlock( + id=id, + created_time=created_time, + image=ExternalImage( + external=External(url=url), + caption=caption_, + ), + ) + return ret + + +def create_quote_block( + text: str | None = None, + id: str | None = None, + created_time=None, + **kwargs, +) -> QuoteBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + rich_text = [] + if text is not None: + rich_text.append(create_rich_text(text, **kwargs)) + + return QuoteBlock( + id=id, + created_time=created_time, + quote=Quote(rich_text=rich_text), + has_children=False, + ) + + +# print(create_paragraph_block(text="Hello, world!")) diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index f0f5cb9..c5a069d 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -4,9 +4,10 @@ def test_convert_html_all_elements(): path = "examples/html/html_all_elements.html" content = open(path, "r").read() + content = "

This is a bold word and this is an emphasized word.

" ret = html_to_jsondoc(content) - + print(ret) import ipdb; ipdb.set_trace() From 062c913295fc0f966837e26d40fb82b32d657d58 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Thu, 5 Sep 2024 22:28:54 +0200 Subject: [PATCH 13/45] Basic example works --- jsondoc/convert/html.py | 102 ++++++++++++++++++++++++++------------- jsondoc/convert/utils.py | 45 +++++++++++++++-- 2 files changed, 109 insertions(+), 38 deletions(-) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 78e1558..2c1654b 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -4,6 +4,7 @@ import six from jsondoc.convert.utils import ( + append_to_rich_text, create_code_block, create_divider_block, create_h1_block, @@ -13,6 +14,7 @@ create_paragraph_block, create_quote_block, create_rich_text, + try_append_rich_text_to_block, ) from jsondoc.models.block.base import BlockBase from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock @@ -82,7 +84,7 @@ def abstract_inline_conversion(markup_fn): """ def implementation(self, el, convert_as_inline): - text = el.get_text() + # text = el.get_text() # markup_prefix = markup_fn(self) # if markup_prefix.startswith("<") and markup_prefix.endswith(">"): # markup_suffix = " RichTextText | RichTextEquation: - if text is None and equation is None: - raise ValueError("Either text or equation must be provided") if text is not None and equation is not None: raise ValueError("Only one of text or equation must be provided") @@ -65,18 +66,28 @@ def create_rich_text( annotations=annotations, plain_text=equation, ) - elif text is not None: + else: ret = RichTextText( text=Text( - content=text, + content=text if text else "", link=Link(url=url) if url else None, ), annotations=annotations, - plain_text=text, + plain_text=text if text else "", ) return ret +def append_to_rich_text(rich_text: RichTextBase, text: str) -> RichTextBase: + if isinstance(rich_text, RichTextText): + rich_text.text.content += text + rich_text.plain_text += text + elif isinstance(rich_text, RichTextEquation): + rich_text.equation.expression += text + rich_text.plain_text += text + else: + raise ValueError(f"Unsupported rich text type: {type(rich_text)}") + return rich_text def create_paragraph_block( text: str | None = None, @@ -271,4 +282,28 @@ def create_quote_block( ) +def try_append_rich_text_to_block(block: BlockBase, rich_text: RichTextBase) -> bool: + if not isinstance(block, BlockBase) or not isinstance(rich_text, RichTextBase): + return False + + if isinstance(block, ParagraphBlock): + block.paragraph.rich_text.append(rich_text) + return True + elif isinstance(block, CodeBlock): + block.code.rich_text.append(rich_text) + return True + elif isinstance(block, Heading1Block): + block.heading_1.rich_text.append(rich_text) + return True + elif isinstance(block, Heading2Block): + block.heading_2.rich_text.append(rich_text) + return True + elif isinstance(block, Heading3Block): + block.heading_3.rich_text.append(rich_text) + return True + elif isinstance(block, QuoteBlock): + block.quote.rich_text.append(rich_text) + return True + # TODO: Add rest of the block types + # print(create_paragraph_block(text="Hello, world!")) From b2b10a51ce021c61f8c88f15fee9bd778ba3728b Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Thu, 5 Sep 2024 23:58:55 +0200 Subject: [PATCH 14/45] Add note --- docs/html-conversion.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/html-conversion.md b/docs/html-conversion.md index 6edca55..35b2791 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -1,2 +1,5 @@ - Terminal text nodes are to be converted to rich text blocks. +- Order of children blocks must be preserved +- Any node in the syntax tree can generate 3 types of objects: string, rich text, or block. +- Consequently, any node can receive a list of these objects as as children. Each of these must be handled properly: merge rich texts, append rich text to current block, if they can't be appended in the current node, pass them to the parent node while preserving the order of the children, and so on. \ No newline at end of file From 8a4c8ea1e30c5f22a3e6f2aaf6b2396181de37cd Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Fri, 6 Sep 2024 12:42:15 +0200 Subject: [PATCH 15/45] Add more doc --- docs/html-conversion.md | 56 ++++++++++++++++++++++++++++++++++++++++- jsondoc/convert/html.py | 9 ++++--- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/docs/html-conversion.md b/docs/html-conversion.md index 35b2791..9bcc10d 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -1,5 +1,59 @@ +# HTML Conversion - Terminal text nodes are to be converted to rich text blocks. - Order of children blocks must be preserved - Any node in the syntax tree can generate 3 types of objects: string, rich text, or block. -- Consequently, any node can receive a list of these objects as as children. Each of these must be handled properly: merge rich texts, append rich text to current block, if they can't be appended in the current node, pass them to the parent node while preserving the order of the children, and so on. \ No newline at end of file +- Consequently, any node can receive a list of these objects as as children. Each of these must be handled properly: merge rich texts, append rich text to current block, if they can't be appended in the current node, pass them to the parent node while preserving the order of the children, and so on. + +Below is an example paragraph element with child elements: + +```html +

This is a bold word and this is an emphasized word.

+``` + +This yields the following syntax tree: + +```mermaid +graph TD; + root["<p>"] + root --> node1["This is a"] + root --> node2["<b>"] + node2 --> node3["bold"] + root --> node4["word and this is an"] + root --> node5["<em>"] + node5 --> node6["emphasized"] + root --> node7["word."] + + classDef string fill:#28a745,color:white,font-weight:bold,stroke-width:2px; + classDef rich_text fill:#ffc107,color:#343a40,font-weight:bold,stroke-width:2px; + classDef block fill:#dc3545,color:white,font-weight:bold,stroke-width:2px; + + class root block; + class node2,node5 rich_text; + class node1,node3,node4,node6,node7 string; +``` + +In this example, only the paragraph element creates a JSON-DOC (paragraph) block. + +- Terminal string nodes (colored green) are returned as strings while recursing the tree. +- HTML tags that don't create blocks (colored yellow), but apply some style, such as `` and ``, are returned as empty rich text objects with corresponding `Annotations`. +- HTML tags that create blocks (colored red), such as `

`, `

`, ``, etc. are returned as empty JSON-DOC blocks. + +The function `process_tag(node)` receives the top level node and recurses its children which are themselves elements. + +```python +def process_tag(node): + children_objects = [] + for child in node.children: + if isinstance(child, NavigableString): + children_objects.append(child.text) + else: + children_objects.append(process_tag(child)) + + # Get the empty object corresponding to the current node (rich text or block) + current_node_object = convert_current_node(node) + + # Reconcile the children objects with the current node object + return_objects = reconcile_children(current_node_object, children_objects) + return return_objects +``` \ No newline at end of file diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 2c1654b..dc82b9a 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -1,7 +1,6 @@ from bs4 import BeautifulSoup, NavigableString, Comment, Doctype from textwrap import fill import re -import six from jsondoc.convert.utils import ( append_to_rich_text, @@ -236,7 +235,7 @@ def is_nested_node(el): ) if ( isinstance(el, NavigableString) - and six.text_type(el).strip() == "" + and str(el).strip() == "" and can_extract ): el.extract() @@ -293,7 +292,9 @@ def is_nested_node(el): remaining_children.append(current_rich_text) elif isinstance(child, RichTextBase): - success_ = try_append_rich_text_to_block(current_level_object, child) + success_ = try_append_rich_text_to_block( + current_level_object, child + ) current_rich_text = None if not success_: remaining_children.append(child) @@ -321,7 +322,7 @@ def is_nested_node(el): return objects def process_text(self, el): - text = six.text_type(el) or "" + text = str(el) or "" # normalize whitespace if we're not inside a preformatted element if not el.find_parent("pre"): From 126ba9c4c2760ef776a3d42cfb7b88852e5d8b07 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:01:05 +0200 Subject: [PATCH 16/45] Cleanup --- docs/html-conversion.md | 14 ++++++++------ jsondoc/convert/html.py | 21 +++++---------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/docs/html-conversion.md b/docs/html-conversion.md index 9bcc10d..6c8383b 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -33,13 +33,13 @@ graph TD; class node1,node3,node4,node6,node7 string; ``` -In this example, only the paragraph element creates a JSON-DOC (paragraph) block. +In this example, only the `

` element creates a JSON-DOC (paragraph) block. - Terminal string nodes (colored green) are returned as strings while recursing the tree. - HTML tags that don't create blocks (colored yellow), but apply some style, such as `` and ``, are returned as empty rich text objects with corresponding `Annotations`. - HTML tags that create blocks (colored red), such as `

`, `

`, ``, etc. are returned as empty JSON-DOC blocks. -The function `process_tag(node)` receives the top level node and recurses its children which are themselves elements. +The function `process_tag(node)` receives the top level node and recurses its children which are themselves either HTML elements or text nodes. ```python def process_tag(node): @@ -48,12 +48,14 @@ def process_tag(node): if isinstance(child, NavigableString): children_objects.append(child.text) else: - children_objects.append(process_tag(child)) + # Note that process_tag returns a list of objects and it is + # concatenated to the children_objects list. + children_objects.extend(process_tag(child)) - # Get the empty object corresponding to the current node (rich text or block) - current_node_object = convert_current_node(node) + # Get the empty object corresponding to the current node (rich text, block or None) + current_node_object: BlockBase | RichTextBase | None = convert_current_node(node) # Reconcile the children objects with the current node object - return_objects = reconcile_children(current_node_object, children_objects) + return_objects: list = reconcile_children(current_node_object, children_objects) return return_objects ``` \ No newline at end of file diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index dc82b9a..1dd5c3f 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -1,3 +1,4 @@ +from typing import List, Union from bs4 import BeautifulSoup, NavigableString, Comment, Doctype from textwrap import fill import re @@ -186,7 +187,9 @@ def convert_soup(self, soup): return ret - def process_tag(self, node, convert_as_inline, children_only=False): + def process_tag( + self, node, convert_as_inline, children_only=False + ) -> List[Union[BlockBase, RichTextBase, str]]: """ Convert a BeautifulSoup node to JSON-DOC. Recurses through the children nodes and converts them to JSON-DOC corresponding current block type @@ -252,15 +255,7 @@ def is_nested_node(el): else: # text += self.process_tag(el, convert_children_as_inline) new_objects = self.process_tag(el, convert_children_as_inline) - if new_objects is None: - continue - elif isinstance(new_objects, list): - children_objects += new_objects - else: - children_objects.append(new_objects) - - # elif isinstance(new_objects, BlockBase): - # children_objects.append(new_objects) + children_objects += new_objects current_level_object = None if not children_only: @@ -298,11 +293,7 @@ def is_nested_node(el): current_rich_text = None if not success_: remaining_children.append(child) - # if children_objects is not None: - # # import ipdb - # # ipdb.set_trace() - # pass objects = [current_level_object] + remaining_children elif isinstance(current_level_object, RichTextBase): # There is an assumption that text formatting tags will not contain @@ -312,9 +303,7 @@ def is_nested_node(el): append_to_rich_text(current_level_object, child) objects = [current_level_object] - # import ipdb - # ipdb.set_trace() import ipdb ipdb.set_trace() From 9557c763ca8708dff553d5c324526ba45f70ad36 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:47:33 +0200 Subject: [PATCH 17/45] Add JSON-DOC to Markdown converter, wip --- jsondoc/convert/markdown.py | 179 ++++++++++++++++++++++++++++++++++ jsondoc/convert/utils.py | 55 +++++++---- jsondoc/serialize.py | 13 +++ tests/test_html_to_jsondoc.py | 4 + 4 files changed, 234 insertions(+), 17 deletions(-) create mode 100644 jsondoc/convert/markdown.py diff --git a/jsondoc/convert/markdown.py b/jsondoc/convert/markdown.py new file mode 100644 index 0000000..9b2c312 --- /dev/null +++ b/jsondoc/convert/markdown.py @@ -0,0 +1,179 @@ +import re +from typing import List, Union +from pydantic import validate_call +from jsondoc.convert.utils import get_rich_text_from_block +from jsondoc.models.block.base import BlockBase +from jsondoc.models.block.types.code import CodeBlock +from jsondoc.models.block.types.rich_text.base import RichTextBase +from jsondoc.models.block.types.rich_text.equation import RichTextEquation +from jsondoc.models.block.types.rich_text.text import RichTextText +from jsondoc.models.page import Page +from jsondoc.models.shared_definitions import Annotations +from jsondoc.serialize import load_jsondoc, load_page + + +convert_heading_re = re.compile(r"convert_h(\d+)") +line_beginning_re = re.compile(r"^", re.MULTILINE) +whitespace_re = re.compile(r"[\t ]+") +all_whitespace_re = re.compile(r"[\s]+") +html_heading_re = re.compile(r"h[1-6]") + + +# Heading styles +ATX = "atx" +ATX_CLOSED = "atx_closed" +UNDERLINED = "underlined" +SETEXT = UNDERLINED + +# Newline style +SPACES = "spaces" +BACKSLASH = "backslash" + +# Strong and emphasis style +ASTERISK = "*" +UNDERSCORE = "_" + + +def _todict(obj): + return dict((k, getattr(obj, k)) for k in dir(obj) if not k.startswith("_")) + + +class JsonDocToMarkdownConverter(object): + + class DefaultOptions: + autolinks = True + bullets = "*+-" # An iterable of bullet types. + code_language = "" + code_language_callback = None + convert = None + default_title = False + escape_asterisks = True + escape_underscores = True + escape_misc = True + heading_style = UNDERLINED + keep_inline_images_in = [] + newline_style = SPACES + strip = None + strong_em_symbol = ASTERISK + wrap = False + wrap_width = 80 + + class Options(DefaultOptions): + pass + + def __init__(self, **options): + # Create an options dictionary. Use DefaultOptions as a base so that + # it doesn't have to be extended. + self.options = _todict(self.DefaultOptions) + self.options.update(_todict(self.Options)) + self.options.update(options) + if self.options["strip"] is not None and self.options["convert"] is not None: + raise ValueError( + "You may specify either tags to strip or tags to" + " convert, but not both." + ) + + @validate_call + def convert(self, obj: str | dict | BlockBase | Page) -> str: + + if isinstance(obj, (str, dict)): + jsondoc = load_jsondoc(obj) + else: + jsondoc = obj + + if isinstance(jsondoc, Page): + return self.convert_page(jsondoc) + elif isinstance(jsondoc, BlockBase): + return self.convert_block(jsondoc) + else: + raise ValueError(f"Invalid object type: {type(jsondoc)}") + + @validate_call + def convert_page(self, page: Page) -> str: + ret = "" + for block in page.children: + block_str = self.convert_block(block) + ret += block_str + + return ret + + @validate_call + def convert_block(self, block: BlockBase) -> str: + rich_text = get_rich_text_from_block(block) + if rich_text is None: + return "" + + block_content = self.convert_rich_text_list_to_markdown(rich_text) + + # Code blocks are kept verbatim + if not isinstance(block, CodeBlock): + block_content = self.escape(block_content) + + return block_content + + def _get_prefix_and_suffix_from_annotations( + self, annotations: Annotations + ) -> tuple[str, str]: + prefix = "" + suffix = "" + if annotations is not None: + if annotations.bold: + prefix += "**" + suffix = "**" + suffix + if annotations.italic: + prefix += "*" + suffix = "*" + suffix + if annotations.strikethrough: + prefix += "~~" + suffix = "~~" + suffix + if annotations.code: + prefix += "`" + suffix = "`" + suffix + if annotations.underline: + prefix += "" + suffix = "" + suffix + + return prefix, suffix + + def escape(self, text): + if not text: + return "" + if self.options["escape_misc"]: + text = re.sub(r"([\\&<`[>~#=+|-])", r"\\\1", text) + text = re.sub(r"([0-9])([.)])", r"\1\\\2", text) + if self.options["escape_asterisks"]: + text = text.replace("*", r"\*") + if self.options["escape_underscores"]: + text = text.replace("_", r"\_") + return text + + def indent(self, text, level): + return line_beginning_re.sub("\t" * level, text) if text else "" + + def underline(self, text, pad_char): + text = (text or "").rstrip() + return "%s\n%s\n\n" % (text, pad_char * len(text)) if text else "" + + def convert_rich_text_list_to_markdown( + self, rich_text_list: List[Union[RichTextText, RichTextEquation]] + ) -> str: + ret = "" + for rich_text in rich_text_list: + if isinstance(rich_text, RichTextText): + text_ = rich_text.text.content + prefix, suffix = self._get_prefix_and_suffix_from_annotations( + rich_text.annotations + ) + ret += prefix + text_ + suffix + elif isinstance(rich_text, RichTextEquation): + text_ = rich_text.equation.expression + prefix, suffix = ["$$", "$$"] + ret += prefix + text_ + suffix + else: + raise ValueError(f"Unsupported rich text type: {type(rich_text)}") + + return ret + + +def jsondoc_to_markdown(jsondoc, **options): + return JsonDocToMarkdownConverter(**options).convert(jsondoc) diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index e5a5917..6c5c780 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -1,6 +1,6 @@ import logging from datetime import datetime, timezone -from typing import Type +from typing import List, Type from jsondoc.models.block.base import BlockBase from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock @@ -78,6 +78,7 @@ def create_rich_text( return ret + def append_to_rich_text(rich_text: RichTextBase, text: str) -> RichTextBase: if isinstance(rich_text, RichTextText): rich_text.text.content += text @@ -89,6 +90,7 @@ def append_to_rich_text(rich_text: RichTextBase, text: str) -> RichTextBase: raise ValueError(f"Unsupported rich text type: {type(rich_text)}") return rich_text + def create_paragraph_block( text: str | None = None, id: str | None = None, @@ -282,28 +284,47 @@ def create_quote_block( ) -def try_append_rich_text_to_block(block: BlockBase, rich_text: RichTextBase) -> bool: - if not isinstance(block, BlockBase) or not isinstance(rich_text, RichTextBase): - return False - +def get_rich_text_from_block(block: BlockBase) -> List[RichTextBase] | None: + """ + Returns the rich text of a block + """ + ret = None if isinstance(block, ParagraphBlock): - block.paragraph.rich_text.append(rich_text) - return True + ret = block.paragraph.rich_text elif isinstance(block, CodeBlock): - block.code.rich_text.append(rich_text) - return True + ret = block.code.rich_text elif isinstance(block, Heading1Block): - block.heading_1.rich_text.append(rich_text) - return True + ret = block.heading_1.rich_text elif isinstance(block, Heading2Block): - block.heading_2.rich_text.append(rich_text) - return True + ret = block.heading_2.rich_text elif isinstance(block, Heading3Block): - block.heading_3.rich_text.append(rich_text) - return True + ret = block.heading_3.rich_text elif isinstance(block, QuoteBlock): - block.quote.rich_text.append(rich_text) + ret = block.quote.rich_text + elif isinstance(block, BulletedListItemBlock): + ret = block.bulleted_list_item.rich_text + elif isinstance(block, NumberedListItemBlock): + ret = block.numbered_list_item.rich_text + elif isinstance(block, ToDoBlock): + ret = block.to_do.rich_text + elif isinstance(block, ToggleBlock): + ret = block.toggle.rich_text + # else: + # raise ValueError(f"Unsupported block type: {type(block)}") + + return ret + + +def try_append_rich_text_to_block(block: BlockBase, rich_text: RichTextBase) -> bool: + if not isinstance(block, BlockBase) or not isinstance(rich_text, RichTextBase): + return False + + rich_text_list = get_rich_text_from_block(block) + if rich_text_list is not None: + rich_text_list.append(rich_text) return True - # TODO: Add rest of the block types + + return False + # print(create_paragraph_block(text="Hello, world!")) diff --git a/jsondoc/serialize.py b/jsondoc/serialize.py index 5ef423d..bfe5518 100644 --- a/jsondoc/serialize.py +++ b/jsondoc/serialize.py @@ -238,3 +238,16 @@ def load_page(obj: Union[str, Dict[str, Any]]) -> Page: page = Page(**mutable_obj) return page + +@validate_call +def load_jsondoc(obj: Union[str, Dict[str, Any]]) -> Page | BlockBase: + if isinstance(obj, str): + obj = json.loads(obj) + + object_ = obj.get("object") + if object_ == "page": + return load_page(obj) + elif object_ == "block": + return load_block(obj) + else: + raise ValueError("Invalid object: must be either 'page' or 'block'") \ No newline at end of file diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index c5a069d..ab46eea 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -1,4 +1,5 @@ from jsondoc.convert.html import html_to_jsondoc +from jsondoc.convert.markdown import jsondoc_to_markdown def test_convert_html_all_elements(): path = "examples/html/html_all_elements.html" @@ -8,6 +9,9 @@ def test_convert_html_all_elements(): ret = html_to_jsondoc(content) print(ret) + + print("\n\nConverted to markdown:\n\n") + print(jsondoc_to_markdown(ret[0])) import ipdb; ipdb.set_trace() From e8ccdea620694aabc2593c412c7afaecba0bbe3c Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Fri, 6 Sep 2024 16:22:59 +0200 Subject: [PATCH 18/45] Convert more blocks into markdown, wip --- jsondoc/convert/markdown.py | 159 +++++++++++++++++++++++++++--- tests/test_jsondoc_to_markdown.py | 19 ++++ 2 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 tests/test_jsondoc_to_markdown.py diff --git a/jsondoc/convert/markdown.py b/jsondoc/convert/markdown.py index 9b2c312..15abbaf 100644 --- a/jsondoc/convert/markdown.py +++ b/jsondoc/convert/markdown.py @@ -4,6 +4,11 @@ from jsondoc.convert.utils import get_rich_text_from_block from jsondoc.models.block.base import BlockBase from jsondoc.models.block.types.code import CodeBlock +from jsondoc.models.block.types.divider import DividerBlock +from jsondoc.models.block.types.heading_1 import Heading1Block +from jsondoc.models.block.types.heading_2 import Heading2Block +from jsondoc.models.block.types.heading_3 import Heading3Block +from jsondoc.models.block.types.paragraph import ParagraphBlock from jsondoc.models.block.types.rich_text.base import RichTextBase from jsondoc.models.block.types.rich_text.equation import RichTextEquation from jsondoc.models.block.types.rich_text.text import RichTextText @@ -12,7 +17,7 @@ from jsondoc.serialize import load_jsondoc, load_page -convert_heading_re = re.compile(r"convert_h(\d+)") +convert_heading_re = re.compile(r"convert_heading_(\d+)") line_beginning_re = re.compile(r"^", re.MULTILINE) whitespace_re = re.compile(r"[\t ]+") all_whitespace_re = re.compile(r"[\s]+") @@ -38,6 +43,19 @@ def _todict(obj): return dict((k, getattr(obj, k)) for k in dir(obj) if not k.startswith("_")) +def chomp(text): + """ + If the text in an inline tag like b, a, or em contains a leading or trailing + space, strip the string and return a space as suffix of prefix, if needed. + This function is used to prevent conversions like + foo => ** foo** + """ + prefix = " " if text and text[0] == " " else "" + suffix = " " if text and text[-1] == " " else "" + text = text.strip() + return (prefix, suffix, text) + + class JsonDocToMarkdownConverter(object): class DefaultOptions: @@ -73,6 +91,21 @@ def __init__(self, **options): " convert, but not both." ) + def __getattr__(self, attr): + # Handle headings + m = convert_heading_re.match(attr) + if m: + n = int(m.group(1)) + + def convert_tag(block, convert_as_inline): + return self.convert_heading_n_block(n, block, convert_as_inline) + + convert_tag.__name__ = "convert_heading_%s_block" % n + setattr(self, convert_tag.__name__, convert_tag) + return convert_tag + + raise AttributeError(attr) + @validate_call def convert(self, obj: str | dict | BlockBase | Page) -> str: @@ -92,22 +125,38 @@ def convert(self, obj: str | dict | BlockBase | Page) -> str: def convert_page(self, page: Page) -> str: ret = "" for block in page.children: - block_str = self.convert_block(block) + block_str = self.convert_block(block, False) ret += block_str return ret @validate_call - def convert_block(self, block: BlockBase) -> str: - rich_text = get_rich_text_from_block(block) - if rich_text is None: - return "" + def convert_block(self, block: BlockBase, convert_as_inline: bool) -> str: - block_content = self.convert_rich_text_list_to_markdown(rich_text) + type_ = block.type + convert_fn = getattr(self, f"convert_{type_}_block", None) - # Code blocks are kept verbatim - if not isinstance(block, CodeBlock): - block_content = self.escape(block_content) + if convert_fn is None: + convert_fn = self.convert_paragraph_block + # TODO: Change this back + # raise ValueError(f"Unsupported block type: {type_}") + + block_content = convert_fn(block, convert_as_inline) + + if hasattr(block, "children") and block.children: + for child in block.children: + block_content += self.convert_block(child, convert_as_inline) + + # rich_text = get_rich_text_from_block(block) + # if rich_text is None: + # return "" + + # # Code blocks are kept verbatim + # escape = isinstance(block, CodeBlock) + + # block_content = self.convert_rich_text_list_to_markdown( + # rich_text, escape=escape + # ) return block_content @@ -155,18 +204,31 @@ def underline(self, text, pad_char): return "%s\n%s\n\n" % (text, pad_char * len(text)) if text else "" def convert_rich_text_list_to_markdown( - self, rich_text_list: List[Union[RichTextText, RichTextEquation]] + self, + rich_text_list: List[Union[RichTextText, RichTextEquation]], + escape: bool = True, ) -> str: ret = "" for rich_text in rich_text_list: if isinstance(rich_text, RichTextText): text_ = rich_text.text.content + if escape: + text_ = self.escape(text_) + + prefix_, suffix_, text_ = chomp(text_) + + url = rich_text.text.link.url if rich_text.text.link else None + if url: + text_ = f"[{text_}]({url})" + prefix, suffix = self._get_prefix_and_suffix_from_annotations( rich_text.annotations ) - ret += prefix + text_ + suffix + ret += prefix_ + prefix + text_ + suffix + suffix_ elif isinstance(rich_text, RichTextEquation): text_ = rich_text.equation.expression + if escape: + text_ = self.escape(text_) prefix, suffix = ["$$", "$$"] ret += prefix + text_ + suffix else: @@ -174,6 +236,79 @@ def convert_rich_text_list_to_markdown( return ret + def convert_paragraph_block( + self, block: ParagraphBlock, convert_as_inline: bool + ) -> str: + rich_text = get_rich_text_from_block(block) + if rich_text is None: + return "" + + # Code blocks are kept verbatim + # escape = isinstance(block, CodeBlock) + + text = self.convert_rich_text_list_to_markdown(rich_text, escape=True) + + if convert_as_inline: + return text + + # return "\n\n" + block_content + return "%s\n\n" % text if text else "" + + def convert_divider_block( + self, block: DividerBlock, convert_as_inline: bool + ) -> str: + if convert_as_inline: + return "" + + return "\n\n---\n\n" + + def convert_code_block(self, block: CodeBlock, convert_as_inline: bool) -> str: + rich_text = get_rich_text_from_block(block) + if rich_text is None: + return "" + + language = block.code.language.value + + # if self.options["code_language_callback"]: + # language = self.options["code_language_callback"](language) + + # if language: + # language = f" {language}" + + code = self.convert_rich_text_list_to_markdown(rich_text, escape=True) + + if convert_as_inline: + return f"{code}" + + return f"```{language}\n{code}\n```\n\n" + + def convert_heading_n_block( + self, + n: int, + block: Heading1Block | Heading2Block | Heading3Block, + convert_as_inline: bool, + ): + + rich_text = get_rich_text_from_block(block) + if rich_text is None: + return "" + + text = self.convert_rich_text_list_to_markdown(rich_text, escape=True) + if convert_as_inline: + return text + + style = self.options["heading_style"].lower() + + if style == UNDERLINED and n <= 2: + line = "=" if n == 1 else "-" + return self.underline(text, line) + + hashes = "#" * n + if style == ATX_CLOSED: + return "%s %s %s\n\n" % (hashes, text, hashes) + + return "%s %s\n\n" % (hashes, text) + def jsondoc_to_markdown(jsondoc, **options): return JsonDocToMarkdownConverter(**options).convert(jsondoc) diff --git a/tests/test_jsondoc_to_markdown.py b/tests/test_jsondoc_to_markdown.py new file mode 100644 index 0000000..dc91d9f --- /dev/null +++ b/tests/test_jsondoc_to_markdown.py @@ -0,0 +1,19 @@ +from jsondoc.convert.html import html_to_jsondoc +from jsondoc.convert.markdown import jsondoc_to_markdown +from jsondoc.serialize import load_jsondoc, load_page +from jsondoc.utils import load_json_file + + +def test_convert_jsondoc_to_markdown(): + path = "schema/page/ex1_success.json" + content = load_json_file(path) + page = load_jsondoc(content) + + print(jsondoc_to_markdown(page)) + import ipdb + + ipdb.set_trace() + + +if __name__ == "__main__": + test_convert_jsondoc_to_markdown() From 73b3128f67a15348757ffcf1a3313117112140c4 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:49:42 +0200 Subject: [PATCH 19/45] wip --- jsondoc/convert/html.py | 15 +++++++++++++-- jsondoc/models/block/__init__.py | 2 +- jsondoc/models/block/base/__init__.py | 2 +- .../block/types/bulleted_list_item/__init__.py | 2 +- jsondoc/models/block/types/code/__init__.py | 2 +- jsondoc/models/block/types/column/__init__.py | 2 +- .../models/block/types/column_list/__init__.py | 2 +- jsondoc/models/block/types/divider/__init__.py | 2 +- jsondoc/models/block/types/equation/__init__.py | 2 +- jsondoc/models/block/types/heading_1/__init__.py | 2 +- jsondoc/models/block/types/heading_2/__init__.py | 2 +- jsondoc/models/block/types/heading_3/__init__.py | 2 +- jsondoc/models/block/types/image/__init__.py | 2 +- .../block/types/image/external_image/__init__.py | 2 +- .../block/types/image/file_image/__init__.py | 2 +- .../block/types/numbered_list_item/__init__.py | 2 +- jsondoc/models/block/types/paragraph/__init__.py | 2 +- jsondoc/models/block/types/quote/__init__.py | 2 +- jsondoc/models/block/types/rich_text/__init__.py | 2 +- .../models/block/types/rich_text/base/__init__.py | 2 +- .../block/types/rich_text/equation/__init__.py | 2 +- .../models/block/types/rich_text/text/__init__.py | 2 +- jsondoc/models/block/types/table/__init__.py | 2 +- jsondoc/models/block/types/table_row/__init__.py | 2 +- jsondoc/models/block/types/to_do/__init__.py | 2 +- jsondoc/models/block/types/toggle/__init__.py | 2 +- jsondoc/models/file/__init__.py | 2 +- jsondoc/models/file/base/__init__.py | 2 +- jsondoc/models/file/external/__init__.py | 2 +- jsondoc/models/file/file/__init__.py | 2 +- jsondoc/models/page/__init__.py | 2 +- jsondoc/models/shared_definitions/__init__.py | 2 +- .../block/types/rich_text/base/base_schema.json | 15 ++++++++++++++- .../block/types/rich_text/rich_text_schema.json | 3 +++ tests/test_html_to_jsondoc.py | 2 +- 35 files changed, 62 insertions(+), 35 deletions(-) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 1dd5c3f..70c1ba2 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -31,6 +31,7 @@ from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock from jsondoc.models.block.types.quote import QuoteBlock from jsondoc.models.block.types.rich_text.base import RichTextBase +from jsondoc.models.block.types.rich_text.equation import RichTextEquation from jsondoc.models.block.types.rich_text.text import RichTextText from jsondoc.models.block.types.table import TableBlock from jsondoc.models.block.types.table_row import TableRowBlock @@ -60,6 +61,9 @@ ASTERISK = "*" UNDERSCORE = "_" +CHILDREN_TYPE = Union[BlockBase, RichTextBase, str] +RICH_TEXT_TYPE = Union[RichTextBase, RichTextEquation] + def chomp(text): """ @@ -137,6 +141,13 @@ def has_direct_text(node): return any(text.strip() for text in direct_text) +def reconcile_to_rich_text( + parent_rich_text: RICH_TEXT_TYPE, children: List[CHILDREN_TYPE] +): + annotations = parent_rich_text.annotations + # TBD + + class HtmlToJsonDocConverter(object): class DefaultOptions: autolinks = True @@ -189,7 +200,7 @@ def convert_soup(self, soup): def process_tag( self, node, convert_as_inline, children_only=False - ) -> List[Union[BlockBase, RichTextBase, str]]: + ) -> List[CHILDREN_TYPE]: """ Convert a BeautifulSoup node to JSON-DOC. Recurses through the children nodes and converts them to JSON-DOC corresponding current block type @@ -264,7 +275,7 @@ def is_nested_node(el): # text = convert_fn(node, text, convert_as_inline) current_level_object = convert_fn(node, convert_as_inline) - print(node, current_level_object) + print(node, repr(current_level_object)) # if children_objects: if current_level_object is None: diff --git a/jsondoc/models/block/__init__.py b/jsondoc/models/block/__init__.py index d08211a..7b8ea3d 100644 --- a/jsondoc/models/block/__init__.py +++ b/jsondoc/models/block/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/base/__init__.py b/jsondoc/models/block/base/__init__.py index e3a39f9..795c260 100644 --- a/jsondoc/models/block/base/__init__.py +++ b/jsondoc/models/block/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/bulleted_list_item/__init__.py b/jsondoc/models/block/types/bulleted_list_item/__init__.py index 6ff3819..3b02d02 100644 --- a/jsondoc/models/block/types/bulleted_list_item/__init__.py +++ b/jsondoc/models/block/types/bulleted_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/code/__init__.py b/jsondoc/models/block/types/code/__init__.py index 42de72e..b3edba1 100644 --- a/jsondoc/models/block/types/code/__init__.py +++ b/jsondoc/models/block/types/code/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column/__init__.py b/jsondoc/models/block/types/column/__init__.py index 56759d1..65da95d 100644 --- a/jsondoc/models/block/types/column/__init__.py +++ b/jsondoc/models/block/types/column/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column_list/__init__.py b/jsondoc/models/block/types/column_list/__init__.py index f946a7a..dc98108 100644 --- a/jsondoc/models/block/types/column_list/__init__.py +++ b/jsondoc/models/block/types/column_list/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/divider/__init__.py b/jsondoc/models/block/types/divider/__init__.py index a5eb80b..000f4f1 100644 --- a/jsondoc/models/block/types/divider/__init__.py +++ b/jsondoc/models/block/types/divider/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/equation/__init__.py b/jsondoc/models/block/types/equation/__init__.py index e1e686c..2cdd44f 100644 --- a/jsondoc/models/block/types/equation/__init__.py +++ b/jsondoc/models/block/types/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_1/__init__.py b/jsondoc/models/block/types/heading_1/__init__.py index cb510cf..5a476cb 100644 --- a/jsondoc/models/block/types/heading_1/__init__.py +++ b/jsondoc/models/block/types/heading_1/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_2/__init__.py b/jsondoc/models/block/types/heading_2/__init__.py index 273e750..94cc452 100644 --- a/jsondoc/models/block/types/heading_2/__init__.py +++ b/jsondoc/models/block/types/heading_2/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_3/__init__.py b/jsondoc/models/block/types/heading_3/__init__.py index 9b507d7..0c364ee 100644 --- a/jsondoc/models/block/types/heading_3/__init__.py +++ b/jsondoc/models/block/types/heading_3/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/__init__.py b/jsondoc/models/block/types/image/__init__.py index a244a4c..baf8c4e 100644 --- a/jsondoc/models/block/types/image/__init__.py +++ b/jsondoc/models/block/types/image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/external_image/__init__.py b/jsondoc/models/block/types/image/external_image/__init__.py index d4e3241..193d2b7 100644 --- a/jsondoc/models/block/types/image/external_image/__init__.py +++ b/jsondoc/models/block/types/image/external_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/file_image/__init__.py b/jsondoc/models/block/types/image/file_image/__init__.py index 53779d3..1102224 100644 --- a/jsondoc/models/block/types/image/file_image/__init__.py +++ b/jsondoc/models/block/types/image/file_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/numbered_list_item/__init__.py b/jsondoc/models/block/types/numbered_list_item/__init__.py index f5f30ea..7cc6632 100644 --- a/jsondoc/models/block/types/numbered_list_item/__init__.py +++ b/jsondoc/models/block/types/numbered_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/paragraph/__init__.py b/jsondoc/models/block/types/paragraph/__init__.py index 6e4a0ba..21118bf 100644 --- a/jsondoc/models/block/types/paragraph/__init__.py +++ b/jsondoc/models/block/types/paragraph/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/quote/__init__.py b/jsondoc/models/block/types/quote/__init__.py index 6f50bcc..f00127c 100644 --- a/jsondoc/models/block/types/quote/__init__.py +++ b/jsondoc/models/block/types/quote/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/__init__.py b/jsondoc/models/block/types/rich_text/__init__.py index 2f57ea3..0be64b9 100644 --- a/jsondoc/models/block/types/rich_text/__init__.py +++ b/jsondoc/models/block/types/rich_text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/base/__init__.py b/jsondoc/models/block/types/rich_text/base/__init__.py index 984c43d..69bbb56 100644 --- a/jsondoc/models/block/types/rich_text/base/__init__.py +++ b/jsondoc/models/block/types/rich_text/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/equation/__init__.py b/jsondoc/models/block/types/rich_text/equation/__init__.py index 4b6a0ab..659f621 100644 --- a/jsondoc/models/block/types/rich_text/equation/__init__.py +++ b/jsondoc/models/block/types/rich_text/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/text/__init__.py b/jsondoc/models/block/types/rich_text/text/__init__.py index 0bf8350..4cf0b5e 100644 --- a/jsondoc/models/block/types/rich_text/text/__init__.py +++ b/jsondoc/models/block/types/rich_text/text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table/__init__.py b/jsondoc/models/block/types/table/__init__.py index 1cebed5..f362464 100644 --- a/jsondoc/models/block/types/table/__init__.py +++ b/jsondoc/models/block/types/table/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table_row/__init__.py b/jsondoc/models/block/types/table_row/__init__.py index 62b5d83..9edb4fc 100644 --- a/jsondoc/models/block/types/table_row/__init__.py +++ b/jsondoc/models/block/types/table_row/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/to_do/__init__.py b/jsondoc/models/block/types/to_do/__init__.py index e8925b8..3802d8e 100644 --- a/jsondoc/models/block/types/to_do/__init__.py +++ b/jsondoc/models/block/types/to_do/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/toggle/__init__.py b/jsondoc/models/block/types/toggle/__init__.py index cf71f44..7e8a453 100644 --- a/jsondoc/models/block/types/toggle/__init__.py +++ b/jsondoc/models/block/types/toggle/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/__init__.py b/jsondoc/models/file/__init__.py index 5312252..32f440e 100644 --- a/jsondoc/models/file/__init__.py +++ b/jsondoc/models/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/base/__init__.py b/jsondoc/models/file/base/__init__.py index 6ec1a9a..5487fc9 100644 --- a/jsondoc/models/file/base/__init__.py +++ b/jsondoc/models/file/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/external/__init__.py b/jsondoc/models/file/external/__init__.py index 3de34ca..47b4d68 100644 --- a/jsondoc/models/file/external/__init__.py +++ b/jsondoc/models/file/external/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/file/__init__.py b/jsondoc/models/file/file/__init__.py index deec346..5f16e03 100644 --- a/jsondoc/models/file/file/__init__.py +++ b/jsondoc/models/file/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/page/__init__.py b/jsondoc/models/page/__init__.py index 3442f11..fb529b2 100644 --- a/jsondoc/models/page/__init__.py +++ b/jsondoc/models/page/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/jsondoc/models/shared_definitions/__init__.py b/jsondoc/models/shared_definitions/__init__.py index 1d9d7a9..9f94220 100644 --- a/jsondoc/models/shared_definitions/__init__.py +++ b/jsondoc/models/shared_definitions/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-05T14:27:19+00:00 +# timestamp: 2024-09-06T15:48:03+00:00 from __future__ import annotations diff --git a/schema/block/types/rich_text/base/base_schema.json b/schema/block/types/rich_text/base/base_schema.json index 77bb587..b91f023 100644 --- a/schema/block/types/rich_text/base/base_schema.json +++ b/schema/block/types/rich_text/base/base_schema.json @@ -4,5 +4,18 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "RichTextBase", "type": "object", - "properties": {} + "properties": { + // NOTE: Ideally, these should be owned by RichTextBase, but there were some issues resolving + // references. Try again later + // + // "annotations": { + // "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/annotations" + // }, + // "plain_text": { + // "type": "string" + // }, + // "href": { + // "type": ["string", "null"] + // } + } } diff --git a/schema/block/types/rich_text/rich_text_schema.json b/schema/block/types/rich_text/rich_text_schema.json index a212a22..c169b26 100644 --- a/schema/block/types/rich_text/rich_text_schema.json +++ b/schema/block/types/rich_text/rich_text_schema.json @@ -11,6 +11,9 @@ }, "required": ["type"], "allOf": [ + // { + // "$ref": "/block/types/rich_text/base/base_schema.json" + // }, { "if": { "properties": { "type": { "const": "text" } } diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index ab46eea..51dbc03 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -5,7 +5,7 @@ def test_convert_html_all_elements(): path = "examples/html/html_all_elements.html" content = open(path, "r").read() - content = "

This is a bold word and this is an emphasized word.

" + # content = "

This is a bold word and this is an emphasized word.

" ret = html_to_jsondoc(content) print(ret) From afe4c3432571b8ad9e3ac03c71fa490779fb2cb8 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Sat, 7 Sep 2024 17:09:12 +0200 Subject: [PATCH 20/45] Implement reconcile_to_rich_text() --- jsondoc/convert/html.py | 66 +++++++++++++++++++++++++++++++++---- jsondoc/convert/markdown.py | 2 +- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 70c1ba2..121f025 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -14,6 +14,7 @@ create_paragraph_block, create_quote_block, create_rich_text, + get_rich_text_from_block, try_append_rich_text_to_block, ) from jsondoc.models.block.base import BlockBase @@ -141,11 +142,63 @@ def has_direct_text(node): return any(text.strip() for text in direct_text) +def apply_parent_annotations( + parent_annotations: Annotations, + child_annotations: Annotations, +): + if parent_annotations.bold is True: + child_annotations.bold = True + + if parent_annotations.italic is True: + child_annotations.italic = True + + if parent_annotations.strikethrough is True: + child_annotations.strikethrough = True + + if parent_annotations.underline is True: + child_annotations.underline = True + + if parent_annotations.code is True: + child_annotations.code = True + + # TBD: Decide how to handle color + + +def apply_annotations_to_block(annotations_to_apply: Annotations, block: BlockBase): + if hasattr(block, "children"): + for child in block.children: + apply_annotations_to_block(annotations_to_apply, child) + + # Get rich text + rich_text_list = get_rich_text_from_block(block) + if isinstance(rich_text_list, list): + for rich_text in rich_text_list: + apply_parent_annotations(annotations_to_apply, rich_text.annotations) + + def reconcile_to_rich_text( parent_rich_text: RICH_TEXT_TYPE, children: List[CHILDREN_TYPE] -): +) -> List[CHILDREN_TYPE]: + """ """ annotations = parent_rich_text.annotations - # TBD + + # Get non-null/false values from annotations + final_objects = [] + final_objects.append(parent_rich_text) + + for child in children: + if isinstance(child, RichTextBase): + apply_parent_annotations(annotations, child.annotations) + final_objects.append(child) + elif isinstance(child, BlockBase): + apply_annotations_to_block(annotations, child) + final_objects.append(child) + elif isinstance(child, str): + append_to_rich_text(parent_rich_text, child) + else: + raise ValueError(f"Unsupported type: {type(child)}") + + return final_objects class HtmlToJsonDocConverter(object): @@ -309,11 +362,12 @@ def is_nested_node(el): elif isinstance(current_level_object, RichTextBase): # There is an assumption that text formatting tags will not contain # higher level tags like blockquotes, lists, etc. - for child in children_objects: - if isinstance(child, str): - append_to_rich_text(current_level_object, child) + # for child in children_objects: + # if isinstance(child, str): + # append_to_rich_text(current_level_object, child) - objects = [current_level_object] + # objects = [current_level_object] + objects = reconcile_to_rich_text(current_level_object, children_objects) import ipdb diff --git a/jsondoc/convert/markdown.py b/jsondoc/convert/markdown.py index 15abbaf..ad35b73 100644 --- a/jsondoc/convert/markdown.py +++ b/jsondoc/convert/markdown.py @@ -117,7 +117,7 @@ def convert(self, obj: str | dict | BlockBase | Page) -> str: if isinstance(jsondoc, Page): return self.convert_page(jsondoc) elif isinstance(jsondoc, BlockBase): - return self.convert_block(jsondoc) + return self.convert_block(jsondoc, False) else: raise ValueError(f"Invalid object type: {type(jsondoc)}") From ac755059e6f3e9f875eca97492ee075736b76f25 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Sun, 8 Sep 2024 10:47:24 +0200 Subject: [PATCH 21/45] Add converter script --- jsondoc/bin/convert_jsondoc.py | 71 +++++++++++++++++++++++++++++++ jsondoc/bin/validate_jsondoc.py | 18 ++++++++ jsondoc/convert/html.py | 23 +++++++--- jsondoc/serialize.py | 28 ++++++++++-- jsondoc/utils.py | 2 +- jsondoc/validate/__init__.py | 16 ------- pyproject.toml | 3 +- tests/html_jsondoc_pairs/ex1.json | 70 ++++++++++++++++++++++++++++++ tests/test_html_to_jsondoc.py | 4 +- 9 files changed, 205 insertions(+), 30 deletions(-) create mode 100644 jsondoc/bin/convert_jsondoc.py create mode 100644 jsondoc/bin/validate_jsondoc.py create mode 100644 tests/html_jsondoc_pairs/ex1.json diff --git a/jsondoc/bin/convert_jsondoc.py b/jsondoc/bin/convert_jsondoc.py new file mode 100644 index 0000000..558720f --- /dev/null +++ b/jsondoc/bin/convert_jsondoc.py @@ -0,0 +1,71 @@ +import argparse +import json +from jsondoc.convert.html import html_to_jsondoc +from jsondoc.convert.markdown import jsondoc_to_markdown +from jsondoc.serialize import jsondoc_dump_json, load_jsondoc + + +def convert_to_jsondoc(input_file, output_file=None, indent=None): + # Read the input file + with open(input_file, "r") as file: + content = file.read() + + # Determine the file type based on extension + file_extension = input_file.split(".")[-1].lower() + + if file_extension in ["html", "htm"]: + # Convert HTML to jsondoc + jsondoc = html_to_jsondoc(content) + elif file_extension in ["md", "markdown"]: + # For markdown, we'll first convert to HTML, then to jsondoc + # This is a placeholder as we don't have a direct markdown to jsondoc converter + html_content = markdown_to_html( + content + ) # You'll need to implement this function + jsondoc = html_to_jsondoc(html_content) + else: + raise ValueError(f"Unsupported file type: {file_extension}") + + # Serialize the jsondoc + serialized_jsondoc = jsondoc_dump_json(jsondoc, indent=indent) + + if output_file: + # Write the output to a file + with open(output_file, "w") as file: + file.write(serialized_jsondoc) + else: + # Print to terminal + print(serialized_jsondoc) + + +def markdown_to_html(markdown_content): + # Placeholder function for markdown to HTML conversion + # You'll need to implement this using a markdown library + # For example, you could use the `markdown` library: + # import markdown + # return markdown.markdown(markdown_content) + raise NotImplementedError("Markdown to HTML conversion not implemented") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Convert files to jsondoc format") + parser.add_argument("input_file", help="Path to the input file") + parser.add_argument( + "-o", + "--output_file", + help="Path to the output jsondoc file (optional)", + default=None, + ) + parser.add_argument( + "--indent", + type=int, + help="Number of spaces for indentation in the output JSON file", + default=None, + ) + args = parser.parse_args() + + convert_to_jsondoc( + args.input_file, + args.output_file, + indent=args.indent, + ) diff --git a/jsondoc/bin/validate_jsondoc.py b/jsondoc/bin/validate_jsondoc.py new file mode 100644 index 0000000..feaa0ed --- /dev/null +++ b/jsondoc/bin/validate_jsondoc.py @@ -0,0 +1,18 @@ +import argparse + +from jsondoc.validate import validate_json + + +def main(): + parser = argparse.ArgumentParser(description="Validate JSON against a JSON schema") + parser.add_argument("schema", help="Path to the JSON schema file") + parser.add_argument("data", help="Path to the JSON data file") + parser.add_argument("--root", help="Root of the schema", default=None) + + args = parser.parse_args() + + validate_json(args.schema, args.data, root=args.root) + + +if __name__ == "__main__": + main() diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 121f025..f542acf 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -38,6 +38,7 @@ from jsondoc.models.block.types.table_row import TableRowBlock from jsondoc.models.block.types.to_do import ToDoBlock from jsondoc.models.block.types.toggle import ToggleBlock +from jsondoc.models.page import Page from jsondoc.models.shared_definitions import Annotations @@ -127,7 +128,8 @@ def _todict(obj): def has_direct_text(node): """ - Checks if the given BeautifulSoup node has direct text content (text that is not part of any child nodes). + Checks if the given BeautifulSoup node has direct text content + (text that is not part of any child nodes). Args: node (BeautifulSoup element): The HTML node to check. @@ -184,7 +186,6 @@ def reconcile_to_rich_text( # Get non-null/false values from annotations final_objects = [] - final_objects.append(parent_rich_text) for child in children: if isinstance(child, RichTextBase): @@ -198,6 +199,10 @@ def reconcile_to_rich_text( else: raise ValueError(f"Unsupported type: {type(child)}") + # Append the parent rich text to the final objects only if it has text + if len(parent_rich_text.plain_text) > 0: + final_objects.insert(0, parent_rich_text) + return final_objects @@ -237,17 +242,21 @@ def __init__(self, **options): " convert, but not both." ) - def convert(self, html): + def convert(self, html: str | bytes) -> Page | BlockBase | List[BlockBase]: soup = BeautifulSoup(html, "html.parser") return self.convert_soup(soup) - def convert_soup(self, soup): + def convert_soup( + self, soup, force_page=False + ) -> Page | BlockBase | List[BlockBase]: ret = self.process_tag(soup, convert_as_inline=False, children_only=True) if isinstance(ret, list): # return - # TODO: create a page and add all the blocks to it - pass + # TODO: create a page and add all the blocks to it if force_page = True + # pass + if len(ret) == 1: + ret = ret[0] return ret @@ -781,5 +790,5 @@ def convert_tr(self, el, convert_as_inline): return None # TBD -def html_to_jsondoc(html, **options): +def html_to_jsondoc(html: str | bytes, **options) -> Page | BlockBase | List[BlockBase]: return HtmlToJsonDocConverter(**options).convert(html) diff --git a/jsondoc/serialize.py b/jsondoc/serialize.py index bfe5518..250fd0f 100644 --- a/jsondoc/serialize.py +++ b/jsondoc/serialize.py @@ -1,8 +1,8 @@ import json from copy import deepcopy -from typing import Any, Dict, Type, Union +from typing import Any, Dict, List, Type, Union -from pydantic import validate_call +from pydantic import BaseModel, validate_call from jsondoc.models.block import Type as BlockType from jsondoc.models.block.base import BlockBase @@ -239,6 +239,7 @@ def load_page(obj: Union[str, Dict[str, Any]]) -> Page: page = Page(**mutable_obj) return page + @validate_call def load_jsondoc(obj: Union[str, Dict[str, Any]]) -> Page | BlockBase: if isinstance(obj, str): @@ -250,4 +251,25 @@ def load_jsondoc(obj: Union[str, Dict[str, Any]]) -> Page | BlockBase: elif object_ == "block": return load_block(obj) else: - raise ValueError("Invalid object: must be either 'page' or 'block'") \ No newline at end of file + raise ValueError("Invalid object: must be either 'page' or 'block'") + + +def base_model_dump_json(obj: BaseModel, indent: int | None = None) -> str: + return obj.model_dump_json( + serialize_as_any=True, + exclude_none=True, + indent=indent, + ) + + +@validate_call +def jsondoc_dump_json( + obj: BlockBase | List[BlockBase] | Page, + indent: int | None = None, +) -> str: + if isinstance(obj, list): + strs = [base_model_dump_json(block, indent=indent) for block in obj] + dicts = [json.loads(s) for s in strs] + return json.dumps(dicts, indent=indent) + else: + return base_model_dump_json(obj, indent=indent) diff --git a/jsondoc/utils.py b/jsondoc/utils.py index 966cab8..b23093e 100644 --- a/jsondoc/utils.py +++ b/jsondoc/utils.py @@ -11,7 +11,7 @@ def generate_id() -> str: - return uuid.uuid4().hex + return str(uuid.uuid4()) def replace_refs_with_arbitrary_object(data): diff --git a/jsondoc/validate/__init__.py b/jsondoc/validate/__init__.py index bbeb6f1..b3a85db 100644 --- a/jsondoc/validate/__init__.py +++ b/jsondoc/validate/__init__.py @@ -1,4 +1,3 @@ -import argparse import os import sys import time @@ -59,18 +58,3 @@ def retrieve(uri: str): except ValidationError as e: print(f"Validation error: {e}") sys.exit(1) - - -def main(): - parser = argparse.ArgumentParser(description="Validate JSON against a JSON schema") - parser.add_argument("schema", help="Path to the JSON schema file") - parser.add_argument("data", help="Path to the JSON data file") - parser.add_argument("--root", help="Root of the schema", default=None) - - args = parser.parse_args() - - validate_json(args.schema, args.data, root=args.root) - - -if __name__ == "__main__": - main() diff --git a/pyproject.toml b/pyproject.toml index 4c49501..efa4609 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,8 @@ readme = "README.md" packages = [{ include = "jsondoc" }] [tool.poetry.scripts] -validate_jsondoc = "jsondoc.validate:main" +validate_jsondoc = "jsondoc.bin.validate_jsondoc:main" +convert_jsondoc = "jsondoc.bin.convert_jsondoc:main" [tool.poetry.dependencies] python = "^3.11" diff --git a/tests/html_jsondoc_pairs/ex1.json b/tests/html_jsondoc_pairs/ex1.json new file mode 100644 index 0000000..2a133b9 --- /dev/null +++ b/tests/html_jsondoc_pairs/ex1.json @@ -0,0 +1,70 @@ +{ + "html": "

This is a bold word and this is an emphasized word.

", + "jsondoc": { + "object": "block", + "id": "072628f436164455a408aa5cb0e01845", + "type": "paragraph", + "created_time": "2024-09-08T08:36:34.346476Z", + "archived": false, + "in_trash": false, + "has_children": false, + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is a " + }, + "annotations": {}, + "plain_text": "This is a " + }, + { + "type": "text", + "text": { + "content": "bold" + }, + "annotations": { + "bold": true, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "bold" + }, + { + "type": "text", + "text": { + "content": " word and this is an " + }, + "annotations": {}, + "plain_text": " word and this is an " + }, + { + "type": "text", + "text": { + "content": "emphasized" + }, + "annotations": { + "bold": false, + "italic": true, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "emphasized" + }, + { + "type": "text", + "text": { + "content": " word." + }, + "annotations": {}, + "plain_text": " word." + } + ] + } + } +} diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index 51dbc03..5ed35fa 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -10,8 +10,8 @@ def test_convert_html_all_elements(): ret = html_to_jsondoc(content) print(ret) - print("\n\nConverted to markdown:\n\n") - print(jsondoc_to_markdown(ret[0])) + # print("\n\nConverted to markdown:\n\n") + # print(jsondoc_to_markdown(ret[0])) import ipdb; ipdb.set_trace() From b1d126756be02581fd25de8866a31c8d21ca6576 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:35:07 +0200 Subject: [PATCH 22/45] Added 1 html to jsondoc test example --- jsondoc/convert/html.py | 6 +- jsondoc/convert/utils.py | 1 + jsondoc/models/block/__init__.py | 2 +- jsondoc/models/block/base/__init__.py | 2 +- .../types/bulleted_list_item/__init__.py | 2 +- jsondoc/models/block/types/code/__init__.py | 2 +- jsondoc/models/block/types/column/__init__.py | 2 +- .../block/types/column_list/__init__.py | 2 +- .../models/block/types/divider/__init__.py | 2 +- .../models/block/types/equation/__init__.py | 2 +- .../models/block/types/heading_1/__init__.py | 2 +- .../models/block/types/heading_2/__init__.py | 2 +- .../models/block/types/heading_3/__init__.py | 2 +- jsondoc/models/block/types/image/__init__.py | 2 +- .../types/image/external_image/__init__.py | 2 +- .../block/types/image/file_image/__init__.py | 2 +- .../types/numbered_list_item/__init__.py | 2 +- .../models/block/types/paragraph/__init__.py | 2 +- jsondoc/models/block/types/quote/__init__.py | 2 +- .../models/block/types/rich_text/__init__.py | 2 +- .../block/types/rich_text/base/__init__.py | 2 +- .../types/rich_text/equation/__init__.py | 2 +- .../block/types/rich_text/text/__init__.py | 2 +- jsondoc/models/block/types/table/__init__.py | 2 +- .../models/block/types/table_row/__init__.py | 2 +- jsondoc/models/block/types/to_do/__init__.py | 2 +- jsondoc/models/block/types/toggle/__init__.py | 2 +- jsondoc/models/file/__init__.py | 2 +- jsondoc/models/file/base/__init__.py | 2 +- jsondoc/models/file/external/__init__.py | 2 +- jsondoc/models/file/file/__init__.py | 2 +- jsondoc/models/page/__init__.py | 2 +- jsondoc/models/shared_definitions/__init__.py | 14 +- jsondoc/utils.py | 49 + poetry.lock | 1326 +++++++++-------- pyproject.toml | 2 +- .../shared_definitions_schema.json | 24 +- tests/html_jsondoc_pairs/ex1.json | 18 +- tests/run_serialization_tests.py | 10 +- tests/test_html_to_jsondoc.py | 76 +- 40 files changed, 878 insertions(+), 708 deletions(-) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index f542acf..3807a35 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -337,7 +337,7 @@ def is_nested_node(el): # text = convert_fn(node, text, convert_as_inline) current_level_object = convert_fn(node, convert_as_inline) - print(node, repr(current_level_object)) + # print(node, repr(current_level_object)) # if children_objects: if current_level_object is None: @@ -378,9 +378,9 @@ def is_nested_node(el): # objects = [current_level_object] objects = reconcile_to_rich_text(current_level_object, children_objects) - import ipdb + # import ipdb - ipdb.set_trace() + # ipdb.set_trace() return objects diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 6c5c780..60ba06c 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -159,6 +159,7 @@ def create_divider_block( return DividerBlock( id=id, created_time=created_time, + divider={}, has_children=False, ) diff --git a/jsondoc/models/block/__init__.py b/jsondoc/models/block/__init__.py index 7b8ea3d..7efc1f2 100644 --- a/jsondoc/models/block/__init__.py +++ b/jsondoc/models/block/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/base/__init__.py b/jsondoc/models/block/base/__init__.py index 795c260..d00b096 100644 --- a/jsondoc/models/block/base/__init__.py +++ b/jsondoc/models/block/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/bulleted_list_item/__init__.py b/jsondoc/models/block/types/bulleted_list_item/__init__.py index 3b02d02..2761ed7 100644 --- a/jsondoc/models/block/types/bulleted_list_item/__init__.py +++ b/jsondoc/models/block/types/bulleted_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/code/__init__.py b/jsondoc/models/block/types/code/__init__.py index b3edba1..8270da2 100644 --- a/jsondoc/models/block/types/code/__init__.py +++ b/jsondoc/models/block/types/code/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column/__init__.py b/jsondoc/models/block/types/column/__init__.py index 65da95d..0251b88 100644 --- a/jsondoc/models/block/types/column/__init__.py +++ b/jsondoc/models/block/types/column/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column_list/__init__.py b/jsondoc/models/block/types/column_list/__init__.py index dc98108..b6c3cb3 100644 --- a/jsondoc/models/block/types/column_list/__init__.py +++ b/jsondoc/models/block/types/column_list/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/divider/__init__.py b/jsondoc/models/block/types/divider/__init__.py index 000f4f1..66a9138 100644 --- a/jsondoc/models/block/types/divider/__init__.py +++ b/jsondoc/models/block/types/divider/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/equation/__init__.py b/jsondoc/models/block/types/equation/__init__.py index 2cdd44f..03eb135 100644 --- a/jsondoc/models/block/types/equation/__init__.py +++ b/jsondoc/models/block/types/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_1/__init__.py b/jsondoc/models/block/types/heading_1/__init__.py index 5a476cb..18cc31d 100644 --- a/jsondoc/models/block/types/heading_1/__init__.py +++ b/jsondoc/models/block/types/heading_1/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_2/__init__.py b/jsondoc/models/block/types/heading_2/__init__.py index 94cc452..de12602 100644 --- a/jsondoc/models/block/types/heading_2/__init__.py +++ b/jsondoc/models/block/types/heading_2/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_3/__init__.py b/jsondoc/models/block/types/heading_3/__init__.py index 0c364ee..a98bb5c 100644 --- a/jsondoc/models/block/types/heading_3/__init__.py +++ b/jsondoc/models/block/types/heading_3/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/__init__.py b/jsondoc/models/block/types/image/__init__.py index baf8c4e..504a14f 100644 --- a/jsondoc/models/block/types/image/__init__.py +++ b/jsondoc/models/block/types/image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/external_image/__init__.py b/jsondoc/models/block/types/image/external_image/__init__.py index 193d2b7..16a996a 100644 --- a/jsondoc/models/block/types/image/external_image/__init__.py +++ b/jsondoc/models/block/types/image/external_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/file_image/__init__.py b/jsondoc/models/block/types/image/file_image/__init__.py index 1102224..3c83c4d 100644 --- a/jsondoc/models/block/types/image/file_image/__init__.py +++ b/jsondoc/models/block/types/image/file_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/numbered_list_item/__init__.py b/jsondoc/models/block/types/numbered_list_item/__init__.py index 7cc6632..64f1a7d 100644 --- a/jsondoc/models/block/types/numbered_list_item/__init__.py +++ b/jsondoc/models/block/types/numbered_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/paragraph/__init__.py b/jsondoc/models/block/types/paragraph/__init__.py index 21118bf..a0d42a8 100644 --- a/jsondoc/models/block/types/paragraph/__init__.py +++ b/jsondoc/models/block/types/paragraph/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/quote/__init__.py b/jsondoc/models/block/types/quote/__init__.py index f00127c..09b164d 100644 --- a/jsondoc/models/block/types/quote/__init__.py +++ b/jsondoc/models/block/types/quote/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/__init__.py b/jsondoc/models/block/types/rich_text/__init__.py index 0be64b9..8b06bca 100644 --- a/jsondoc/models/block/types/rich_text/__init__.py +++ b/jsondoc/models/block/types/rich_text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/base/__init__.py b/jsondoc/models/block/types/rich_text/base/__init__.py index 69bbb56..faed3e4 100644 --- a/jsondoc/models/block/types/rich_text/base/__init__.py +++ b/jsondoc/models/block/types/rich_text/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/equation/__init__.py b/jsondoc/models/block/types/rich_text/equation/__init__.py index 659f621..c1893a5 100644 --- a/jsondoc/models/block/types/rich_text/equation/__init__.py +++ b/jsondoc/models/block/types/rich_text/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/text/__init__.py b/jsondoc/models/block/types/rich_text/text/__init__.py index 4cf0b5e..c5e8110 100644 --- a/jsondoc/models/block/types/rich_text/text/__init__.py +++ b/jsondoc/models/block/types/rich_text/text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table/__init__.py b/jsondoc/models/block/types/table/__init__.py index f362464..6fbd595 100644 --- a/jsondoc/models/block/types/table/__init__.py +++ b/jsondoc/models/block/types/table/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table_row/__init__.py b/jsondoc/models/block/types/table_row/__init__.py index 9edb4fc..8bf671f 100644 --- a/jsondoc/models/block/types/table_row/__init__.py +++ b/jsondoc/models/block/types/table_row/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/to_do/__init__.py b/jsondoc/models/block/types/to_do/__init__.py index 3802d8e..5a1e4e3 100644 --- a/jsondoc/models/block/types/to_do/__init__.py +++ b/jsondoc/models/block/types/to_do/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/toggle/__init__.py b/jsondoc/models/block/types/toggle/__init__.py index 7e8a453..599b102 100644 --- a/jsondoc/models/block/types/toggle/__init__.py +++ b/jsondoc/models/block/types/toggle/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/__init__.py b/jsondoc/models/file/__init__.py index 32f440e..40f0c01 100644 --- a/jsondoc/models/file/__init__.py +++ b/jsondoc/models/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/base/__init__.py b/jsondoc/models/file/base/__init__.py index 5487fc9..f41ed49 100644 --- a/jsondoc/models/file/base/__init__.py +++ b/jsondoc/models/file/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/external/__init__.py b/jsondoc/models/file/external/__init__.py index 47b4d68..c1dbbe3 100644 --- a/jsondoc/models/file/external/__init__.py +++ b/jsondoc/models/file/external/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/file/__init__.py b/jsondoc/models/file/file/__init__.py index 5f16e03..894f680 100644 --- a/jsondoc/models/file/file/__init__.py +++ b/jsondoc/models/file/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/page/__init__.py b/jsondoc/models/page/__init__.py index fb529b2..8dc8f9b 100644 --- a/jsondoc/models/page/__init__.py +++ b/jsondoc/models/page/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations diff --git a/jsondoc/models/shared_definitions/__init__.py b/jsondoc/models/shared_definitions/__init__.py index 9f94220..cd7b795 100644 --- a/jsondoc/models/shared_definitions/__init__.py +++ b/jsondoc/models/shared_definitions/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-06T15:48:03+00:00 +# timestamp: 2024-09-08T10:03:42+00:00 from __future__ import annotations @@ -37,9 +37,9 @@ class Color(Enum): class Annotations(BaseModel): - bold: Optional[bool] = False - italic: Optional[bool] = False - strikethrough: Optional[bool] = False - underline: Optional[bool] = False - code: Optional[bool] = False - color: Optional[str] = 'default' + bold: Optional[bool] = None + italic: Optional[bool] = None + strikethrough: Optional[bool] = None + underline: Optional[bool] = None + code: Optional[bool] = None + color: Optional[str] = None diff --git a/jsondoc/utils.py b/jsondoc/utils.py index b23093e..c0fcc6f 100644 --- a/jsondoc/utils.py +++ b/jsondoc/utils.py @@ -1,4 +1,6 @@ +import difflib import json +import logging import time from contextlib import contextmanager import uuid @@ -80,3 +82,50 @@ def timer(name, unit="s"): print(f"{name} took {elapsed * 1000:.4f} milliseconds") else: print(f"{name} took {end_time - start_time:.4f} seconds") + + +def diff_strings(string1, string2): + lines1 = string1.splitlines(keepends=True) + lines2 = string2.splitlines(keepends=True) + + diff = difflib.unified_diff(lines1, lines2, lineterm="") + return "".join(diff) + + +def diff_jsonable_dict(d1: dict, d2: dict) -> str: + """ + Diffs two Python dictionaries that can be serialized to JSON. + """ + try: + d1_json = json.dumps(d1) + d2_json = json.dumps(d2) + except Exception as e: + raise ValueError(f"Failed to diff dictionaries: {e}") + + return diff_json(d1_json, d2_json) + + +def diff_json(j1: str, j2: str) -> str: + """ + Diffs two given JSON strings. + TBD: Handle other inputs, like files, bytes, etc. + """ + j1_canonical = json.dumps(json.loads(j1), indent=2, sort_keys=True) + j2_canonical = json.dumps(json.loads(j2), indent=2, sort_keys=True) + + return diff_strings(j1_canonical, j2_canonical) + + +def set_dict_recursive(d: dict | list, key: str, value: str): + """ + Set all values that match a key in a nested dictionary or list. + """ + for k, v in d.items(): + if isinstance(v, dict): + set_dict_recursive(v, key, value) + elif isinstance(v, list): + for item in v: + if isinstance(item, dict): + set_dict_recursive(item, key, value) + elif k == key: + d[k] = value diff --git a/poetry.lock b/poetry.lock index 09a7872..59c5c59 100644 --- a/poetry.lock +++ b/poetry.lock @@ -90,22 +90,22 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] [[package]] name = "attrs" -version = "23.2.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "beautifulsoup4" @@ -188,13 +188,13 @@ beautifulsoup4 = "*" [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] @@ -411,13 +411,13 @@ idna = ">=2.0.0" [[package]] name = "executing" -version = "2.0.1" +version = "2.1.0" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, + {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, + {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, ] [package.extras] @@ -425,29 +425,29 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.14.0" +version = "3.16.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"}, - {file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"}, + {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, + {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "fsspec" -version = "2024.5.0" +version = "2024.9.0" description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "fsspec-2024.5.0-py3-none-any.whl", hash = "sha256:e0fdbc446d67e182f49a70b82cf7889028a63588fde6b222521f10937b2b670c"}, - {file = "fsspec-2024.5.0.tar.gz", hash = "sha256:1d021b0b0f933e3b3029ed808eb400c08ba101ca2de4b3483fbc9ca23fcee94a"}, + {file = "fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b"}, + {file = "fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8"}, ] [package.extras] @@ -456,6 +456,7 @@ adl = ["adlfs"] arrow = ["pyarrow (>=1)"] dask = ["dask", "distributed"] dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] dropbox = ["dropbox", "dropboxdrivefs", "requests"] full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] fuse = ["fusepy"] @@ -522,13 +523,13 @@ trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" -version = "0.27.0" +version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [package.dependencies] @@ -543,16 +544,17 @@ brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "huggingface-hub" -version = "0.23.2" +version = "0.24.6" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" files = [ - {file = "huggingface_hub-0.23.2-py3-none-any.whl", hash = "sha256:48727a16e704d409c4bb5913613308499664f22a99743435dc3a13b23c485827"}, - {file = "huggingface_hub-0.23.2.tar.gz", hash = "sha256:f6829b62d5fdecb452a76fdbec620cba4c1573655a8d710c1df71735fd9edbd2"}, + {file = "huggingface_hub-0.24.6-py3-none-any.whl", hash = "sha256:a990f3232aa985fe749bc9474060cbad75e8b2f115f6665a9fda5b9c97818970"}, + {file = "huggingface_hub-0.24.6.tar.gz", hash = "sha256:cc2579e761d070713eaa9c323e3debe39d5b464ae3a7261c39a9195b27bb8000"}, ] [package.dependencies] @@ -565,28 +567,28 @@ tqdm = ">=4.42.1" typing-extensions = ">=3.7.4.3" [package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.5.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] hf-transfer = ["hf-transfer (>=0.1.4)"] inference = ["aiohttp", "minijinja (>=1.0)"] -quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.5.0)"] tensorflow = ["graphviz", "pydot", "tensorflow"] tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] -torch = ["safetensors", "torch"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "fastapi", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -621,13 +623,13 @@ ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""} [[package]] name = "ipython" -version = "8.24.0" +version = "8.27.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.24.0-py3-none-any.whl", hash = "sha256:d7bf2f6c4314984e3e02393213bab8703cf163ede39672ce5918c51fe253a2a3"}, - {file = "ipython-8.24.0.tar.gz", hash = "sha256:010db3f8a728a578bb641fdd06c063b9fb8e96a9464c63aec6310fbcb5e80501"}, + {file = "ipython-8.27.0-py3-none-any.whl", hash = "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c"}, + {file = "ipython-8.27.0.tar.gz", hash = "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e"}, ] [package.dependencies] @@ -645,7 +647,7 @@ typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"] kernel = ["ipykernel"] matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] @@ -653,7 +655,7 @@ nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] @@ -708,72 +710,72 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jiter" -version = "0.4.0" +version = "0.5.0" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" files = [ - {file = "jiter-0.4.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4aa6226d82a4a4505078c0bd5947bad65399635fc5cd4b226512e41753624edf"}, - {file = "jiter-0.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:947111ac906740a948e7b63799481acd3d5ef666ccb178d146e25718640b7408"}, - {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69572ffb4e84ae289a7422b9af4ea123cae2ce0772228859b37d4b26b4bc92ea"}, - {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba6046cbb5d1baa5a781b846f7e5438596a332f249a857d63f86ef5d1d9563b0"}, - {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4f346e54602782e66d07df0d1c7389384fd93680052ed6170da2c6dc758409e"}, - {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49110ce693f07e97d61089d894cea05a0b9894d5ccc6ac6fc583028726c8c8af"}, - {file = "jiter-0.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e358df6fd129f3a4e087539f086355ad0107e5da16dbc8bc857d94222eaeed5"}, - {file = "jiter-0.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb852ca39a48f3c049def56f0d1771b32e948e4f429a782d14ef4cc64cfd26e"}, - {file = "jiter-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:44dc045decb2545bffe2da04ea4c36d9438d3f3d49fc47ed423ea75c352b712e"}, - {file = "jiter-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:413adb15372ac63db04373240f40925788e4282c997eeafc2040530049a0a599"}, - {file = "jiter-0.4.0-cp310-none-win32.whl", hash = "sha256:0b48ea71673a97b897e4b94bbc871e62495a5a85f836c9f90712a4c70aa3ef7e"}, - {file = "jiter-0.4.0-cp310-none-win_amd64.whl", hash = "sha256:6a1c84b44afafaf0ba6223679cf17af664b889da14da31d8af3595fd977d96fa"}, - {file = "jiter-0.4.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b2cc498345fa37ca23fbc20271a553aa46e6eb00924600f49b7dc4b2aa8952ee"}, - {file = "jiter-0.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69f7221ac09ab421abf04f89942026868297c568133998fb181bcf435760cbf3"}, - {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7d01c52f3e5a56ae73af36bd13797dd1a56711eb522748e5e84d15425b3f10"}, - {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:39be97d5ce0c4d0dae28c23c03a0af0501a725589427e99763f99c42e18aa402"}, - {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eac2ed1ec1e577b92b7ea2d4e6de8aec0c1164defd8af8affdc8ec0f0ec2904a"}, - {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6258837d184c92c9cb91c983c310ad7269d41afb49d34f00ca9246e073943a03"}, - {file = "jiter-0.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:123c2a77b066bf17a4d021e238e8351058cfa56b90ac04f2522d120dc64ea055"}, - {file = "jiter-0.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2df939f792c7a40e55f36700417db551b9f6b84d348990fa0f2c608adeb1f11b"}, - {file = "jiter-0.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cb1b09b16d40cf9ba1d11ba11e5b96ad29286a6a1c4ad5e6a2aef5e352a89f5d"}, - {file = "jiter-0.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0efb4208889ebdbf933bf08dbcbc16e64ffd34c8e2b28044ee142789a9dc3a67"}, - {file = "jiter-0.4.0-cp311-none-win32.whl", hash = "sha256:20545ac1b68e7e5b066a1e8347840c9cebdd02ace65faae2e655fc02ec5c915c"}, - {file = "jiter-0.4.0-cp311-none-win_amd64.whl", hash = "sha256:6b300f9887c8e4431cd03a974ea3e4f9958885636003c3864220a9b2d2f8462b"}, - {file = "jiter-0.4.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:923432a0563bbae404ff25bb010e348514a69bfab979f2f8119b23b625dbf6d9"}, - {file = "jiter-0.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab8bb0ec8b97cec4422dc8b37b525442d969244488c805b834609ab0ccd788e2"}, - {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b857adb127b9c533907226791eafa79c5038c3eb5a477984994bf7c4715ba518"}, - {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2609cc0d1d8d470e921ff9a604afeb4c701bbe13e00bd9834d5aa6e7ea732a9b"}, - {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d39e99f8b7df46a119b6f84321f6ba01f16fa46abfa765d44c05c486d8e66829"}, - {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:56de8b518ebfe76a70f856741f6de248ce396c50a87acef827b6e8388e3a502d"}, - {file = "jiter-0.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488b7e777be47f67ce1a1f8f8eb907f9bbd81af5c03784a9bab09d025c250233"}, - {file = "jiter-0.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7ea35e0ecbb5dadd457855eb980dcc548c14cf5341bcd22a43814cb56f2bcc79"}, - {file = "jiter-0.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e1a9e9ee69c80b63951c93226b68d0e955953f64fe758bad2afe7ef7f9016af9"}, - {file = "jiter-0.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:78e2f3cc2a32a21d43ccc5afcf66f5d17e827ccc4e6d21c0b353bdad2c7dcc9c"}, - {file = "jiter-0.4.0-cp312-none-win32.whl", hash = "sha256:eeaa7a2b47a99f4ebbb4142bb58b95617e09f24c87570f6a57d2770687c9ddbe"}, - {file = "jiter-0.4.0-cp312-none-win_amd64.whl", hash = "sha256:8d4a78b385b93ff59a67215d26000fcb4789a388fca3730d1b60fab17fc81e3c"}, - {file = "jiter-0.4.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ebf20a3fac1089ce26963bf04140da0f803d55332ec69d59c5a87cf1a87d29c4"}, - {file = "jiter-0.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d62244ffc6a168187452277adeefb7b2c30170689c6bf543a51e98e8c17ddab7"}, - {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40b2cde77446a41cec595739fd168be87edff2428eaf7c3438231224dd0ab7a5"}, - {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e51fc0a22021ec8905b9b00a2f7d25756f2ff7a653e35a790a2067ae126b51f6"}, - {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a56e6f980b89d7cfe5c43811dcf52d6f37b319428a4540511235dafda9ea7808"}, - {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fec16adab8d3d3d6d74e3711a1f380836ebeab2a20e3f88cfe2ec5094d8b84"}, - {file = "jiter-0.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19e3de515801c954e8f1dc1f575282a4a86df9e782d4993ea1ed2be9a8dedaa0"}, - {file = "jiter-0.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17e0ad8abf0bb04d81810eaeaab35d2c99b5da11fcd1058e0a389607ff6503b0"}, - {file = "jiter-0.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8dc0132b728f3b3e90ff0d1874504cd49c78f3553bf3745168a7fc0b4cf674e1"}, - {file = "jiter-0.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81a883104aa96e494d3d28eaf7070780d03ecee8ccfdfaf7e4899710340c47f1"}, - {file = "jiter-0.4.0-cp38-none-win32.whl", hash = "sha256:a044c53ab1aaa4af624ac9574181b5bad8e260aea7e03104738156511433deba"}, - {file = "jiter-0.4.0-cp38-none-win_amd64.whl", hash = "sha256:d920035c869053e3d9a0b3ff94384d16a8ef5fde3dea55f97bd29916f6e27554"}, - {file = "jiter-0.4.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:091e978f4e586a2f1c69bf940d45f4e6a23455877172a0ab7d6de04a3b119299"}, - {file = "jiter-0.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79134b2d601309bcbe3304a262d7d228ad61d53c80883231c637773000a6d683"}, - {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c471473e0b05058b5d729ff04271b6d45a575ac8bd9948563268c734b380ac7e"}, - {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb84b8930da8b32b0b1fdff9817e2c4b47e8981b5647ad11c4975403416e4112"}, - {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f2805e28941751ebfe0948596a64cde4cfb9b84bea5282affd020063e659c96"}, - {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42ef59f9e513bf081a8b5c5578933ea9c3a63e559e6e3501a3e72edcd456ff5e"}, - {file = "jiter-0.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ae12e3906f9e565120ab569de261b738e3a1ec50c40e30c67499e4f893e9a8c"}, - {file = "jiter-0.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:264dc1324f45a793bc89af4f653225229eb17bca9ec7107dce6c8fb4fe68d20f"}, - {file = "jiter-0.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9a1c172ec47d846e25881dfbd52438ddb690da4ea04d185e477abd3db6c32f8a"}, - {file = "jiter-0.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ccde31d0bc114aedad0dbd71b7f63ba0f0eecd7ec9ae1926a0ca01c1eb2854e7"}, - {file = "jiter-0.4.0-cp39-none-win32.whl", hash = "sha256:13139b05792fbc13a0f9a5b4c89823ea0874141decae1b8f693f12bb1d28e061"}, - {file = "jiter-0.4.0-cp39-none-win_amd64.whl", hash = "sha256:3a729b2631c6d5551a41069697415fee9659c3eadc9ab87369376ba51930cd00"}, - {file = "jiter-0.4.0.tar.gz", hash = "sha256:68203e02e0419bc3eca717c580c2d8f615aeee1150e2a1fb68d6600a7e52a37c"}, + {file = "jiter-0.5.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b599f4e89b3def9a94091e6ee52e1d7ad7bc33e238ebb9c4c63f211d74822c3f"}, + {file = "jiter-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a063f71c4b06225543dddadbe09d203dc0c95ba352d8b85f1221173480a71d5"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc0d5b8b3dd12e91dd184b87273f864b363dfabc90ef29a1092d269f18c7e28"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c22541f0b672f4d741382a97c65609332a783501551445ab2df137ada01e019e"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63314832e302cc10d8dfbda0333a384bf4bcfce80d65fe99b0f3c0da8945a91a"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a25fbd8a5a58061e433d6fae6d5298777c0814a8bcefa1e5ecfff20c594bd749"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:503b2c27d87dfff5ab717a8200fbbcf4714516c9d85558048b1fc14d2de7d8dc"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d1f3d27cce923713933a844872d213d244e09b53ec99b7a7fdf73d543529d6d"}, + {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c95980207b3998f2c3b3098f357994d3fd7661121f30669ca7cb945f09510a87"}, + {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afa66939d834b0ce063f57d9895e8036ffc41c4bd90e4a99631e5f261d9b518e"}, + {file = "jiter-0.5.0-cp310-none-win32.whl", hash = "sha256:f16ca8f10e62f25fd81d5310e852df6649af17824146ca74647a018424ddeccf"}, + {file = "jiter-0.5.0-cp310-none-win_amd64.whl", hash = "sha256:b2950e4798e82dd9176935ef6a55cf6a448b5c71515a556da3f6b811a7844f1e"}, + {file = "jiter-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4c8e1ed0ef31ad29cae5ea16b9e41529eb50a7fba70600008e9f8de6376d553"}, + {file = "jiter-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f16e21276074a12d8421692515b3fd6d2ea9c94fd0734c39a12960a20e85f3"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280e68e7740c8c128d3ae5ab63335ce6d1fb6603d3b809637b11713487af9e6"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:583c57fc30cc1fec360e66323aadd7fc3edeec01289bfafc35d3b9dcb29495e4"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26351cc14507bdf466b5f99aba3df3143a59da75799bf64a53a3ad3155ecded9"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829df14d656b3fb87e50ae8b48253a8851c707da9f30d45aacab2aa2ba2d614"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42a4bdcf7307b86cb863b2fb9bb55029b422d8f86276a50487982d99eed7c6e"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04d461ad0aebf696f8da13c99bc1b3e06f66ecf6cfd56254cc402f6385231c06"}, + {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6375923c5f19888c9226582a124b77b622f8fd0018b843c45eeb19d9701c403"}, + {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cec323a853c24fd0472517113768c92ae0be8f8c384ef4441d3632da8baa646"}, + {file = "jiter-0.5.0-cp311-none-win32.whl", hash = "sha256:aa1db0967130b5cab63dfe4d6ff547c88b2a394c3410db64744d491df7f069bb"}, + {file = "jiter-0.5.0-cp311-none-win_amd64.whl", hash = "sha256:aa9d2b85b2ed7dc7697597dcfaac66e63c1b3028652f751c81c65a9f220899ae"}, + {file = "jiter-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9f664e7351604f91dcdd557603c57fc0d551bc65cc0a732fdacbf73ad335049a"}, + {file = "jiter-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:044f2f1148b5248ad2c8c3afb43430dccf676c5a5834d2f5089a4e6c5bbd64df"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:702e3520384c88b6e270c55c772d4bd6d7b150608dcc94dea87ceba1b6391248"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:528d742dcde73fad9d63e8242c036ab4a84389a56e04efd854062b660f559544"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf80e5fe6ab582c82f0c3331df27a7e1565e2dcf06265afd5173d809cdbf9ba"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44dfc9ddfb9b51a5626568ef4e55ada462b7328996294fe4d36de02fce42721f"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c451f7922992751a936b96c5f5b9bb9312243d9b754c34b33d0cb72c84669f4e"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:308fce789a2f093dca1ff91ac391f11a9f99c35369117ad5a5c6c4903e1b3e3a"}, + {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7f5ad4a7c6b0d90776fdefa294f662e8a86871e601309643de30bf94bb93a64e"}, + {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea189db75f8eca08807d02ae27929e890c7d47599ce3d0a6a5d41f2419ecf338"}, + {file = "jiter-0.5.0-cp312-none-win32.whl", hash = "sha256:e3bbe3910c724b877846186c25fe3c802e105a2c1fc2b57d6688b9f8772026e4"}, + {file = "jiter-0.5.0-cp312-none-win_amd64.whl", hash = "sha256:a586832f70c3f1481732919215f36d41c59ca080fa27a65cf23d9490e75b2ef5"}, + {file = "jiter-0.5.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f04bc2fc50dc77be9d10f73fcc4e39346402ffe21726ff41028f36e179b587e6"}, + {file = "jiter-0.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f433a4169ad22fcb550b11179bb2b4fd405de9b982601914ef448390b2954f3"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad4a6398c85d3a20067e6c69890ca01f68659da94d74c800298581724e426c7e"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6baa88334e7af3f4d7a5c66c3a63808e5efbc3698a1c57626541ddd22f8e4fbf"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ece0a115c05efca597c6d938f88c9357c843f8c245dbbb53361a1c01afd7148"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:335942557162ad372cc367ffaf93217117401bf930483b4b3ebdb1223dbddfa7"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649b0ee97a6e6da174bffcb3c8c051a5935d7d4f2f52ea1583b5b3e7822fbf14"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4be354c5de82157886ca7f5925dbda369b77344b4b4adf2723079715f823989"}, + {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5206144578831a6de278a38896864ded4ed96af66e1e63ec5dd7f4a1fce38a3a"}, + {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8120c60f8121ac3d6f072b97ef0e71770cc72b3c23084c72c4189428b1b1d3b6"}, + {file = "jiter-0.5.0-cp38-none-win32.whl", hash = "sha256:6f1223f88b6d76b519cb033a4d3687ca157c272ec5d6015c322fc5b3074d8a5e"}, + {file = "jiter-0.5.0-cp38-none-win_amd64.whl", hash = "sha256:c59614b225d9f434ea8fc0d0bec51ef5fa8c83679afedc0433905994fb36d631"}, + {file = "jiter-0.5.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0af3838cfb7e6afee3f00dc66fa24695199e20ba87df26e942820345b0afc566"}, + {file = "jiter-0.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:550b11d669600dbc342364fd4adbe987f14d0bbedaf06feb1b983383dcc4b961"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:489875bf1a0ffb3cb38a727b01e6673f0f2e395b2aad3c9387f94187cb214bbf"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b250ca2594f5599ca82ba7e68785a669b352156260c5362ea1b4e04a0f3e2389"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ea18e01f785c6667ca15407cd6dabbe029d77474d53595a189bdc813347218e"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462a52be85b53cd9bffd94e2d788a09984274fe6cebb893d6287e1c296d50653"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92cc68b48d50fa472c79c93965e19bd48f40f207cb557a8346daa020d6ba973b"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c834133e59a8521bc87ebcad773608c6fa6ab5c7a022df24a45030826cf10bc"}, + {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab3a71ff31cf2d45cb216dc37af522d335211f3a972d2fe14ea99073de6cb104"}, + {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cccd3af9c48ac500c95e1bcbc498020c87e1781ff0345dd371462d67b76643eb"}, + {file = "jiter-0.5.0-cp39-none-win32.whl", hash = "sha256:368084d8d5c4fc40ff7c3cc513c4f73e02c85f6009217922d0823a48ee7adf61"}, + {file = "jiter-0.5.0-cp39-none-win_amd64.whl", hash = "sha256:ce03f7b4129eb72f1687fa11300fbf677b02990618428934662406d2a76742a1"}, + {file = "jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a"}, ] [[package]] @@ -907,13 +909,13 @@ files = [ [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -958,84 +960,95 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "10.3.0" +version = "10.4.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, - {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, - {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, - {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, - {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, - {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, - {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, - {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, - {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, - {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, - {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, - {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, - {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, - {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, - {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, - {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, - {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, - {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, - {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, - {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, - {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, - {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, - {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, - {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, - {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, - {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, - {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, - {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, - {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, - {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, - {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, - {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, - {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] @@ -1044,13 +1057,13 @@ xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.1-py3-none-any.whl", hash = "sha256:facaa5a3c57aa1e053e3da7b49e0cc31fe0113ca42a4659d5c2e98e545624afe"}, + {file = "platformdirs-4.3.1.tar.gz", hash = "sha256:63b79589009fa8159973601dd4563143396b35c5f93a58b36f9049ff046949b1"}, ] [package.extras] @@ -1060,13 +1073,13 @@ type = ["mypy (>=1.8)"] [[package]] name = "prompt-toolkit" -version = "3.0.45" +version = "3.0.47" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.45-py3-none-any.whl", hash = "sha256:a29b89160e494e3ea8622b09fa5897610b437884dcdcd054fdc1308883326c2a"}, - {file = "prompt_toolkit-3.0.45.tar.gz", hash = "sha256:07c60ee4ab7b7e90824b61afa840c8f5aad2d46b3e2e10acc33d8ecc94a49089"}, + {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, + {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, ] [package.dependencies] @@ -1085,13 +1098,13 @@ files = [ [[package]] name = "pure-eval" -version = "0.2.2" +version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, + {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, + {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, ] [package.extras] @@ -1099,110 +1112,124 @@ tests = ["pytest"] [[package]] name = "pydantic" -version = "2.7.2" +version = "2.9.0" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"}, - {file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"}, + {file = "pydantic-2.9.0-py3-none-any.whl", hash = "sha256:f66a7073abd93214a20c5f7b32d56843137a7a2e70d02111f3be287035c45370"}, + {file = "pydantic-2.9.0.tar.gz", hash = "sha256:c7a8a9fdf7d100afa49647eae340e2d23efa382466a8d177efcd1381e9be5598"}, ] [package.dependencies] annotated-types = ">=0.4.0" email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} -pydantic-core = "2.18.3" -typing-extensions = ">=4.6.1" +pydantic-core = "2.23.2" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] +tzdata = {version = "*", markers = "python_version >= \"3.9\""} [package.extras] email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.18.3" +version = "2.23.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"}, - {file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"}, - {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"}, - {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"}, - {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"}, - {file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"}, - {file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"}, - {file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"}, - {file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"}, - {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"}, - {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"}, - {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"}, - {file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"}, - {file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"}, - {file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"}, - {file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"}, - {file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"}, - {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"}, - {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"}, - {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"}, - {file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"}, - {file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"}, - {file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"}, - {file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"}, - {file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"}, - {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"}, - {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"}, - {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"}, - {file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"}, - {file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"}, - {file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"}, - {file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"}, - {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"}, - {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"}, - {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"}, - {file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"}, - {file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"}, - {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"}, - {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"}, - {file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"}, + {file = "pydantic_core-2.23.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7d0324a35ab436c9d768753cbc3c47a865a2cbc0757066cb864747baa61f6ece"}, + {file = "pydantic_core-2.23.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:276ae78153a94b664e700ac362587c73b84399bd1145e135287513442e7dfbc7"}, + {file = "pydantic_core-2.23.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:964c7aa318da542cdcc60d4a648377ffe1a2ef0eb1e996026c7f74507b720a78"}, + {file = "pydantic_core-2.23.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1cf842265a3a820ebc6388b963ead065f5ce8f2068ac4e1c713ef77a67b71f7c"}, + {file = "pydantic_core-2.23.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae90b9e50fe1bd115b24785e962b51130340408156d34d67b5f8f3fa6540938e"}, + {file = "pydantic_core-2.23.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ae65fdfb8a841556b52935dfd4c3f79132dc5253b12c0061b96415208f4d622"}, + {file = "pydantic_core-2.23.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c8aa40f6ca803f95b1c1c5aeaee6237b9e879e4dfb46ad713229a63651a95fb"}, + {file = "pydantic_core-2.23.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c53100c8ee5a1e102766abde2158077d8c374bee0639201f11d3032e3555dfbc"}, + {file = "pydantic_core-2.23.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d6b9dd6aa03c812017411734e496c44fef29b43dba1e3dd1fa7361bbacfc1354"}, + {file = "pydantic_core-2.23.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b18cf68255a476b927910c6873d9ed00da692bb293c5b10b282bd48a0afe3ae2"}, + {file = "pydantic_core-2.23.2-cp310-none-win32.whl", hash = "sha256:e460475719721d59cd54a350c1f71c797c763212c836bf48585478c5514d2854"}, + {file = "pydantic_core-2.23.2-cp310-none-win_amd64.whl", hash = "sha256:5f3cf3721eaf8741cffaf092487f1ca80831202ce91672776b02b875580e174a"}, + {file = "pydantic_core-2.23.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7ce8e26b86a91e305858e018afc7a6e932f17428b1eaa60154bd1f7ee888b5f8"}, + {file = "pydantic_core-2.23.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e9b24cca4037a561422bf5dc52b38d390fb61f7bfff64053ce1b72f6938e6b2"}, + {file = "pydantic_core-2.23.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753294d42fb072aa1775bfe1a2ba1012427376718fa4c72de52005a3d2a22178"}, + {file = "pydantic_core-2.23.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:257d6a410a0d8aeb50b4283dea39bb79b14303e0fab0f2b9d617701331ed1515"}, + {file = "pydantic_core-2.23.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c8319e0bd6a7b45ad76166cc3d5d6a36c97d0c82a196f478c3ee5346566eebfd"}, + {file = "pydantic_core-2.23.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a05c0240f6c711eb381ac392de987ee974fa9336071fb697768dfdb151345ce"}, + {file = "pydantic_core-2.23.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d5b0ff3218858859910295df6953d7bafac3a48d5cd18f4e3ed9999efd2245f"}, + {file = "pydantic_core-2.23.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:96ef39add33ff58cd4c112cbac076726b96b98bb8f1e7f7595288dcfb2f10b57"}, + {file = "pydantic_core-2.23.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0102e49ac7d2df3379ef8d658d3bc59d3d769b0bdb17da189b75efa861fc07b4"}, + {file = "pydantic_core-2.23.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a6612c2a844043e4d10a8324c54cdff0042c558eef30bd705770793d70b224aa"}, + {file = "pydantic_core-2.23.2-cp311-none-win32.whl", hash = "sha256:caffda619099cfd4f63d48462f6aadbecee3ad9603b4b88b60cb821c1b258576"}, + {file = "pydantic_core-2.23.2-cp311-none-win_amd64.whl", hash = "sha256:6f80fba4af0cb1d2344869d56430e304a51396b70d46b91a55ed4959993c0589"}, + {file = "pydantic_core-2.23.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4c83c64d05ffbbe12d4e8498ab72bdb05bcc1026340a4a597dc647a13c1605ec"}, + {file = "pydantic_core-2.23.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6294907eaaccf71c076abdd1c7954e272efa39bb043161b4b8aa1cd76a16ce43"}, + {file = "pydantic_core-2.23.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a801c5e1e13272e0909c520708122496647d1279d252c9e6e07dac216accc41"}, + {file = "pydantic_core-2.23.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cc0c316fba3ce72ac3ab7902a888b9dc4979162d320823679da270c2d9ad0cad"}, + {file = "pydantic_core-2.23.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b06c5d4e8701ac2ba99a2ef835e4e1b187d41095a9c619c5b185c9068ed2a49"}, + {file = "pydantic_core-2.23.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82764c0bd697159fe9947ad59b6db6d7329e88505c8f98990eb07e84cc0a5d81"}, + {file = "pydantic_core-2.23.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b1a195efd347ede8bcf723e932300292eb13a9d2a3c1f84eb8f37cbbc905b7f"}, + {file = "pydantic_core-2.23.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7efb12e5071ad8d5b547487bdad489fbd4a5a35a0fc36a1941517a6ad7f23e0"}, + {file = "pydantic_core-2.23.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5dd0ec5f514ed40e49bf961d49cf1bc2c72e9b50f29a163b2cc9030c6742aa73"}, + {file = "pydantic_core-2.23.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:820f6ee5c06bc868335e3b6e42d7ef41f50dfb3ea32fbd523ab679d10d8741c0"}, + {file = "pydantic_core-2.23.2-cp312-none-win32.whl", hash = "sha256:3713dc093d5048bfaedbba7a8dbc53e74c44a140d45ede020dc347dda18daf3f"}, + {file = "pydantic_core-2.23.2-cp312-none-win_amd64.whl", hash = "sha256:e1895e949f8849bc2757c0dbac28422a04be031204df46a56ab34bcf98507342"}, + {file = "pydantic_core-2.23.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:da43cbe593e3c87d07108d0ebd73771dc414488f1f91ed2e204b0370b94b37ac"}, + {file = "pydantic_core-2.23.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:64d094ea1aa97c6ded4748d40886076a931a8bf6f61b6e43e4a1041769c39dd2"}, + {file = "pydantic_core-2.23.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:084414ffe9a85a52940b49631321d636dadf3576c30259607b75516d131fecd0"}, + {file = "pydantic_core-2.23.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:043ef8469f72609c4c3a5e06a07a1f713d53df4d53112c6d49207c0bd3c3bd9b"}, + {file = "pydantic_core-2.23.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3649bd3ae6a8ebea7dc381afb7f3c6db237fc7cebd05c8ac36ca8a4187b03b30"}, + {file = "pydantic_core-2.23.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6db09153d8438425e98cdc9a289c5fade04a5d2128faff8f227c459da21b9703"}, + {file = "pydantic_core-2.23.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5668b3173bb0b2e65020b60d83f5910a7224027232c9f5dc05a71a1deac9f960"}, + {file = "pydantic_core-2.23.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c7b81beaf7c7ebde978377dc53679c6cba0e946426fc7ade54251dfe24a7604"}, + {file = "pydantic_core-2.23.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:ae579143826c6f05a361d9546446c432a165ecf1c0b720bbfd81152645cb897d"}, + {file = "pydantic_core-2.23.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:19f1352fe4b248cae22a89268720fc74e83f008057a652894f08fa931e77dced"}, + {file = "pydantic_core-2.23.2-cp313-none-win32.whl", hash = "sha256:e1a79ad49f346aa1a2921f31e8dbbab4d64484823e813a002679eaa46cba39e1"}, + {file = "pydantic_core-2.23.2-cp313-none-win_amd64.whl", hash = "sha256:582871902e1902b3c8e9b2c347f32a792a07094110c1bca6c2ea89b90150caac"}, + {file = "pydantic_core-2.23.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:743e5811b0c377eb830150d675b0847a74a44d4ad5ab8845923d5b3a756d8100"}, + {file = "pydantic_core-2.23.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6650a7bbe17a2717167e3e23c186849bae5cef35d38949549f1c116031b2b3aa"}, + {file = "pydantic_core-2.23.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56e6a12ec8d7679f41b3750ffa426d22b44ef97be226a9bab00a03365f217b2b"}, + {file = "pydantic_core-2.23.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:810ca06cca91de9107718dc83d9ac4d2e86efd6c02cba49a190abcaf33fb0472"}, + {file = "pydantic_core-2.23.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:785e7f517ebb9890813d31cb5d328fa5eda825bb205065cde760b3150e4de1f7"}, + {file = "pydantic_core-2.23.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ef71ec876fcc4d3bbf2ae81961959e8d62f8d74a83d116668409c224012e3af"}, + {file = "pydantic_core-2.23.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d50ac34835c6a4a0d456b5db559b82047403c4317b3bc73b3455fefdbdc54b0a"}, + {file = "pydantic_core-2.23.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16b25a4a120a2bb7dab51b81e3d9f3cde4f9a4456566c403ed29ac81bf49744f"}, + {file = "pydantic_core-2.23.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:41ae8537ad371ec018e3c5da0eb3f3e40ee1011eb9be1da7f965357c4623c501"}, + {file = "pydantic_core-2.23.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07049ec9306ec64e955b2e7c40c8d77dd78ea89adb97a2013d0b6e055c5ee4c5"}, + {file = "pydantic_core-2.23.2-cp38-none-win32.whl", hash = "sha256:086c5db95157dc84c63ff9d96ebb8856f47ce113c86b61065a066f8efbe80acf"}, + {file = "pydantic_core-2.23.2-cp38-none-win_amd64.whl", hash = "sha256:67b6655311b00581914aba481729971b88bb8bc7996206590700a3ac85e457b8"}, + {file = "pydantic_core-2.23.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:358331e21a897151e54d58e08d0219acf98ebb14c567267a87e971f3d2a3be59"}, + {file = "pydantic_core-2.23.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c4d9f15ffe68bcd3898b0ad7233af01b15c57d91cd1667f8d868e0eacbfe3f87"}, + {file = "pydantic_core-2.23.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0123655fedacf035ab10c23450163c2f65a4174f2bb034b188240a6cf06bb123"}, + {file = "pydantic_core-2.23.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e6e3ccebdbd6e53474b0bb7ab8b88e83c0cfe91484b25e058e581348ee5a01a5"}, + {file = "pydantic_core-2.23.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc535cb898ef88333cf317777ecdfe0faac1c2a3187ef7eb061b6f7ecf7e6bae"}, + {file = "pydantic_core-2.23.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aab9e522efff3993a9e98ab14263d4e20211e62da088298089a03056980a3e69"}, + {file = "pydantic_core-2.23.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05b366fb8fe3d8683b11ac35fa08947d7b92be78ec64e3277d03bd7f9b7cda79"}, + {file = "pydantic_core-2.23.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7568f682c06f10f30ef643a1e8eec4afeecdafde5c4af1b574c6df079e96f96c"}, + {file = "pydantic_core-2.23.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:cdd02a08205dc90238669f082747612cb3c82bd2c717adc60f9b9ecadb540f80"}, + {file = "pydantic_core-2.23.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a2ab4f410f4b886de53b6bddf5dd6f337915a29dd9f22f20f3099659536b2f6"}, + {file = "pydantic_core-2.23.2-cp39-none-win32.whl", hash = "sha256:0448b81c3dfcde439551bb04a9f41d7627f676b12701865c8a2574bcea034437"}, + {file = "pydantic_core-2.23.2-cp39-none-win_amd64.whl", hash = "sha256:4cebb9794f67266d65e7e4cbe5dcf063e29fc7b81c79dc9475bd476d9534150e"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e758d271ed0286d146cf7c04c539a5169a888dd0b57026be621547e756af55bc"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f477d26183e94eaafc60b983ab25af2a809a1b48ce4debb57b343f671b7a90b6"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da3131ef2b940b99106f29dfbc30d9505643f766704e14c5d5e504e6a480c35e"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329a721253c7e4cbd7aad4a377745fbcc0607f9d72a3cc2102dd40519be75ed2"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7706e15cdbf42f8fab1e6425247dfa98f4a6f8c63746c995d6a2017f78e619ae"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e64ffaf8f6e17ca15eb48344d86a7a741454526f3a3fa56bc493ad9d7ec63936"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dd59638025160056687d598b054b64a79183f8065eae0d3f5ca523cde9943940"}, + {file = "pydantic_core-2.23.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:12625e69b1199e94b0ae1c9a95d000484ce9f0182f9965a26572f054b1537e44"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5d813fd871b3d5c3005157622ee102e8908ad6011ec915a18bd8fde673c4360e"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1eb37f7d6a8001c0f86dc8ff2ee8d08291a536d76e49e78cda8587bb54d8b329"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce7eaf9a98680b4312b7cebcdd9352531c43db00fca586115845df388f3c465"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f087879f1ffde024dd2788a30d55acd67959dcf6c431e9d3682d1c491a0eb474"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ce883906810b4c3bd90e0ada1f9e808d9ecf1c5f0b60c6b8831d6100bcc7dd6"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a8031074a397a5925d06b590121f8339d34a5a74cfe6970f8a1124eb8b83f4ac"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23af245b8f2f4ee9e2c99cb3f93d0e22fb5c16df3f2f643f5a8da5caff12a653"}, + {file = "pydantic_core-2.23.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c57e493a0faea1e4c38f860d6862ba6832723396c884fbf938ff5e9b224200e2"}, + {file = "pydantic_core-2.23.2.tar.gz", hash = "sha256:95d6bf449a1ac81de562d65d180af5d8c19672793c81877a2eda8fde5d08f2fd"}, ] [package.dependencies] @@ -1238,153 +1265,181 @@ cli = ["click (>=5.0)"] [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] name = "rapidfuzz" -version = "3.9.2" +version = "3.9.7" description = "rapid fuzzy string matching" optional = false python-versions = ">=3.8" files = [ - {file = "rapidfuzz-3.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45e0c3e279e70589381f47ad410de7211bac943e827eb09eb8339d2124abca90"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:280ef2f3066df9c486ffd3874d2489978fb8021044c47c006eb96be8d47917d7"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe128ac0e05ca3a71d8ff18e70884a64fde00b6fbd2b4d9f59f7a4d798257c55"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fbc0f6e1b6f4063b937d0edcf0a56cbc1d7179ade9b7d6c849c94e44a7b20f6"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:df19455c2fb85e86a721111b84ac8dd3685194f0edc9faefb226731ad3e134a7"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:801a5d97c465a3467b3cdf50cdcdadec129ddca582b24430f5d24c715c80be9b"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81f218524596d261a6cb33cda965687e62dd30def478d39f0befa243642c3985"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5c61d53f293b4e3286919b0e081513367afabcb5aef0b6f899d006117778e558"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0ed70fc6627ae37319f822e5d8d21d561044e0b3331b6f0e6904476faa8d8ed7"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:96fa229d06ee005d2f46374fb2af65590a590a6fa2fd56e66474829f5fa9adfe"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6609e881b57cabb40d515cc226bbf570e32e768bd2cc688ba026a45ffbc60875"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:204fd4d293ef4d409c4142ddf830b7613924b998670f67e512ab1f880a60218a"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-win32.whl", hash = "sha256:5b331a09446bc8f8971cf488c9e6c0f7dbf2739828588e063cf08fd400638a24"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:01a9975984953fe549649e6a4c3f0d9c60707acf458184ec09678d6a57560112"}, - {file = "rapidfuzz-3.9.2-cp310-cp310-win_arm64.whl", hash = "sha256:ca4af5d7fc9c17bdc498aa1cab9ecf5140c8535c9cedeba1990bbe4b8be75098"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:300ab53981a5d6831fe7e0f30c407c79520ad0f0ab51b2cece8717689026f495"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f4828642acdb075154ce2ff3260f8afb6a17b5b0c8a437efbadac06e9995dd7b"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b262883c3ce93dee1a9a974992961c8098e96b8142e2e01cabdb15ea8105c4a"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf8582d85e35641734d6c1f43eb37c1f2a5eda338d3cfa8e651e078246b9ec58"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e33b61ef87e1876d216c479fa2256233b3bb0424465ab2db1d94ab7b8649ae1c"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fa1b3eb21756003a6a3977847dd4e0e9a26e2e02731d9daa5e92a9258e7f0db"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923ae0301a56356364f1159e3005fbeb2191e7a0e8705d5cc1b481d9eea27b97"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8e4041cfd87f0a022aa8a9a187d3b0824e35be2bd9b3bceada11578ddd9ad65"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1f832b430f976727bdbba009ee64acda25412602976fbfb2113d41e765d81849"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6ce5e57e0c6acf5a98ffbdfaf8bccb6e41fbddb9eda3e041f4cc69b7cade5fa0"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d65f34e71102d9cbe733d4ba1c645e7623eef850562501bab1ac79d217831436"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5dd9ba4df0db46b9f909289e4687cc7721c622985c4cd169969005dd30fc1e24"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-win32.whl", hash = "sha256:34c8bca3fef33d7e71f290de68be2184fac7a9e136fa0ed22b17ec597e181406"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:91e1a8872c0b8aef95c33db86d25e8bdea6f557b9cdf683123c25035b2bcfb8e"}, - {file = "rapidfuzz-3.9.2-cp311-cp311-win_arm64.whl", hash = "sha256:ed02d73e46b7a4604d2bc1e0364b25f204862d40dd162f6b36ee22b9bf6d9df2"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ae6c4ba2778b097397968130f2b0cb795cdc415c115539a49ce798f606152ad5"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7270556ddebaa98fb777f493f17ed6a733b3527de16c43342bce1db109042845"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4625273447bdd94f2ab06b2951cd8b74356c3a48552208279a3ec2947ceee141"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5107b5ec8821453f7cac70b2d0bc4866699b25bff4819ada8b28bf2b11e87f65"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b04c851d309df8261ed42951444db657936234ceddf4032f4409b0214c95ecbe"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aeefff80f3f5d6841c30ffe0cdc84d62874de5a64cff509ae26fbd7478297af8"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cdc106b5a99edd46443449c767287dbb5d4464a7536475a365e368e7ee4d651"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ce253a2b7a71a01a4abac71ac31fd05f6ac1f1cd2af2d98fa80fe5c402175e54"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5c30407cadbfe99753b7a996f0dd6da490b1e27d318c01db227e8f49770a01ec"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:fb3fc387783f70387a91aababd8a5faeb230931b655ad99bcf838cd72404ba66"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c409852a89535ec8720301a847bab198c1c14d0f34ed07dfabbb90b1dbfc506d"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8603050e547249c1cc8a8dc6a49917076572ea69b04bc51eb1748c403cfc9f46"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-win32.whl", hash = "sha256:77bdb96e82d8831f0dd6db83e2ff0d4a731cff53e926d029c65a1dc3ae0f160a"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:09f354fa28e0fd170c6e4eea5e97eea0dba43761067df93109f49a5414ca8584"}, - {file = "rapidfuzz-3.9.2-cp312-cp312-win_arm64.whl", hash = "sha256:168299c9a2b4f20f10c1bb96d8da0bb05bf1f3b9957be3a0bae5db65ce9f095f"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d87621d60078f87cb52082b1cbf9849afeaa1cb6d0a2b072fce25fe21c8675b4"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c447d0e534418ef3eaabcd890d85c7e9f289c1c6ef6e060a0b1f239799781747"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7161b205f25eff5f88ab809fb05a2a102634e06f452c0deb9535c9f41cd7b0a"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f13a6bbadba8fdd42676c1213ebc692bba9fac00f7db0ae92acc06bb734294c4"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54534743820a15bd0dc30a0a0010825be337973236550fd63587700a7950bbca"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea61851a4c2f93148aa2779458fb3f70a62342d77c9ec3d9d08445c8485b738"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e941f81a60351a842976fea208e6a6701a5899eb8a80b907e57d7c3099337900"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1bbfaf439e48efe3a48cada946cf7678b09c818ce9668e09dac40d05b772f6f8"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:574f464da18d660712e9776072572d462cf6a26144c833d18d9c93778286e023"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8a56c494246d29aacf5ac93ca3cf338d79588a1a5c05d8f496c3f4d7127e9031"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:2943b0f17195c000948a7668bb11979ea0e50079a3d3db9d139e51b68c3a7c26"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:27214f93555d4f9b7b1baf107a6ba13e9daee21f1ec6e36418556d04a7ee4d9b"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-win32.whl", hash = "sha256:876c6628fec6241262c27f8fda3c73bab88e205e9b9394c8868361e2eda59048"}, - {file = "rapidfuzz-3.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf1952b486589ffcfbde2015ca9be15e0f4b0e63d1e2c25f3daec0263fda4e69"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1ca9a135060ee4d887d6af86493c3e0eb1c99ca205bca943fe5994dc93e648d5"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:723518c9a18e8bda996d77aa9307b6f8b0e77905702b2772b020adf24191073a"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65eb9aeae73ac60e53a9d6c509daaa217ea256a5e184eb8920c9b15295c48677"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef2964f4eb9a37487c96e5e32167a3c4fa51bf8e899853d0ac67e0465a27702c"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c64a252c96f29667c206726903bb9705c5195f01850360c9b9268de92ac878dc"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b32b03398517b5e33c7f36d625a00fcb1c955b9fe3c939325688175fb21730"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec5f7b1bac77439b624f5acbd8bfe61e7b833678701068b43f7a489c151427c0"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5fd1b49fba8b4b9172eed5b131c1e9864d4d76bebea34359274f16a3591e5f44"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c05b033fc3ff043f48e744f67038af7fd34003047c7810f24bec7c01ce7da05b"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c3bea20db89b510d78d017b349b9d87159c32418693ddf091d9035dbe20b4dc0"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:77226a77590f83ee073f4f8cc86a1232da88e24d19d349361faa169fb17ba1cd"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:83ed8bc2c942dc61ab739bbca1ead791143b4639dc92156d3060bd0b6f4541ea"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-win32.whl", hash = "sha256:2db70f64974c10b76ae37d5cff6124dce791def815d4fdf5ac16fe60be88d905"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:bdead23114206dea4a22ed3aad6565b99a9e4b3fff9837c423afc556d2814b1a"}, - {file = "rapidfuzz-3.9.2-cp39-cp39-win_arm64.whl", hash = "sha256:0ec69ad076cfc7c88323d671613e40bb8754ba95a203556d9a7759e60f0544e8"}, - {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:018360654881e75131b227aa96cdaba543c438da881c70a12ca0c86e2c4083b2"}, - {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:eaa8178ec9238f32f15b6e49f70b852accda0a848448c4e30bce77c6624ebaba"}, - {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32dd79b0f90ce609df96d0d48ef4327cf1f0415b9274588a466d3610a775d2f9"}, - {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04a1c38a72a50f3e6d346a33d53fa51ba390552b3592fca64a07e54d749b439b"}, - {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77ca96eec40e815f0cf10b00008f295fd26ca43792a844cf62588a8ea614e160"}, - {file = "rapidfuzz-3.9.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c01c515a928f295f49d588b6523f44b474f047f9f2de0079bc57bcd00b870778"}, - {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:07e14ef260b6f4ee03dff07a0ac95a16aff1ddbc7e6171e07e49d2d61526f3be"}, - {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:64f3480bddc12b89969930f12a50a1aeb53e09aad41cf8b27694d83ca1cc7864"}, - {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3c9e33ec21755bda1878095537cb84848e9cf6510d4837d22144ba04e33df29"}, - {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a70045e84225697ddf67d656aa25b70d6802e2ff339d51f9545fca5b9b13fb8c"}, - {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9ec1fd328518c33adb9171afe8735137cb7b492e4a81cddc23568f9980c235c"}, - {file = "rapidfuzz-3.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:1fd8458fdac232766d55593c1228c70968f382fdc376c25685273f99b5d1d921"}, - {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a373748fddb5403b562b6d682082de360bb08395f44e3cb7e74819461e39a16c"}, - {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:45f80856db3e22cb5f96ad1572aa1d004714514625ed4668144661d8a7c7e61f"}, - {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:663e52cf878e0ccbbad0744eb3e2bb83a784645b146f15611bac225bc218f19b"}, - {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbe4d3034a8cfe59a2b477375ad7d739b3e5935f10af08abdf64aae55780cad"}, - {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd38abfda97e42b30093f207108dcba944beab1edf6624ba757cf57354063177"}, - {file = "rapidfuzz-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:16b41fe360387283a3184ce72d4d26d1928e7ce809268a88e8491a776dd770af"}, - {file = "rapidfuzz-3.9.2.tar.gz", hash = "sha256:c899d78709f8d4bd0059784fa27a9f6c53d04fc4aeaa21de7c0c8e34a7154e88"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ccf68e30b80e903f2309f90a438dbd640dd98e878eeb5ad361a288051ee5b75c"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:696a79018ef989bf1c9abd9005841cee18005ccad4748bad8a4c274c47b6241a"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4eebf6c93af0ae866c22b403a84747580bb5c10f0d7b51c82a87f25405d4dcb"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e9125377fa3d21a8abd4fbdbcf1c27be73e8b1850f0b61b5b711364bf3b59db"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c12d180b17a22d107c8747de9c68d0b9c1d15dcda5445ff9bf9f4ccfb67c3e16"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1318d42610c26dcd68bd3279a1bf9e3605377260867c9a8ed22eafc1bd93a7c"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd5fa6e3c6e0333051c1f3a49f0807b3366f4131c8d6ac8c3e05fd0d0ce3755c"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fcf79b686962d7bec458a0babc904cb4fa319808805e036b9d5a531ee6b9b835"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8b01153c7466d0bad48fba77a303d5a768e66f24b763853469f47220b3de4661"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:94baaeea0b4f8632a6da69348b1e741043eba18d4e3088d674d3f76586b6223d"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6c5b32875646cb7f60c193ade99b2e4b124f19583492115293cd00f6fb198b17"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:110b6294396bc0a447648627479c9320f095c2034c0537f687592e0f58622638"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-win32.whl", hash = "sha256:3445a35c4c8d288f2b2011eb61bce1227c633ce85a3154e727170f37c0266bb2"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-win_amd64.whl", hash = "sha256:0d1415a732ee75e74a90af12020b77a0b396b36c60afae1bde3208a78cd2c9fc"}, + {file = "rapidfuzz-3.9.7-cp310-cp310-win_arm64.whl", hash = "sha256:836f4d88b8bd0fff2ebe815dcaab8aa6c8d07d1d566a7e21dd137cf6fe11ed5b"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d098ce6162eb5e48fceb0745455bc950af059df6113eec83e916c129fca11408"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:048d55d36c02c6685a2b2741688503c3d15149694506655b6169dcfd3b6c2585"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c33211cfff9aec425bb1bfedaf94afcf337063aa273754f22779d6dadebef4c2"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6d9db2fa4e9be171e9bb31cf2d2575574774966b43f5b951062bb2e67885852"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4e049d5ad61448c9a020d1061eba20944c4887d720c4069724beb6ea1692507"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cfa74aac64c85898b93d9c80bb935a96bf64985e28d4ee0f1a3d1f3bf11a5106"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:965693c2e9efd425b0f059f5be50ef830129f82892fa1858e220e424d9d0160f"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8501000a5eb8037c4b56857724797fe5a8b01853c363de91c8d0d0ad56bef319"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d92c552c6b7577402afdd547dcf5d31ea6c8ae31ad03f78226e055cfa37f3c6"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1ee2086f490cb501d86b7e386c1eb4e3a0ccbb0c99067089efaa8c79012c8952"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1de91e7fd7f525e10ea79a6e62c559d1b0278ec097ad83d9da378b6fab65a265"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4da514d13f4433e16960a17f05b67e0af30ac771719c9a9fb877e5004f74477"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-win32.whl", hash = "sha256:a40184c67db8252593ec518e17fb8a6e86d7259dc9f2d6c0bf4ff4db8cf1ad4b"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-win_amd64.whl", hash = "sha256:c4f28f1930b09a2c300357d8465b388cecb7e8b2f454a5d5425561710b7fd07f"}, + {file = "rapidfuzz-3.9.7-cp311-cp311-win_arm64.whl", hash = "sha256:675b75412a943bb83f1f53e2e54fd18c80ef15ed642dc6eb0382d1949419d904"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1ef6a1a8f0b12f8722f595f15c62950c9a02d5abc64742561299ffd49f6c6944"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:32532af1d70c6ec02ea5ac7ee2766dfff7c8ae8c761abfe8da9e527314e634e8"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1a38bade755aa9dd95a81cda949e1bf9cd92b79341ccc5e2189c9e7bdfc5ec"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d73ee2df41224c87336448d279b5b6a3a75f36e41dd3dcf538c0c9cce36360d8"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be3a1fc3e2ab3bdf93dc0c83c00acca8afd2a80602297d96cf4a0ba028333cdf"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:603f48f621272a448ff58bb556feb4371252a02156593303391f5c3281dfaeac"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:268f8e1ca50fc61c0736f3fe9d47891424adf62d96ed30196f30f4bd8216b41f"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f8bf3f0d02935751d8660abda6044821a861f6229f7d359f98bcdcc7e66c39b"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b997ff3b39d4cee9fb025d6c46b0a24bd67595ce5a5b652a97fb3a9d60beb651"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca66676c8ef6557f9b81c5b2b519097817a7c776a6599b8d6fcc3e16edd216fe"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:35d3044cb635ca6b1b2b7b67b3597bd19f34f1753b129eb6d2ae04cf98cd3945"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a93c9e60904cb76e7aefef67afffb8b37c4894f81415ed513db090f29d01101"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-win32.whl", hash = "sha256:579d107102c0725f7c79b4e79f16d3cf4d7c9208f29c66b064fa1fd4641d5155"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-win_amd64.whl", hash = "sha256:953b3780765c8846866faf891ee4290f6a41a6dacf4fbcd3926f78c9de412ca6"}, + {file = "rapidfuzz-3.9.7-cp312-cp312-win_arm64.whl", hash = "sha256:7c20c1474b068c4bd45bf2fd0ad548df284f74e9a14a68b06746c56e3aa8eb70"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fde81b1da9a947f931711febe2e2bee694e891f6d3e6aa6bc02c1884702aea19"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47e92c155a14f44511ea8ebcc6bc1535a1fe8d0a7d67ad3cc47ba61606df7bcf"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8772b745668260c5c4d069c678bbaa68812e6c69830f3771eaad521af7bc17f8"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:578302828dd97ee2ba507d2f71d62164e28d2fc7bc73aad0d2d1d2afc021a5d5"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc3e6081069eea61593f1d6839029da53d00c8c9b205c5534853eaa3f031085c"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b1c2d504eddf97bc0f2eba422c8915576dbf025062ceaca2d68aecd66324ad9"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb76e5a21034f0307c51c5a2fc08856f698c53a4c593b17d291f7d6e9d09ca3"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d4ba2318ef670ce505f42881a5d2af70f948124646947341a3c6ccb33cd70369"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:057bb03f39e285047d7e9412e01ecf31bb2d42b9466a5409d715d587460dd59b"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a8feac9006d5c9758438906f093befffc4290de75663dbb2098461df7c7d28dd"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:95b8292383e717e10455f2c917df45032b611141e43d1adf70f71b1566136b11"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e9fbf659537d246086d0297628b3795dc3e4a384101ecc01e5791c827b8d7345"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-win32.whl", hash = "sha256:1dc516ac6d32027be2b0196bedf6d977ac26debd09ca182376322ad620460feb"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-win_amd64.whl", hash = "sha256:b4f86e09d3064dca0b014cd48688964036a904a2d28048f00c8f4640796d06a8"}, + {file = "rapidfuzz-3.9.7-cp313-cp313-win_arm64.whl", hash = "sha256:19c64d8ddb2940b42a4567b23f1681af77f50a5ff6c9b8e85daba079c210716e"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbda3dd68d8b28ccb20ffb6f756fefd9b5ba570a772bedd7643ed441f5793308"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2379e0b2578ad3ac7004f223251550f08bca873ff76c169b09410ec562ad78d8"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d1eff95362f993b0276fd3839aee48625b09aac8938bb0c23b40d219cba5dc5"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd9360e30041690912525a210e48a897b49b230768cc8af1c702e5395690464f"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a93cd834b3c315ab437f0565ee3a2f42dd33768dc885ccbabf9710b131cf70d2"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff196996240db7075f62c7bc4506f40a3c80cd4ae3ab0e79ac6892283a90859"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948dcee7aaa1cd14358b2a7ef08bf0be42bf89049c3a906669874a715fc2c937"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95751f505a301af1aaf086c19f34536056d6c8efa91b2240de532a3db57b543"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:90db86fa196eecf96cb6db09f1083912ea945c50c57188039392d810d0b784e1"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:3171653212218a162540a3c8eb8ae7d3dcc8548540b69eaecaf3b47c14d89c90"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:36dd6e820379c37a1ffefc8a52b648758e867cd9d78ee5b5dc0c9a6a10145378"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7b702de95666a1f7d5c6b47eacadfe2d2794af3742d63d2134767d13e5d1c713"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-win32.whl", hash = "sha256:9030e7238c0df51aed5c9c5ed8eee2bdd47a2ae788e562c1454af2851c3d1906"}, + {file = "rapidfuzz-3.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:f847fb0fbfb72482b1c05c59cbb275c58a55b73708a7f77a83f8035ee3c86497"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:97f2ce529d2a70a60c290f6ab269a2bbf1d3b47b9724dccc84339b85f7afb044"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e2957fdad10bb83b1982b02deb3604a3f6911a5e545f518b59c741086f92d152"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d5262383634626eb45c536017204b8163a03bc43bda880cf1bdd7885db9a163"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:364587827d7cbd41afa0782adc2d2d19e3f07d355b0750a02a8e33ad27a9c368"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecc24af7f905f3d6efb371a01680116ffea8d64e266618fb9ad1602a9b4f7934"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dc86aa6b29d174713c5f4caac35ffb7f232e3e649113e8d13812b35ab078228"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3dcfbe7266e74a707173a12a7b355a531f2dcfbdb32f09468e664330da14874"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b23806fbdd6b510ba9ac93bb72d503066263b0fba44b71b835be9f063a84025f"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5551d68264c1bb6943f542da83a4dc8940ede52c5847ef158698799cc28d14f5"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:13d8675a1fa7e2b19650ca7ef9a6ec01391d4bb12ab9e0793e8eb024538b4a34"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9b6a5de507b9be6de688dae40143b656f7a93b10995fb8bd90deb555e7875c60"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:111a20a3c090cf244d9406e60500b6c34b2375ba3a5009e2b38fd806fe38e337"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-win32.whl", hash = "sha256:22589c0b8ccc6c391ce7f776c93a8c92c96ab8d34e1a19f1bd2b12a235332632"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-win_amd64.whl", hash = "sha256:6f83221db5755b8f34222e40607d87f1176a8d5d4dbda4a55a0f0b67d588a69c"}, + {file = "rapidfuzz-3.9.7-cp39-cp39-win_arm64.whl", hash = "sha256:3665b92e788578c3bb334bd5b5fa7ee1a84bafd68be438e3110861d1578c63a0"}, + {file = "rapidfuzz-3.9.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7df9c2194c7ec930b33c991c55dbd0c10951bd25800c0b7a7b571994ebbced5"}, + {file = "rapidfuzz-3.9.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:68bd888eafd07b09585dcc8bc2716c5ecdb7eed62827470664d25588982b2873"}, + {file = "rapidfuzz-3.9.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1230e0f9026851a6a432beaa0ce575dda7b39fe689b576f99a0704fbb81fc9c"}, + {file = "rapidfuzz-3.9.7-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3b36e1c61b796ae1777f3e9e11fd39898b09d351c9384baf6e3b7e6191d8ced"}, + {file = "rapidfuzz-3.9.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dba13d86806fcf3fe9c9919f58575e0090eadfb89c058bde02bcc7ab24e4548"}, + {file = "rapidfuzz-3.9.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1f1a33e84056b7892c721d84475d3bde49a145126bc4c6efe0d6d0d59cb31c29"}, + {file = "rapidfuzz-3.9.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3492c7a42b7fa9f0051d7fcce9893e95ed91c97c9ec7fb64346f3e070dd318ed"}, + {file = "rapidfuzz-3.9.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:ece45eb2af8b00f90d10f7419322e8804bd42fb1129026f9bfe712c37508b514"}, + {file = "rapidfuzz-3.9.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcd14cf4876f04b488f6e54a7abd3e9b31db5f5a6aba0ce90659917aaa8c088"}, + {file = "rapidfuzz-3.9.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:521c58c72ed8a612b25cda378ff10dee17e6deb4ee99a070b723519a345527b9"}, + {file = "rapidfuzz-3.9.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18669bb6cdf7d40738526d37e550df09ba065b5a7560f3d802287988b6cb63cf"}, + {file = "rapidfuzz-3.9.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7abe2dbae81120a64bb4f8d3fcafe9122f328c9f86d7f327f174187a5af4ed86"}, + {file = "rapidfuzz-3.9.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a3c0783910911f4f24655826d007c9f4360f08107410952c01ee3df98c713eb2"}, + {file = "rapidfuzz-3.9.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:03126f9a040ff21d2a110610bfd6b93b79377ce8b4121edcb791d61b7df6eec5"}, + {file = "rapidfuzz-3.9.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:591908240f4085e2ade5b685c6e8346e2ed44932cffeaac2fb32ddac95b55c7f"}, + {file = "rapidfuzz-3.9.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9012d86c6397edbc9da4ac0132de7f8ee9d6ce857f4194d5684c4ddbcdd1c5c"}, + {file = "rapidfuzz-3.9.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df596ddd3db38aa513d4c0995611267b3946e7cbe5a8761b50e9306dfec720ee"}, + {file = "rapidfuzz-3.9.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3ed5adb752f4308fcc8f4fb6f8eb7aa4082f9d12676fda0a74fa5564242a8107"}, + {file = "rapidfuzz-3.9.7.tar.gz", hash = "sha256:f1c7296534c1afb6f495aa95871f14ccdc197c6db42965854e483100df313030"}, ] [package.extras] @@ -1407,13 +1462,13 @@ rpds-py = ">=0.7.0" [[package]] name = "requests" -version = "2.32.2" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, - {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1428,114 +1483,114 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rpds-py" -version = "0.19.1" +version = "0.20.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491"}, - {file = "rpds_py-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a"}, - {file = "rpds_py-0.19.1-cp310-none-win32.whl", hash = "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866"}, - {file = "rpds_py-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a"}, - {file = "rpds_py-0.19.1-cp311-none-win32.whl", hash = "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd"}, - {file = "rpds_py-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c"}, - {file = "rpds_py-0.19.1-cp312-none-win32.whl", hash = "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace"}, - {file = "rpds_py-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859"}, - {file = "rpds_py-0.19.1-cp313-none-win32.whl", hash = "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309"}, - {file = "rpds_py-0.19.1-cp313-none-win_amd64.whl", hash = "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec"}, - {file = "rpds_py-0.19.1-cp38-none-win32.whl", hash = "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14"}, - {file = "rpds_py-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f"}, - {file = "rpds_py-0.19.1-cp39-none-win32.whl", hash = "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f"}, - {file = "rpds_py-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c"}, - {file = "rpds_py-0.19.1.tar.gz", hash = "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, ] [[package]] @@ -1606,111 +1661,111 @@ rapidfuzz = ">=3.0.0,<4.0.0" [[package]] name = "tokenizers" -version = "0.19.1" +version = "0.20.0" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "tokenizers-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:952078130b3d101e05ecfc7fc3640282d74ed26bcf691400f872563fca15ac97"}, - {file = "tokenizers-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82c8b8063de6c0468f08e82c4e198763e7b97aabfe573fd4cf7b33930ca4df77"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f03727225feaf340ceeb7e00604825addef622d551cbd46b7b775ac834c1e1c4"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:453e4422efdfc9c6b6bf2eae00d5e323f263fff62b29a8c9cd526c5003f3f642"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02e81bf089ebf0e7f4df34fa0207519f07e66d8491d963618252f2e0729e0b46"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b07c538ba956843833fee1190cf769c60dc62e1cf934ed50d77d5502194d63b1"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28cab1582e0eec38b1f38c1c1fb2e56bce5dc180acb1724574fc5f47da2a4fe"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b01afb7193d47439f091cd8f070a1ced347ad0f9144952a30a41836902fe09e"}, - {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7fb297edec6c6841ab2e4e8f357209519188e4a59b557ea4fafcf4691d1b4c98"}, - {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e8a3dd055e515df7054378dc9d6fa8c8c34e1f32777fb9a01fea81496b3f9d3"}, - {file = "tokenizers-0.19.1-cp310-none-win32.whl", hash = "sha256:7ff898780a155ea053f5d934925f3902be2ed1f4d916461e1a93019cc7250837"}, - {file = "tokenizers-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:bea6f9947e9419c2fda21ae6c32871e3d398cba549b93f4a65a2d369662d9403"}, - {file = "tokenizers-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5c88d1481f1882c2e53e6bb06491e474e420d9ac7bdff172610c4f9ad3898059"}, - {file = "tokenizers-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddf672ed719b4ed82b51499100f5417d7d9f6fb05a65e232249268f35de5ed14"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dadc509cc8a9fe460bd274c0e16ac4184d0958117cf026e0ea8b32b438171594"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfedf31824ca4915b511b03441784ff640378191918264268e6923da48104acc"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac11016d0a04aa6487b1513a3a36e7bee7eec0e5d30057c9c0408067345c48d2"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76951121890fea8330d3a0df9a954b3f2a37e3ec20e5b0530e9a0044ca2e11fe"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b342d2ce8fc8d00f376af068e3274e2e8649562e3bc6ae4a67784ded6b99428d"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16ff18907f4909dca9b076b9c2d899114dd6abceeb074eca0c93e2353f943aa"}, - {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:706a37cc5332f85f26efbe2bdc9ef8a9b372b77e4645331a405073e4b3a8c1c6"}, - {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16baac68651701364b0289979ecec728546133e8e8fe38f66fe48ad07996b88b"}, - {file = "tokenizers-0.19.1-cp311-none-win32.whl", hash = "sha256:9ed240c56b4403e22b9584ee37d87b8bfa14865134e3e1c3fb4b2c42fafd3256"}, - {file = "tokenizers-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:ad57d59341710b94a7d9dbea13f5c1e7d76fd8d9bcd944a7a6ab0b0da6e0cc66"}, - {file = "tokenizers-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:621d670e1b1c281a1c9698ed89451395d318802ff88d1fc1accff0867a06f153"}, - {file = "tokenizers-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d924204a3dbe50b75630bd16f821ebda6a5f729928df30f582fb5aade90c818a"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f3fefdc0446b1a1e6d81cd4c07088ac015665d2e812f6dbba4a06267d1a2c95"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9620b78e0b2d52ef07b0d428323fb34e8ea1219c5eac98c2596311f20f1f9266"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04ce49e82d100594715ac1b2ce87d1a36e61891a91de774755f743babcd0dd52"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5c2ff13d157afe413bf7e25789879dd463e5a4abfb529a2d8f8473d8042e28f"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3174c76efd9d08f836bfccaca7cfec3f4d1c0a4cf3acbc7236ad577cc423c840"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d5b6c0e7a1e979bec10ff960fae925e947aab95619a6fdb4c1d8ff3708ce3"}, - {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a179856d1caee06577220ebcfa332af046d576fb73454b8f4d4b0ba8324423ea"}, - {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:952b80dac1a6492170f8c2429bd11fcaa14377e097d12a1dbe0ef2fb2241e16c"}, - {file = "tokenizers-0.19.1-cp312-none-win32.whl", hash = "sha256:01d62812454c188306755c94755465505836fd616f75067abcae529c35edeb57"}, - {file = "tokenizers-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:b70bfbe3a82d3e3fb2a5e9b22a39f8d1740c96c68b6ace0086b39074f08ab89a"}, - {file = "tokenizers-0.19.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:bb9dfe7dae85bc6119d705a76dc068c062b8b575abe3595e3c6276480e67e3f1"}, - {file = "tokenizers-0.19.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:1f0360cbea28ea99944ac089c00de7b2e3e1c58f479fb8613b6d8d511ce98267"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:71e3ec71f0e78780851fef28c2a9babe20270404c921b756d7c532d280349214"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b82931fa619dbad979c0ee8e54dd5278acc418209cc897e42fac041f5366d626"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ff5b90eabdcdaa19af697885f70fe0b714ce16709cf43d4952f1f85299e73a"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e742d76ad84acbdb1a8e4694f915fe59ff6edc381c97d6dfdd054954e3478ad4"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8c5d59d7b59885eab559d5bc082b2985555a54cda04dda4c65528d90ad252ad"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b2da5c32ed869bebd990c9420df49813709e953674c0722ff471a116d97b22d"}, - {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:638e43936cc8b2cbb9f9d8dde0fe5e7e30766a3318d2342999ae27f68fdc9bd6"}, - {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:78e769eb3b2c79687d9cb0f89ef77223e8e279b75c0a968e637ca7043a84463f"}, - {file = "tokenizers-0.19.1-cp37-none-win32.whl", hash = "sha256:72791f9bb1ca78e3ae525d4782e85272c63faaef9940d92142aa3eb79f3407a3"}, - {file = "tokenizers-0.19.1-cp37-none-win_amd64.whl", hash = "sha256:f3bbb7a0c5fcb692950b041ae11067ac54826204318922da754f908d95619fbc"}, - {file = "tokenizers-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:07f9295349bbbcedae8cefdbcfa7f686aa420be8aca5d4f7d1ae6016c128c0c5"}, - {file = "tokenizers-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10a707cc6c4b6b183ec5dbfc5c34f3064e18cf62b4a938cb41699e33a99e03c1"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6309271f57b397aa0aff0cbbe632ca9d70430839ca3178bf0f06f825924eca22"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad23d37d68cf00d54af184586d79b84075ada495e7c5c0f601f051b162112dc"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:427c4f0f3df9109314d4f75b8d1f65d9477033e67ffaec4bca53293d3aca286d"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e83a31c9cf181a0a3ef0abad2b5f6b43399faf5da7e696196ddd110d332519ee"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c27b99889bd58b7e301468c0838c5ed75e60c66df0d4db80c08f43462f82e0d3"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bac0b0eb952412b0b196ca7a40e7dce4ed6f6926489313414010f2e6b9ec2adf"}, - {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8a6298bde623725ca31c9035a04bf2ef63208d266acd2bed8c2cb7d2b7d53ce6"}, - {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:08a44864e42fa6d7d76d7be4bec62c9982f6f6248b4aa42f7302aa01e0abfd26"}, - {file = "tokenizers-0.19.1-cp38-none-win32.whl", hash = "sha256:1de5bc8652252d9357a666e609cb1453d4f8e160eb1fb2830ee369dd658e8975"}, - {file = "tokenizers-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:0bcce02bf1ad9882345b34d5bd25ed4949a480cf0e656bbd468f4d8986f7a3f1"}, - {file = "tokenizers-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0b9394bd204842a2a1fd37fe29935353742be4a3460b6ccbaefa93f58a8df43d"}, - {file = "tokenizers-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4692ab92f91b87769d950ca14dbb61f8a9ef36a62f94bad6c82cc84a51f76f6a"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6258c2ef6f06259f70a682491c78561d492e885adeaf9f64f5389f78aa49a051"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85cf76561fbd01e0d9ea2d1cbe711a65400092bc52b5242b16cfd22e51f0c58"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670b802d4d82bbbb832ddb0d41df7015b3e549714c0e77f9bed3e74d42400fbe"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85aa3ab4b03d5e99fdd31660872249df5e855334b6c333e0bc13032ff4469c4a"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf001afbbed111a79ca47d75941e9e5361297a87d186cbfc11ed45e30b5daba"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c89aa46c269e4e70c4d4f9d6bc644fcc39bb409cb2a81227923404dd6f5227"}, - {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:39c1ec76ea1027438fafe16ecb0fb84795e62e9d643444c1090179e63808c69d"}, - {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c2a0d47a89b48d7daa241e004e71fb5a50533718897a4cd6235cb846d511a478"}, - {file = "tokenizers-0.19.1-cp39-none-win32.whl", hash = "sha256:61b7fe8886f2e104d4caf9218b157b106207e0f2a4905c9c7ac98890688aabeb"}, - {file = "tokenizers-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:f97660f6c43efd3e0bfd3f2e3e5615bf215680bad6ee3d469df6454b8c6e8256"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b11853f17b54c2fe47742c56d8a33bf49ce31caf531e87ac0d7d13d327c9334"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d26194ef6c13302f446d39972aaa36a1dda6450bc8949f5eb4c27f51191375bd"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e8d1ed93beda54bbd6131a2cb363a576eac746d5c26ba5b7556bc6f964425594"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca407133536f19bdec44b3da117ef0d12e43f6d4b56ac4c765f37eca501c7bda"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce05fde79d2bc2e46ac08aacbc142bead21614d937aac950be88dc79f9db9022"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:35583cd46d16f07c054efd18b5d46af4a2f070a2dd0a47914e66f3ff5efb2b1e"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:43350270bfc16b06ad3f6f07eab21f089adb835544417afda0f83256a8bf8b75"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b4399b59d1af5645bcee2072a463318114c39b8547437a7c2d6a186a1b5a0e2d"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6852c5b2a853b8b0ddc5993cd4f33bfffdca4fcc5d52f89dd4b8eada99379285"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd266ae85c3d39df2f7e7d0e07f6c41a55e9a3123bb11f854412952deacd828"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecb2651956eea2aa0a2d099434134b1b68f1c31f9a5084d6d53f08ed43d45ff2"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b279ab506ec4445166ac476fb4d3cc383accde1ea152998509a94d82547c8e2a"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89183e55fb86e61d848ff83753f64cded119f5d6e1f553d14ffee3700d0a4a49"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2edbc75744235eea94d595a8b70fe279dd42f3296f76d5a86dde1d46e35f574"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0e64bfde9a723274e9a71630c3e9494ed7b4c0f76a1faacf7fe294cd26f7ae7c"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b5ca92bfa717759c052e345770792d02d1f43b06f9e790ca0a1db62838816f3"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f8a20266e695ec9d7a946a019c1d5ca4eddb6613d4f466888eee04f16eedb85"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63c38f45d8f2a2ec0f3a20073cccb335b9f99f73b3c69483cd52ebc75369d8a1"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dd26e3afe8a7b61422df3176e06664503d3f5973b94f45d5c45987e1cb711876"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:eddd5783a4a6309ce23432353cdb36220e25cbb779bfa9122320666508b44b88"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:56ae39d4036b753994476a1b935584071093b55c7a72e3b8288e68c313ca26e7"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9939ca7e58c2758c01b40324a59c034ce0cebad18e0d4563a9b1beab3018243"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c330c0eb815d212893c67a032e9dc1b38a803eccb32f3e8172c19cc69fbb439"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec11802450a2487cdf0e634b750a04cbdc1c4d066b97d94ce7dd2cb51ebb325b"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b718f316b596f36e1dae097a7d5b91fc5b85e90bf08b01ff139bd8953b25af"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ed69af290c2b65169f0ba9034d1dc39a5db9459b32f1dd8b5f3f32a3fcf06eab"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f8a9c828277133af13f3859d1b6bf1c3cb6e9e1637df0e45312e6b7c2e622b1f"}, - {file = "tokenizers-0.19.1.tar.gz", hash = "sha256:ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3"}, + {file = "tokenizers-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6cff5c5e37c41bc5faa519d6f3df0679e4b37da54ea1f42121719c5e2b4905c0"}, + {file = "tokenizers-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:62a56bf75c27443432456f4ca5ca055befa95e25be8a28141cc495cac8ae4d6d"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68cc7de6a63f09c4a86909c2597b995aa66e19df852a23aea894929c74369929"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:053c37ecee482cc958fdee53af3c6534286a86f5d35aac476f7c246830e53ae5"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d7074aaabc151a6363fa03db5493fc95b423b2a1874456783989e96d541c7b6"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a11435780f2acd89e8fefe5e81cecf01776f6edb9b3ac95bcb76baee76b30b90"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a81cd2712973b007d84268d45fc3f6f90a79c31dfe7f1925e6732f8d2959987"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7dfd796ab9d909f76fb93080e1c7c8309f196ecb316eb130718cd5e34231c69"}, + {file = "tokenizers-0.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8029ad2aa8cb00605c9374566034c1cc1b15130713e0eb5afcef6cface8255c9"}, + {file = "tokenizers-0.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ca4d54260ebe97d59dfa9a30baa20d0c4dd9137d99a8801700055c561145c24e"}, + {file = "tokenizers-0.20.0-cp310-none-win32.whl", hash = "sha256:95ee16b57cec11b86a7940174ec5197d506439b0f415ab3859f254b1dffe9df0"}, + {file = "tokenizers-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:0a61a11e93eeadbf02aea082ffc75241c4198e0608bbbac4f65a9026851dcf37"}, + {file = "tokenizers-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6636b798b3c4d6c9b1af1a918bd07c867808e5a21c64324e95318a237e6366c3"}, + {file = "tokenizers-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ec603e42eaf499ffd58b9258162add948717cf21372458132f14e13a6bc7172"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cce124264903a8ea6f8f48e1cc7669e5ef638c18bd4ab0a88769d5f92debdf7f"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07bbeba0231cf8de07aa6b9e33e9779ff103d47042eeeb859a8c432e3292fb98"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06c0ca8397b35d38b83a44a9c6929790c1692957d88541df061cb34d82ebbf08"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca6557ac3b83d912dfbb1f70ab56bd4b0594043916688e906ede09f42e192401"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a5ad94c9e80ac6098328bee2e3264dbced4c6faa34429994d473f795ec58ef4"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5c7f906ee6bec30a9dc20268a8b80f3b9584de1c9f051671cb057dc6ce28f6"}, + {file = "tokenizers-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:31e087e9ee1b8f075b002bfee257e858dc695f955b43903e1bb4aa9f170e37fe"}, + {file = "tokenizers-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3124fb6f3346cb3d8d775375d3b429bf4dcfc24f739822702009d20a4297990"}, + {file = "tokenizers-0.20.0-cp311-none-win32.whl", hash = "sha256:a4bb8b40ba9eefa621fdcabf04a74aa6038ae3be0c614c6458bd91a4697a452f"}, + {file = "tokenizers-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:2b709d371f1fe60a28ef0c5c67815952d455ca7f34dbe7197eaaed3cc54b658e"}, + {file = "tokenizers-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:15c81a17d0d66f4987c6ca16f4bea7ec253b8c7ed1bb00fdc5d038b1bb56e714"}, + {file = "tokenizers-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a531cdf1fb6dc41c984c785a3b299cb0586de0b35683842a3afbb1e5207f910"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06caabeb4587f8404e0cd9d40f458e9cba3e815c8155a38e579a74ff3e2a4301"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8768f964f23f5b9f50546c0369c75ab3262de926983888bbe8b98be05392a79c"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:626403860152c816f97b649fd279bd622c3d417678c93b4b1a8909b6380b69a8"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c1b88fa9e5ff062326f4bf82681da5a96fca7104d921a6bd7b1e6fcf224af26"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7e559436a07dc547f22ce1101f26d8b2fad387e28ec8e7e1e3b11695d681d8"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48afb75e50449848964e4a67b0da01261dd3aa8df8daecf10db8fd7f5b076eb"}, + {file = "tokenizers-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:baf5d0e1ff44710a95eefc196dd87666ffc609fd447c5e5b68272a7c3d342a1d"}, + {file = "tokenizers-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e5e56df0e8ed23ba60ae3848c3f069a0710c4b197218fe4f89e27eba38510768"}, + {file = "tokenizers-0.20.0-cp312-none-win32.whl", hash = "sha256:ec53e5ecc142a82432f9c6c677dbbe5a2bfee92b8abf409a9ecb0d425ee0ce75"}, + {file = "tokenizers-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:f18661ece72e39c0dfaa174d6223248a15b457dbd4b0fc07809b8e6d3ca1a234"}, + {file = "tokenizers-0.20.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:f7065b1084d8d1a03dc89d9aad69bcbc8415d4bc123c367063eb32958cd85054"}, + {file = "tokenizers-0.20.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e5d4069e4714e3f7ba0a4d3d44f9d84a432cd4e4aa85c3d7dd1f51440f12e4a1"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:799b808529e54b7e1a36350bda2aeb470e8390e484d3e98c10395cee61d4e3c6"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f9baa027cc8a281ad5f7725a93c204d7a46986f88edbe8ef7357f40a23fb9c7"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:010ec7f3f7a96adc4c2a34a3ada41fa14b4b936b5628b4ff7b33791258646c6b"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98d88f06155335b14fd78e32ee28ca5b2eb30fced4614e06eb14ae5f7fba24ed"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e13eb000ef540c2280758d1b9cfa5fe424b0424ae4458f440e6340a4f18b2638"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fab3cf066ff426f7e6d70435dc28a9ff01b2747be83810e397cba106f39430b0"}, + {file = "tokenizers-0.20.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:39fa3761b30a89368f322e5daf4130dce8495b79ad831f370449cdacfb0c0d37"}, + {file = "tokenizers-0.20.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c8da0fba4d179ddf2607821575998df3c294aa59aa8df5a6646dc64bc7352bce"}, + {file = "tokenizers-0.20.0-cp37-none-win32.whl", hash = "sha256:fada996d6da8cf213f6e3c91c12297ad4f6cdf7a85c2fadcd05ec32fa6846fcd"}, + {file = "tokenizers-0.20.0-cp37-none-win_amd64.whl", hash = "sha256:7d29aad702279e0760c265fcae832e89349078e3418dd329732d4503259fd6bd"}, + {file = "tokenizers-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:099c68207f3ef0227ecb6f80ab98ea74de559f7b124adc7b17778af0250ee90a"}, + {file = "tokenizers-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:68012d8a8cddb2eab3880870d7e2086cb359c7f7a2b03f5795044f5abff4e850"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9253bdd209c6aee168deca7d0e780581bf303e0058f268f9bb06859379de19b6"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f868600ddbcb0545905ed075eb7218a0756bf6c09dae7528ea2f8436ebd2c93"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9643d9c8c5f99b6aba43fd10034f77cc6c22c31f496d2f0ee183047d948fa0"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c375c6a889aeab44734028bc65cc070acf93ccb0f9368be42b67a98e1063d3f6"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e359f852328e254f070bbd09a19a568421d23388f04aad9f2fb7da7704c7228d"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d98b01a309d4387f3b1c1dd68a8b8136af50376cf146c1b7e8d8ead217a5be4b"}, + {file = "tokenizers-0.20.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:459f7537119554c2899067dec1ac74a00d02beef6558f4ee2e99513bf6d568af"}, + {file = "tokenizers-0.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:392b87ec89452628c045c9f2a88bc2a827f4c79e7d84bc3b72752b74c2581f70"}, + {file = "tokenizers-0.20.0-cp38-none-win32.whl", hash = "sha256:55a393f893d2ed4dd95a1553c2e42d4d4086878266f437b03590d3f81984c4fe"}, + {file = "tokenizers-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:30ffe33c5c2f2aab8e9a3340d0110dd9f7ace7eec7362e20a697802306bd8068"}, + {file = "tokenizers-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aa2d4a6fed2a7e3f860c7fc9d48764bb30f2649d83915d66150d6340e06742b8"}, + {file = "tokenizers-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5ef0f814084a897e9071fc4a868595f018c5c92889197bdc4bf19018769b148"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc1e1b791e8c3bf4c4f265f180dadaff1c957bf27129e16fdd5e5d43c2d3762c"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b69e55e481459c07885263743a0d3c18d52db19bae8226a19bcca4aaa213fff"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806b4d82e27a2512bc23057b2986bc8b85824914286975b84d8105ff40d03d9"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9859e9ef13adf5a473ccab39d31bff9c550606ae3c784bf772b40f615742a24f"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef703efedf4c20488a8eb17637b55973745b27997ff87bad88ed499b397d1144"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eec0061bab94b1841ab87d10831fdf1b48ebaed60e6d66d66dbe1d873f92bf5"}, + {file = "tokenizers-0.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:980f3d0d7e73f845b69087f29a63c11c7eb924c4ad6b358da60f3db4cf24bdb4"}, + {file = "tokenizers-0.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c157550a2f3851b29d7fdc9dc059fcf81ff0c0fc49a1e5173a89d533ed043fa"}, + {file = "tokenizers-0.20.0-cp39-none-win32.whl", hash = "sha256:8a3d2f4d08608ec4f9895ec25b4b36a97f05812543190a5f2c3cd19e8f041e5a"}, + {file = "tokenizers-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:d90188d12afd0c75e537f9a1d92f9c7375650188ee4f48fdc76f9e38afbd2251"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d68e15f1815357b059ec266062340c343ea7f98f7f330602df81ffa3474b6122"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:23f9ecec637b9bc80da5f703808d29ed5329e56b5aa8d791d1088014f48afadc"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f830b318ee599e3d0665b3e325f85bc75ee2d2ca6285f52e439dc22b64691580"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3dc750def789cb1de1b5a37657919545e1d9ffa667658b3fa9cb7862407a1b8"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e26e6c755ae884c2ea6135cd215bdd0fccafe4ee62405014b8c3cd19954e3ab9"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a1158c7174f427182e08baa2a8ded2940f2b4a3e94969a85cc9cfd16004cbcea"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:6324826287a3fc198898d3dcf758fe4a8479e42d6039f4c59e2cedd3cf92f64e"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d8653149405bb0c16feaf9cfee327fdb6aaef9dc2998349fec686f35e81c4e2"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a2dc1e402a155e97309287ca085c80eb1b7fab8ae91527d3b729181639fa51"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07bef67b20aa6e5f7868c42c7c5eae4d24f856274a464ae62e47a0f2cccec3da"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da06e397182ff53789c506c7833220c192952c57e1581a53f503d8d953e2d67e"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:302f7e11a14814028b7fc88c45a41f1bbe9b5b35fd76d6869558d1d1809baa43"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:055ec46e807b875589dfbe3d9259f9a6ee43394fb553b03b3d1e9541662dbf25"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e3144b8acebfa6ae062e8f45f7ed52e4b50fb6c62f93afc8871b525ab9fdcab3"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b52aa3fd14b2a07588c00a19f66511cff5cca8f7266ca3edcdd17f3512ad159f"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b8cf52779ffc5d4d63a0170fbeb512372bad0dd014ce92bbb9149756c831124"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:983a45dd11a876124378dae71d6d9761822199b68a4c73f32873d8cdaf326a5b"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6b819c9a19831ebec581e71a7686a54ab45d90faf3842269a10c11d746de0c"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e738cfd80795fcafcef89c5731c84b05638a4ab3f412f97d5ed7765466576eb1"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c8842c7be2fadb9c9edcee233b1b7fe7ade406c99b0973f07439985c1c1d0683"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e47a82355511c373a4a430c4909dc1e518e00031207b1fec536c49127388886b"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9afbf359004551179a5db19424180c81276682773cff2c5d002f6eaaffe17230"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07eaa8799a92e6af6f472c21a75bf71575de2af3c0284120b7a09297c0de2f3"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0994b2e5fc53a301071806bc4303e4bc3bdc3f490e92a21338146a36746b0872"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6466e0355b603d10e3cc3d282d350b646341b601e50969464a54939f9848d0"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1e86594c2a433cb1ea09cfbe596454448c566e57ee8905bd557e489d93e89986"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3e14cdef1efa96ecead6ea64a891828432c3ebba128bdc0596e3059fea104ef3"}, + {file = "tokenizers-0.20.0.tar.gz", hash = "sha256:39d7acc43f564c274085cafcd1dae9d36f332456de1a31970296a6b8da4eac8d"}, ] [package.dependencies] @@ -1723,13 +1778,13 @@ testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] [[package]] name = "tqdm" -version = "4.66.4" +version = "4.66.5" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, - {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] [package.dependencies] @@ -1758,24 +1813,35 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typing-extensions" -version = "4.12.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, - {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -1798,4 +1864,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "087149517d2f7a46ecdea6580f73d8358d21f75230132f51f147517e13645eb2" +content-hash = "86a90e7482e6c09e7d1cdddd5780e38aaf0eaab9f5555e7e42a1eb362d8b152b" diff --git a/pyproject.toml b/pyproject.toml index efa4609..2e01864 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,6 @@ convert_jsondoc = "jsondoc.bin.convert_jsondoc:main" python = "^3.11" requests = "^2.32.2" python-dotenv = "^1.0.1" -anthropic = "^0.26.1" ipdb = "^0.13.13" pillow = "^10.3.0" pydantic = "^2.7.2" @@ -25,6 +24,7 @@ bs4 = "^0.0.2" [tool.poetry.group.dev.dependencies] datamodel-code-generator = "^0.25.9" +anthropic = "^0.26.1" [build-system] requires = ["poetry-core"] diff --git a/schema/shared_definitions/shared_definitions_schema.json b/schema/shared_definitions/shared_definitions_schema.json index 8ad6195..a91219e 100644 --- a/schema/shared_definitions/shared_definitions_schema.json +++ b/schema/shared_definitions/shared_definitions_schema.json @@ -31,28 +31,28 @@ "customTypePath": "jsondoc.models.shared_definitions.Annotations", "properties": { "bold": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, "italic": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, "strikethrough": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, "underline": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, "code": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, "color": { - "type": "string", - "default": "default" + "type": "string" + // "default": "default" // TODO: Fix resolution of this reference // "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" } diff --git a/tests/html_jsondoc_pairs/ex1.json b/tests/html_jsondoc_pairs/ex1.json index 2a133b9..bb07d92 100644 --- a/tests/html_jsondoc_pairs/ex1.json +++ b/tests/html_jsondoc_pairs/ex1.json @@ -2,9 +2,9 @@ "html": "

This is a bold word and this is an emphasized word.

", "jsondoc": { "object": "block", - "id": "072628f436164455a408aa5cb0e01845", + "id": "e78ab305-2511-4d81-a3aa-b5e575d96e63", "type": "paragraph", - "created_time": "2024-09-08T08:36:34.346476Z", + "created_time": "2024-09-08T10:05:11.460021Z", "archived": false, "in_trash": false, "has_children": false, @@ -24,12 +24,7 @@ "content": "bold" }, "annotations": { - "bold": true, - "italic": false, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" + "bold": true }, "plain_text": "bold" }, @@ -47,12 +42,7 @@ "content": "emphasized" }, "annotations": { - "bold": false, - "italic": true, - "strikethrough": false, - "underline": false, - "code": false, - "color": "default" + "italic": true }, "plain_text": "emphasized" }, diff --git a/tests/run_serialization_tests.py b/tests/run_serialization_tests.py index 5176d29..874f519 100644 --- a/tests/run_serialization_tests.py +++ b/tests/run_serialization_tests.py @@ -2,19 +2,11 @@ import json import time from jsondoc.serialize import load_page -from jsondoc.utils import load_json_file, timer +from jsondoc.utils import diff_strings, load_json_file, timer PAGE_PATH = "schema/page/ex1_success.json" -def diff_strings(string1, string2): - lines1 = string1.splitlines(keepends=True) - lines2 = string2.splitlines(keepends=True) - - diff = difflib.unified_diff(lines1, lines2, lineterm="") - return "".join(diff) - - def remove_null_fields(string): """ This is just to make the test pass, because they get removed when diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index 5ed35fa..85ab620 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -1,5 +1,14 @@ +import json +import os +from pathlib import Path + +from pydantic import BaseModel + from jsondoc.convert.html import html_to_jsondoc from jsondoc.convert.markdown import jsondoc_to_markdown +from jsondoc.serialize import jsondoc_dump_json, load_jsondoc +from jsondoc.utils import diff_jsonable_dict, set_dict_recursive + def test_convert_html_all_elements(): path = "examples/html/html_all_elements.html" @@ -12,8 +21,71 @@ def test_convert_html_all_elements(): # print("\n\nConverted to markdown:\n\n") # print(jsondoc_to_markdown(ret[0])) - import ipdb; ipdb.set_trace() + import ipdb + + ipdb.set_trace() + + +def compare_jsondoc(jsondoc1: BaseModel, jsondoc2: BaseModel) -> bool: + jsondoc1 = jsondoc_dump_json(jsondoc1) + jsondoc2 = jsondoc_dump_json(jsondoc2) + + # Load the jsondoc back as JSON + jsondoc1 = json.loads(jsondoc1) + jsondoc2 = json.loads(jsondoc2) + + # Set found time fields to "" + fields_to_set_to_empty_string = [ + "id", + "created_time", + "last_edited_time", + "expiry_time", + ] + for field in fields_to_set_to_empty_string: + set_dict_recursive(jsondoc1, field, "") + set_dict_recursive(jsondoc2, field, "") + + diff = diff_jsonable_dict(jsondoc1, jsondoc2) + + if len(diff) > 0: + print(f"Discrepancies found:") + print(diff) + + return len(diff) == 0 + + +def _process_example(json_path): + with open(json_path, "r") as f: + data = json.load(f) + + html_source = data.get("html") + jsondoc_target = data.get("jsondoc") + + assert html_source is not None, f"file {json_path} does not contain field 'html'" + assert ( + jsondoc_target is not None + ), f"file {json_path} does not contain field 'jsondoc'" + + ret = html_to_jsondoc(html_source) + + # Load the jsondoc target + jsondoc_reference = load_jsondoc(jsondoc_target) + + assert compare_jsondoc(ret, jsondoc_reference), f"file {json_path} does not match" + + +def test_examples(): + + current_dir = Path(__file__).parent + html_jsondoc_pairs_dir = current_dir / "html_jsondoc_pairs" + + json_files = [f for f in os.listdir(html_jsondoc_pairs_dir) if f.endswith(".json")] + + for json_file in json_files: + json_path = html_jsondoc_pairs_dir / json_file + _process_example(json_path) if __name__ == "__main__": - test_convert_html_all_elements() \ No newline at end of file + test_examples() + test_convert_html_all_elements() From 7c817a9d18580f3d69d3b6a719a0d9fc393c866e Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:47:23 +0200 Subject: [PATCH 23/45] Add another test --- jsondoc/convert/html.py | 2 +- jsondoc/models/block/__init__.py | 2 +- jsondoc/models/block/base/__init__.py | 8 ++-- .../types/bulleted_list_item/__init__.py | 2 +- jsondoc/models/block/types/code/__init__.py | 2 +- jsondoc/models/block/types/column/__init__.py | 2 +- .../block/types/column_list/__init__.py | 2 +- .../models/block/types/divider/__init__.py | 2 +- .../models/block/types/equation/__init__.py | 2 +- .../models/block/types/heading_1/__init__.py | 2 +- .../models/block/types/heading_2/__init__.py | 2 +- .../models/block/types/heading_3/__init__.py | 2 +- jsondoc/models/block/types/image/__init__.py | 2 +- .../types/image/external_image/__init__.py | 2 +- .../block/types/image/file_image/__init__.py | 2 +- .../types/numbered_list_item/__init__.py | 2 +- .../models/block/types/paragraph/__init__.py | 2 +- jsondoc/models/block/types/quote/__init__.py | 2 +- .../models/block/types/rich_text/__init__.py | 2 +- .../block/types/rich_text/base/__init__.py | 2 +- .../types/rich_text/equation/__init__.py | 2 +- .../block/types/rich_text/text/__init__.py | 2 +- jsondoc/models/block/types/table/__init__.py | 2 +- .../models/block/types/table_row/__init__.py | 2 +- jsondoc/models/block/types/to_do/__init__.py | 2 +- jsondoc/models/block/types/toggle/__init__.py | 2 +- jsondoc/models/file/__init__.py | 2 +- jsondoc/models/file/base/__init__.py | 2 +- jsondoc/models/file/external/__init__.py | 2 +- jsondoc/models/file/file/__init__.py | 2 +- jsondoc/models/page/__init__.py | 2 +- jsondoc/models/shared_definitions/__init__.py | 2 +- schema/block/base/base_schema.json | 12 ++--- tests/html_jsondoc_pairs/ex1.json | 2 - tests/html_jsondoc_pairs/ex2.json | 45 +++++++++++++++++++ 35 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 tests/html_jsondoc_pairs/ex2.json diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 3807a35..c3757be 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -167,7 +167,7 @@ def apply_parent_annotations( def apply_annotations_to_block(annotations_to_apply: Annotations, block: BlockBase): - if hasattr(block, "children"): + if hasattr(block, "children") and isinstance(block.children, list): for child in block.children: apply_annotations_to_block(annotations_to_apply, child) diff --git a/jsondoc/models/block/__init__.py b/jsondoc/models/block/__init__.py index 7efc1f2..c6f053a 100644 --- a/jsondoc/models/block/__init__.py +++ b/jsondoc/models/block/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/base/__init__.py b/jsondoc/models/block/base/__init__.py index d00b096..6ef2e90 100644 --- a/jsondoc/models/block/base/__init__.py +++ b/jsondoc/models/block/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations @@ -44,7 +44,7 @@ class BlockBase(BaseModel): created_by: Optional[CreatedBy] = None last_edited_time: Optional[AwareDatetime] = None last_edited_by: Optional[LastEditedBy] = None - archived: Optional[bool] = False - in_trash: Optional[bool] = False - has_children: Optional[bool] = False + archived: Optional[bool] = None + in_trash: Optional[bool] = None + has_children: Optional[bool] = None metadata: Optional[Dict[str, Any]] = None diff --git a/jsondoc/models/block/types/bulleted_list_item/__init__.py b/jsondoc/models/block/types/bulleted_list_item/__init__.py index 2761ed7..b501452 100644 --- a/jsondoc/models/block/types/bulleted_list_item/__init__.py +++ b/jsondoc/models/block/types/bulleted_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/code/__init__.py b/jsondoc/models/block/types/code/__init__.py index 8270da2..3dee9a2 100644 --- a/jsondoc/models/block/types/code/__init__.py +++ b/jsondoc/models/block/types/code/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column/__init__.py b/jsondoc/models/block/types/column/__init__.py index 0251b88..a2c1f1a 100644 --- a/jsondoc/models/block/types/column/__init__.py +++ b/jsondoc/models/block/types/column/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column_list/__init__.py b/jsondoc/models/block/types/column_list/__init__.py index b6c3cb3..302f1c4 100644 --- a/jsondoc/models/block/types/column_list/__init__.py +++ b/jsondoc/models/block/types/column_list/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/divider/__init__.py b/jsondoc/models/block/types/divider/__init__.py index 66a9138..8132168 100644 --- a/jsondoc/models/block/types/divider/__init__.py +++ b/jsondoc/models/block/types/divider/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/equation/__init__.py b/jsondoc/models/block/types/equation/__init__.py index 03eb135..e3622b4 100644 --- a/jsondoc/models/block/types/equation/__init__.py +++ b/jsondoc/models/block/types/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_1/__init__.py b/jsondoc/models/block/types/heading_1/__init__.py index 18cc31d..eee6006 100644 --- a/jsondoc/models/block/types/heading_1/__init__.py +++ b/jsondoc/models/block/types/heading_1/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_2/__init__.py b/jsondoc/models/block/types/heading_2/__init__.py index de12602..bbc3dee 100644 --- a/jsondoc/models/block/types/heading_2/__init__.py +++ b/jsondoc/models/block/types/heading_2/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_3/__init__.py b/jsondoc/models/block/types/heading_3/__init__.py index a98bb5c..8b3a408 100644 --- a/jsondoc/models/block/types/heading_3/__init__.py +++ b/jsondoc/models/block/types/heading_3/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/__init__.py b/jsondoc/models/block/types/image/__init__.py index 504a14f..eae37ec 100644 --- a/jsondoc/models/block/types/image/__init__.py +++ b/jsondoc/models/block/types/image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/external_image/__init__.py b/jsondoc/models/block/types/image/external_image/__init__.py index 16a996a..1f322a5 100644 --- a/jsondoc/models/block/types/image/external_image/__init__.py +++ b/jsondoc/models/block/types/image/external_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/file_image/__init__.py b/jsondoc/models/block/types/image/file_image/__init__.py index 3c83c4d..607bef4 100644 --- a/jsondoc/models/block/types/image/file_image/__init__.py +++ b/jsondoc/models/block/types/image/file_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/numbered_list_item/__init__.py b/jsondoc/models/block/types/numbered_list_item/__init__.py index 64f1a7d..5a88ccb 100644 --- a/jsondoc/models/block/types/numbered_list_item/__init__.py +++ b/jsondoc/models/block/types/numbered_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/paragraph/__init__.py b/jsondoc/models/block/types/paragraph/__init__.py index a0d42a8..72d073b 100644 --- a/jsondoc/models/block/types/paragraph/__init__.py +++ b/jsondoc/models/block/types/paragraph/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/quote/__init__.py b/jsondoc/models/block/types/quote/__init__.py index 09b164d..7078254 100644 --- a/jsondoc/models/block/types/quote/__init__.py +++ b/jsondoc/models/block/types/quote/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/__init__.py b/jsondoc/models/block/types/rich_text/__init__.py index 8b06bca..c82dc69 100644 --- a/jsondoc/models/block/types/rich_text/__init__.py +++ b/jsondoc/models/block/types/rich_text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/base/__init__.py b/jsondoc/models/block/types/rich_text/base/__init__.py index faed3e4..ce4a93a 100644 --- a/jsondoc/models/block/types/rich_text/base/__init__.py +++ b/jsondoc/models/block/types/rich_text/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/equation/__init__.py b/jsondoc/models/block/types/rich_text/equation/__init__.py index c1893a5..1f948f5 100644 --- a/jsondoc/models/block/types/rich_text/equation/__init__.py +++ b/jsondoc/models/block/types/rich_text/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/text/__init__.py b/jsondoc/models/block/types/rich_text/text/__init__.py index c5e8110..630bbec 100644 --- a/jsondoc/models/block/types/rich_text/text/__init__.py +++ b/jsondoc/models/block/types/rich_text/text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table/__init__.py b/jsondoc/models/block/types/table/__init__.py index 6fbd595..c181fa8 100644 --- a/jsondoc/models/block/types/table/__init__.py +++ b/jsondoc/models/block/types/table/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table_row/__init__.py b/jsondoc/models/block/types/table_row/__init__.py index 8bf671f..f5cac2b 100644 --- a/jsondoc/models/block/types/table_row/__init__.py +++ b/jsondoc/models/block/types/table_row/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/to_do/__init__.py b/jsondoc/models/block/types/to_do/__init__.py index 5a1e4e3..bbbc2aa 100644 --- a/jsondoc/models/block/types/to_do/__init__.py +++ b/jsondoc/models/block/types/to_do/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/toggle/__init__.py b/jsondoc/models/block/types/toggle/__init__.py index 599b102..ca055a0 100644 --- a/jsondoc/models/block/types/toggle/__init__.py +++ b/jsondoc/models/block/types/toggle/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:27+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/__init__.py b/jsondoc/models/file/__init__.py index 40f0c01..5605d7d 100644 --- a/jsondoc/models/file/__init__.py +++ b/jsondoc/models/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/base/__init__.py b/jsondoc/models/file/base/__init__.py index f41ed49..1da9957 100644 --- a/jsondoc/models/file/base/__init__.py +++ b/jsondoc/models/file/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/external/__init__.py b/jsondoc/models/file/external/__init__.py index c1dbbe3..ff2abd4 100644 --- a/jsondoc/models/file/external/__init__.py +++ b/jsondoc/models/file/external/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/file/__init__.py b/jsondoc/models/file/file/__init__.py index 894f680..a06fa9b 100644 --- a/jsondoc/models/file/file/__init__.py +++ b/jsondoc/models/file/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/page/__init__.py b/jsondoc/models/page/__init__.py index 8dc8f9b..bc66e72 100644 --- a/jsondoc/models/page/__init__.py +++ b/jsondoc/models/page/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/jsondoc/models/shared_definitions/__init__.py b/jsondoc/models/shared_definitions/__init__.py index cd7b795..52805b7 100644 --- a/jsondoc/models/shared_definitions/__init__.py +++ b/jsondoc/models/shared_definitions/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T10:03:42+00:00 +# timestamp: 2024-09-08T11:45:26+00:00 from __future__ import annotations diff --git a/schema/block/base/base_schema.json b/schema/block/base/base_schema.json index ccbd9f9..764e833 100644 --- a/schema/block/base/base_schema.json +++ b/schema/block/base/base_schema.json @@ -105,16 +105,16 @@ "additionalProperties": false }, "archived": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, "in_trash": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, "has_children": { - "type": "boolean", - "default": false + "type": "boolean" + // "default": false }, // notion-diverge "metadata": { diff --git a/tests/html_jsondoc_pairs/ex1.json b/tests/html_jsondoc_pairs/ex1.json index bb07d92..8c8dcf9 100644 --- a/tests/html_jsondoc_pairs/ex1.json +++ b/tests/html_jsondoc_pairs/ex1.json @@ -5,8 +5,6 @@ "id": "e78ab305-2511-4d81-a3aa-b5e575d96e63", "type": "paragraph", "created_time": "2024-09-08T10:05:11.460021Z", - "archived": false, - "in_trash": false, "has_children": false, "paragraph": { "rich_text": [ diff --git a/tests/html_jsondoc_pairs/ex2.json b/tests/html_jsondoc_pairs/ex2.json new file mode 100644 index 0000000..4ad484f --- /dev/null +++ b/tests/html_jsondoc_pairs/ex2.json @@ -0,0 +1,45 @@ +{ + "html": "

This is a bold paragraph and this is a bold and emphasized word.

", + "jsondoc": { + "object": "block", + "id": "07f7d08c-78b7-4e46-a354-940d4e00e6fd", + "type": "paragraph", + "created_time": "2024-09-08T11:45:31.925539Z", + "has_children": false, + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is a bold paragraph and this is " + }, + "annotations": { + "bold": true + }, + "plain_text": "This is a bold paragraph and this is " + }, + { + "type": "text", + "text": { + "content": "a bold and emphasized" + }, + "annotations": { + "bold": true, + "italic": true + }, + "plain_text": "a bold and emphasized" + }, + { + "type": "text", + "text": { + "content": " word." + }, + "annotations": { + "bold": true + }, + "plain_text": " word." + } + ] + } + } +} From 3364f79fa27012fbd25f371fff7c7be6bece5dfa Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Mon, 9 Sep 2024 23:30:49 +0200 Subject: [PATCH 24/45] Implement reconcile_to_block(), wip --- jsondoc/bin/convert_jsondoc.py | 2 + jsondoc/convert/html.py | 72 ++++--- jsondoc/convert/markdown.py | 32 ++- jsondoc/convert/utils.py | 28 +++ jsondoc/rules.py | 14 ++ schema/block/types/quote/ex3_success.json | 238 ++++++++++++++++++++++ tests/html_jsondoc_pairs/ex3.json | 70 +++++++ 7 files changed, 429 insertions(+), 27 deletions(-) create mode 100644 schema/block/types/quote/ex3_success.json create mode 100644 tests/html_jsondoc_pairs/ex3.json diff --git a/jsondoc/bin/convert_jsondoc.py b/jsondoc/bin/convert_jsondoc.py index 558720f..befc550 100644 --- a/jsondoc/bin/convert_jsondoc.py +++ b/jsondoc/bin/convert_jsondoc.py @@ -37,6 +37,8 @@ def convert_to_jsondoc(input_file, output_file=None, indent=None): # Print to terminal print(serialized_jsondoc) + # print(jsondoc_to_markdown(jsondoc)) + def markdown_to_html(markdown_content): # Placeholder function for markdown to HTML conversion diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index c3757be..6b9c300 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -4,6 +4,7 @@ import re from jsondoc.convert.utils import ( + append_to_parent_block, append_to_rich_text, create_code_block, create_divider_block, @@ -40,6 +41,7 @@ from jsondoc.models.block.types.toggle import ToggleBlock from jsondoc.models.page import Page from jsondoc.models.shared_definitions import Annotations +from jsondoc.rules import is_block_child_allowed convert_heading_re = re.compile(r"convert_h(\d+)") @@ -181,7 +183,11 @@ def apply_annotations_to_block(annotations_to_apply: Annotations, block: BlockBa def reconcile_to_rich_text( parent_rich_text: RICH_TEXT_TYPE, children: List[CHILDREN_TYPE] ) -> List[CHILDREN_TYPE]: - """ """ + """ + Given a parent rich text and a list of children, + this function will apply the parent annotations + to the children and return a list of objects + """ annotations = parent_rich_text.annotations # Get non-null/false values from annotations @@ -199,13 +205,52 @@ def reconcile_to_rich_text( else: raise ValueError(f"Unsupported type: {type(child)}") - # Append the parent rich text to the final objects only if it has text + # Append the parent rich text to the final objects ONLY if it has text if len(parent_rich_text.plain_text) > 0: final_objects.insert(0, parent_rich_text) return final_objects +def reconcile_to_block( + block: BlockBase, children: List[CHILDREN_TYPE] +) -> List[CHILDREN_TYPE]: + """ + Given a block and a list of children, + this function will reconcile the children to the block + and return a list of objects + """ + remaining_children = [] + + # Any string will be added to the current rich text + current_rich_text = None + for child in children: + if isinstance(child, str): + if current_rich_text is None: + current_rich_text = create_rich_text() + append_to_rich_text(current_rich_text, child) + success_ = try_append_rich_text_to_block(block, current_rich_text) + if not success_: + remaining_children.append(current_rich_text) + + elif isinstance(child, RichTextBase): + success_ = try_append_rich_text_to_block(block, child) + current_rich_text = None + if not success_: + remaining_children.append(child) + + elif isinstance(child, BlockBase): + child_allowed = is_block_child_allowed(block, child) + + if child_allowed: + append_to_parent_block(block, child) + else: + remaining_children.append(child) + + objects = [block] + remaining_children + return objects + + class HtmlToJsonDocConverter(object): class DefaultOptions: autolinks = True @@ -346,28 +391,7 @@ def is_nested_node(el): objects = current_level_object + children_objects elif isinstance(current_level_object, BlockBase): # objects = current_level_object - remaining_children = [] - current_rich_text = None - for child in children_objects: - if isinstance(child, str): - if current_rich_text is None: - current_rich_text = create_rich_text() - append_to_rich_text(current_rich_text, child) - success_ = try_append_rich_text_to_block( - current_level_object, current_rich_text - ) - if not success_: - remaining_children.append(current_rich_text) - - elif isinstance(child, RichTextBase): - success_ = try_append_rich_text_to_block( - current_level_object, child - ) - current_rich_text = None - if not success_: - remaining_children.append(child) - - objects = [current_level_object] + remaining_children + objects = reconcile_to_block(current_level_object, children_objects) elif isinstance(current_level_object, RichTextBase): # There is an assumption that text formatting tags will not contain # higher level tags like blockquotes, lists, etc. diff --git a/jsondoc/convert/markdown.py b/jsondoc/convert/markdown.py index ad35b73..34aaedb 100644 --- a/jsondoc/convert/markdown.py +++ b/jsondoc/convert/markdown.py @@ -9,6 +9,7 @@ from jsondoc.models.block.types.heading_2 import Heading2Block from jsondoc.models.block.types.heading_3 import Heading3Block from jsondoc.models.block.types.paragraph import ParagraphBlock +from jsondoc.models.block.types.quote import QuoteBlock from jsondoc.models.block.types.rich_text.base import RichTextBase from jsondoc.models.block.types.rich_text.equation import RichTextEquation from jsondoc.models.block.types.rich_text.text import RichTextText @@ -130,6 +131,14 @@ def convert_page(self, page: Page) -> str: return ret + def _get_children_content(self, block: BlockBase, convert_as_inline: bool) -> str: + children_content = "" + if hasattr(block, "children") and block.children: + for child in block.children: + children_content += self.convert_block(child, convert_as_inline) + + return children_content + @validate_call def convert_block(self, block: BlockBase, convert_as_inline: bool) -> str: @@ -143,9 +152,6 @@ def convert_block(self, block: BlockBase, convert_as_inline: bool) -> str: block_content = convert_fn(block, convert_as_inline) - if hasattr(block, "children") and block.children: - for child in block.children: - block_content += self.convert_block(child, convert_as_inline) # rich_text = get_rich_text_from_block(block) # if rich_text is None: @@ -248,6 +254,8 @@ def convert_paragraph_block( text = self.convert_rich_text_list_to_markdown(rich_text, escape=True) + text += self._get_children_content(block, convert_as_inline) + if convert_as_inline: return text @@ -276,6 +284,7 @@ def convert_code_block(self, block: CodeBlock, convert_as_inline: bool) -> str: # language = f" {language}" code = self.convert_rich_text_list_to_markdown(rich_text, escape=True) + code += self._get_children_content(block, convert_as_inline) if convert_as_inline: return f"{code}" @@ -294,6 +303,8 @@ def convert_heading_n_block( return "" text = self.convert_rich_text_list_to_markdown(rich_text, escape=True) + text += self._get_children_content(block, convert_as_inline) + if convert_as_inline: return text @@ -309,6 +320,21 @@ def convert_heading_n_block( return "%s %s\n\n" % (hashes, text) + def convert_quote_block(self, block: QuoteBlock, convert_as_inline: bool) -> str: + rich_text = get_rich_text_from_block(block) + + if rich_text is None: + return "" + + # Code blocks are kept verbatim + # escape = isinstance(block, CodeBlock) + + text = self.convert_rich_text_list_to_markdown(rich_text, escape=True) + text += self._get_children_content(block, convert_as_inline) + + if convert_as_inline: + return text + return '\n' + (line_beginning_re.sub('> ', text.strip()) + '\n\n') if text else '' def jsondoc_to_markdown(jsondoc, **options): return JsonDocToMarkdownConverter(**options).convert(jsondoc) diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 60ba06c..7ecc2d6 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -2,6 +2,8 @@ from datetime import datetime, timezone from typing import List, Type +from pydantic import validate_call + from jsondoc.models.block.base import BlockBase from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock from jsondoc.models.block.types.code import Code, CodeBlock, Language @@ -28,6 +30,7 @@ from jsondoc.models.block.types.toggle import ToggleBlock from jsondoc.models.file.external import External from jsondoc.models.shared_definitions import Annotations +from jsondoc.rules import is_block_child_allowed from jsondoc.utils import generate_id @@ -328,4 +331,29 @@ def try_append_rich_text_to_block(block: BlockBase, rich_text: RichTextBase) -> return False +@validate_call +def append_to_parent_block(parent: BlockBase, child: BlockBase) -> bool: + """ + Appends a child block to a parent block + """ + if not hasattr(parent, "children"): + raise ValueError("Parent block cannot have children") + + if not is_block_child_allowed(parent, child): + raise ValueError( + f"Parent block of type {type(parent)} does not allow " + f"children of type {type(child)}" + ) + + if parent.children is None: + parent.children = [] + + parent.children.append(child) + + if parent.has_children is False: + parent.has_children = True + + return True + + # print(create_paragraph_block(text="Hello, world!")) diff --git a/jsondoc/rules.py b/jsondoc/rules.py index 4c75491..e6f3aa1 100644 --- a/jsondoc/rules.py +++ b/jsondoc/rules.py @@ -1,3 +1,4 @@ +from jsondoc.models.block.base import BlockBase from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock from jsondoc.models.block.types.code import CodeBlock from jsondoc.models.block.types.column import ColumnBlock @@ -58,3 +59,16 @@ ToDoBlock: ALL_BLOCK_TYPES, ToggleBlock: ALL_BLOCK_TYPES, } + + +def is_block_child_allowed(parent: BlockBase, child: BlockBase) -> bool: + """ + Given a parent block and a child block, + this function will return True if the child block is allowed + as a child of the parent block, otherwise False + """ + allowed_types = ALLOWED_CHILDREN_BLOCK_TYPES[type(parent)] + for allowed_type in allowed_types: + if isinstance(child, allowed_type): + return True + return False diff --git a/schema/block/types/quote/ex3_success.json b/schema/block/types/quote/ex3_success.json new file mode 100644 index 0000000..d473fee --- /dev/null +++ b/schema/block/types/quote/ex3_success.json @@ -0,0 +1,238 @@ +// Nested quote block example +// +// Per Notion's current convention (2024-09-09), the first line in a quote block +// is stored in `quote.rich_text` +// Any subsequent lines are stored in `children` as paragraph blocks, as well as +// any other block types. +// Conceptually, `quote.rich_text` could be empty +// and all content could be in `children`. +// +// I haven't checked this, but Notion would probably be able +// to render an empty `quote.rich_text` +// and so should any other JSON-DOC renderer. +{ + "object": "block", + "id": "2b76c883-42ff-4b74-9880-36a289b6b358", + "parent": { + "type": "page_id", + "page_id": "04ca6c1c-a077-4925-b9e7-f2cf722ce486" + }, + "created_time": "2024-09-04T13:53:00.000Z", + "last_edited_time": "2024-09-04T13:57:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "quote", + "quote": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Here is a quote block", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Here is a quote block", + "href": null + } + ], + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "15314acc-b6cc-4160-8766-1d0935601b63", + "parent": { + "type": "block_id", + "block_id": "2b76c883-42ff-4b74-9880-36a289b6b358" + }, + "created_time": "2024-09-04T13:53:00.000Z", + "last_edited_time": "2024-09-04T13:54:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Is this a quote block child?", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "Is this a quote block child?", + "href": null + } + ], + "color": "default" + } + }, + { + "object": "block", + "id": "a412fa2d-96ee-4bd6-8a86-b71b4c7b8e2f", + "parent": { + "type": "block_id", + "block_id": "2b76c883-42ff-4b74-9880-36a289b6b358" + }, + "created_time": "2024-09-04T13:56:00.000Z", + "last_edited_time": "2024-09-04T13:56:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": true, + "archived": false, + "in_trash": false, + "type": "to_do", + "to_do": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "test", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "test", + "href": null + } + ], + "checked": false, + "color": "default" + }, + "children": [ + { + "object": "block", + "id": "e7654181-5623-4831-8a0f-76b8cad3e848", + "parent": { + "type": "block_id", + "block_id": "a412fa2d-96ee-4bd6-8a86-b71b4c7b8e2f" + }, + "created_time": "2024-09-04T13:56:00.000Z", + "last_edited_time": "2024-09-04T13:56:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "paragraph", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "test", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "test", + "href": null + } + ], + "color": "default" + } + } + ] + }, + { + "object": "block", + "id": "930bd261-d4ae-4f3c-949e-8e91052710a9", + "parent": { + "type": "block_id", + "block_id": "2b76c883-42ff-4b74-9880-36a289b6b358" + }, + "created_time": "2024-09-04T13:56:00.000Z", + "last_edited_time": "2024-09-04T13:57:00.000Z", + "created_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "last_edited_by": { + "object": "user", + "id": "b9eb2a95-ab37-462d-b6ff-ff84080051f0" + }, + "has_children": false, + "archived": false, + "in_trash": false, + "type": "heading_2", + "heading_2": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "heading inside quote", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "heading inside quote", + "href": null + } + ], + "is_toggleable": false, + "color": "default" + } + } + ] +} diff --git a/tests/html_jsondoc_pairs/ex3.json b/tests/html_jsondoc_pairs/ex3.json new file mode 100644 index 0000000..99f03b2 --- /dev/null +++ b/tests/html_jsondoc_pairs/ex3.json @@ -0,0 +1,70 @@ +{ + "html": "

This is a paragraph inside a blockquote element.

", + "jsondoc": { + "object": "block", + "id": "4616a665-f8cd-478d-8116-f243c321d261", + "type": "quote", + "created_time": "2024-09-09T21:21:32.914510Z", + "has_children": true, + "quote": { + "rich_text": [] + }, + "children": [ + { + "object": "block", + "id": "783174c8-d717-47e0-a2de-1945007bb051", + "type": "paragraph", + "created_time": "2024-09-09T21:21:32.913806Z", + "has_children": false, + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is a " + }, + "annotations": {}, + "plain_text": "This is a " + }, + { + "type": "text", + "text": { + "content": "paragraph" + }, + "annotations": { + "bold": true + }, + "plain_text": "paragraph" + }, + { + "type": "text", + "text": { + "content": " inside a " + }, + "annotations": {}, + "plain_text": " inside a " + }, + { + "type": "text", + "text": { + "content": "blockquote" + }, + "annotations": { + "italic": true + }, + "plain_text": "blockquote" + }, + { + "type": "text", + "text": { + "content": " element." + }, + "annotations": {}, + "plain_text": " element." + } + ] + } + } + ] + } +} From b31f217a82f74c737951a0507c0ebdae5956375f Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Mon, 9 Sep 2024 23:38:38 +0200 Subject: [PATCH 25/45] Minor --- jsondoc/convert/html.py | 30 +++++++++++------------------- tests/test_html_to_jsondoc.py | 1 + 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 6b9c300..6d9bd7a 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -387,24 +387,15 @@ def is_nested_node(el): if current_level_object is None: objects = children_objects - elif isinstance(current_level_object, list): - objects = current_level_object + children_objects elif isinstance(current_level_object, BlockBase): - # objects = current_level_object objects = reconcile_to_block(current_level_object, children_objects) elif isinstance(current_level_object, RichTextBase): - # There is an assumption that text formatting tags will not contain - # higher level tags like blockquotes, lists, etc. - # for child in children_objects: - # if isinstance(child, str): - # append_to_rich_text(current_level_object, child) - - # objects = [current_level_object] objects = reconcile_to_rich_text(current_level_object, children_objects) - - # import ipdb - - # ipdb.set_trace() + else: + import ipdb; ipdb.set_trace() + raise Exception( + f"Current node has yielded an unexpected type {type(current_level_object)}" + ) return objects @@ -499,11 +490,12 @@ def convert_a(self, el, convert_as_inline): # title = href # title_part = ' "%s"' % title.replace('"', r"\"") if title else "" - return [ - create_rich_text(text=prefix), - create_rich_text(text=text, url=href), - create_rich_text(text=suffix), - ] + # return [ + # create_rich_text(text=prefix), + # create_rich_text(text=text, url=href), + # create_rich_text(text=suffix), + # ] + return create_rich_text(text=text, url=href) # return ( # "%s[%s](%s%s)%s" % (prefix, text, href, title_part, suffix) # if href diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index 85ab620..a7f806d 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -21,6 +21,7 @@ def test_convert_html_all_elements(): # print("\n\nConverted to markdown:\n\n") # print(jsondoc_to_markdown(ret[0])) + # print(jsondoc_to_markdown(ret)) import ipdb ipdb.set_trace() From f8ecf32ba158f9a45197b8685ef03c6c77058731 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:11:48 +0200 Subject: [PATCH 26/45] Implement table support --- jsondoc/bin/convert_jsondoc.py | 1 - jsondoc/convert/html.py | 126 ++++++------ jsondoc/convert/markdown.py | 54 +++++- jsondoc/convert/utils.py | 73 ++++++- .../{ex1.json => ex01_basic_paragraph.json} | 0 .../{ex2.json => ex02_bold_paragraph.json} | 0 ...3.json => ex03_blockquoted_paragraph.json} | 0 .../html_jsondoc_pairs/ex04_basic_table.json | 179 ++++++++++++++++++ 8 files changed, 372 insertions(+), 61 deletions(-) rename tests/html_jsondoc_pairs/{ex1.json => ex01_basic_paragraph.json} (100%) rename tests/html_jsondoc_pairs/{ex2.json => ex02_bold_paragraph.json} (100%) rename tests/html_jsondoc_pairs/{ex3.json => ex03_blockquoted_paragraph.json} (100%) create mode 100644 tests/html_jsondoc_pairs/ex04_basic_table.json diff --git a/jsondoc/bin/convert_jsondoc.py b/jsondoc/bin/convert_jsondoc.py index befc550..2d6b4e1 100644 --- a/jsondoc/bin/convert_jsondoc.py +++ b/jsondoc/bin/convert_jsondoc.py @@ -36,7 +36,6 @@ def convert_to_jsondoc(input_file, output_file=None, indent=None): else: # Print to terminal print(serialized_jsondoc) - # print(jsondoc_to_markdown(jsondoc)) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 6d9bd7a..600acfa 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -15,7 +15,10 @@ create_paragraph_block, create_quote_block, create_rich_text, + create_table_block, + create_table_row_block, get_rich_text_from_block, + table_has_header_row, try_append_rich_text_to_block, ) from jsondoc.models.block.base import BlockBase @@ -180,6 +183,30 @@ def apply_annotations_to_block(annotations_to_apply: Annotations, block: BlockBa apply_parent_annotations(annotations_to_apply, rich_text.annotations) +def append_paragraph_block_to_table_row_block( + parent_table_row_block: TableRowBlock, child_paragraph_block: ParagraphBlock +): + """ + Appends a paragraph block to a table row block + """ + if parent_table_row_block.table_row.cells is None: + parent_table_row_block.table_row.cells = [] + + child_rich_text = get_rich_text_from_block(child_paragraph_block) + if child_rich_text is None: + child_rich_text = [] + + parent_table_row_block.table_row.cells.append(child_rich_text) + + +# Override append functions +# Maps pairs of (parent_block_type, child_block_type) to a function +# that appends the child block to the parent block +OVERRIDE_APPEND_FUNCTIONS = { + (TableRowBlock, ParagraphBlock): append_paragraph_block_to_table_row_block, +} + + def reconcile_to_rich_text( parent_rich_text: RICH_TEXT_TYPE, children: List[CHILDREN_TYPE] ) -> List[CHILDREN_TYPE]: @@ -241,9 +268,19 @@ def reconcile_to_block( elif isinstance(child, BlockBase): child_allowed = is_block_child_allowed(block, child) + append_function = OVERRIDE_APPEND_FUNCTIONS.get( + (type(block), type(child)) + ) - if child_allowed: - append_to_parent_block(block, child) + # We introduce the following condition instead of only + # if child_allowed: + # because we need to do a hack with some block types, such as + # convert td/th elements to paragraph blocks + if child_allowed or append_function is not None: + if append_function: + append_function(block, child) + else: + append_to_parent_block(block, child) else: remaining_children.append(child) @@ -392,7 +429,9 @@ def is_nested_node(el): elif isinstance(current_level_object, RichTextBase): objects = reconcile_to_rich_text(current_level_object, children_objects) else: - import ipdb; ipdb.set_trace() + import ipdb + + ipdb.set_trace() raise Exception( f"Current node has yielded an unexpected type {type(current_level_object)}" ) @@ -571,43 +610,44 @@ def convert_code(self, el, convert_as_inline): # if style == ATX_CLOSED: # return "%s %s %s\n\n" % (hashes, text, hashes) # return "%s %s\n\n" % (hashes, text) + def convert_h1(self, el, convert_as_inline): - text = el.get_text() + # text = el.get_text() if convert_as_inline: return create_rich_text() return create_h1_block() def convert_h2(self, el, convert_as_inline): - text = el.get_text() + # text = el.get_text() if convert_as_inline: return create_rich_text() return create_h2_block() def convert_h3(self, el, convert_as_inline): - text = el.get_text() + # text = el.get_text() if convert_as_inline: return create_rich_text() return create_h3_block() def convert_h4(self, el, convert_as_inline): - text = el.get_text() + # text = el.get_text() if convert_as_inline: return create_rich_text() return create_paragraph_block() def convert_h5(self, el, convert_as_inline): - text = el.get_text() + # text = el.get_text() if convert_as_inline: return create_rich_text() return create_paragraph_block() def convert_h6(self, el, convert_as_inline): - text = el.get_text() + # text = el.get_text() if convert_as_inline: return create_rich_text() @@ -720,11 +760,9 @@ def convert_pre(self, el, convert_as_inline): return create_code_block(code=text, language=code_language) def convert_script(self, el, convert_as_inline): - # return "" return None def convert_style(self, el, convert_as_inline): - # return "" return None convert_s = convert_del @@ -746,7 +784,10 @@ def convert_style(self, el, convert_as_inline): def convert_table(self, el, convert_as_inline): # return "\n\n" + text + "\n" - return None # TBD + has_column_header = table_has_header_row(el) + return create_table_block( + has_column_header=has_column_header, + ) def convert_caption(self, el, convert_as_inline): # return text + "\n" @@ -757,53 +798,28 @@ def convert_figcaption(self, el, convert_as_inline): return None # TBD def convert_td(self, el, convert_as_inline): - # colspan = 1 - # if "colspan" in el.attrs and el["colspan"].isdigit(): - # colspan = int(el["colspan"]) - # return " " + text.strip().replace("\n", " ") + " |" * colspan - return None # TBD + """ + Regular table cell + +
are not in this order +{ + "html": "
This is a caption for a table + This is a caption for a table +
ID and elements are intermediately converted to ParagraphBlocks. + This is so that they act as rich-text containers. + While paragraph block child is being reconciled to a table row block, + paragraph_block.rich_text will be extracted to form table_row.cells. + """ + return create_paragraph_block() def convert_th(self, el, convert_as_inline): - # colspan = 1 - # if "colspan" in el.attrs and el["colspan"].isdigit(): - # colspan = int(el["colspan"]) - # return " " + text.strip().replace("\n", " ") + " |" * colspan - return None # TBD + """ + Table header cell + """ + # TBD: Somehow convey header info to table block + return create_paragraph_block() def convert_tr(self, el, convert_as_inline): - # cells = el.find_all(["td", "th"]) - # is_headrow = ( - # all([cell.name == "th" for cell in cells]) - # or (not el.previous_sibling and not el.parent.name == "tbody") - # or ( - # not el.previous_sibling - # and el.parent.name == "tbody" - # and len(el.parent.parent.find_all(["thead"])) < 1 - # ) - # ) - # overline = "" - # underline = "" - # if is_headrow and not el.previous_sibling: - # # first row and is headline: print headline underline - # full_colspan = 0 - # for cell in cells: - # if "colspan" in cell.attrs and cell["colspan"].isdigit(): - # full_colspan += int(cell["colspan"]) - # else: - # full_colspan += 1 - # underline += "| " + " | ".join(["---"] * full_colspan) + " |" + "\n" - # elif not el.previous_sibling and ( - # el.parent.name == "table" - # or (el.parent.name == "tbody" and not el.parent.previous_sibling) - # ): - # # first row, not headline, and: - # # - the parent is table or - # # - the parent is tbody at the beginning of a table. - # # print empty headline above this row - # overline += "| " + " | ".join([""] * len(cells)) + " |" + "\n" - # overline += "| " + " | ".join(["---"] * len(cells)) + " |" + "\n" - # return overline + "|" + text + "\n" + underline - return None # TBD + """ + Table row + """ + return create_table_row_block() def html_to_jsondoc(html: str | bytes, **options) -> Page | BlockBase | List[BlockBase]: diff --git a/jsondoc/convert/markdown.py b/jsondoc/convert/markdown.py index 34aaedb..bc04aa6 100644 --- a/jsondoc/convert/markdown.py +++ b/jsondoc/convert/markdown.py @@ -13,6 +13,8 @@ from jsondoc.models.block.types.rich_text.base import RichTextBase from jsondoc.models.block.types.rich_text.equation import RichTextEquation from jsondoc.models.block.types.rich_text.text import RichTextText +from jsondoc.models.block.types.table import TableBlock +from jsondoc.models.block.types.table_row import TableRowBlock from jsondoc.models.page import Page from jsondoc.models.shared_definitions import Annotations from jsondoc.serialize import load_jsondoc, load_page @@ -152,7 +154,6 @@ def convert_block(self, block: BlockBase, convert_as_inline: bool) -> str: block_content = convert_fn(block, convert_as_inline) - # rich_text = get_rich_text_from_block(block) # if rich_text is None: # return "" @@ -334,7 +335,56 @@ def convert_quote_block(self, block: QuoteBlock, convert_as_inline: bool) -> str if convert_as_inline: return text - return '\n' + (line_beginning_re.sub('> ', text.strip()) + '\n\n') if text else '' + return ( + "\n" + (line_beginning_re.sub("> ", text.strip()) + "\n\n") if text else "" + ) + + def _convert_table_row_block( + self, block: TableRowBlock, convert_as_inline: bool, is_headrow: bool + ) -> str: + # cells = block.table_row.cells + + # cells = el.find_all(["td", "th"]) + cells = block.table_row.cells + + overline = "" + underline = "" + if is_headrow: + # first row and is headline: print headline underline + full_colspan = 0 + for cell in cells: + # if "colspan" in cell.attrs and cell["colspan"].isdigit(): + # full_colspan += int(cell["colspan"]) + # else: + full_colspan += 1 + underline += "| " + " | ".join(["---"] * full_colspan) + " |" + "\n" + # else: + # # first row, not headline, and: + # # - the parent is table or + # # - the parent is tbody at the beginning of a table. + # # print empty headline above this row + # overline += "| " + " | ".join([""] * len(cells)) + " |" + "\n" + # overline += "| " + " | ".join(["---"] * len(cells)) + " |" + "\n" + + text = "" + for cell in cells: + cell_text = self.convert_rich_text_list_to_markdown(cell, escape=True) + text += " " + cell_text.strip().replace("\n", " ") + " |" + + return overline + "|" + text + "\n" + underline + + def convert_table_block(self, block: TableBlock, convert_as_inline: bool) -> str: + text = "" + + for n, row in enumerate(block.children): + is_headrow = block.table.has_column_header and n == 0 + + text += self._convert_table_row_block( + row, convert_as_inline, is_headrow=is_headrow, + ) + + return "\n\n" + text + "\n" + def jsondoc_to_markdown(jsondoc, **options): return JsonDocToMarkdownConverter(**options).convert(jsondoc) diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 7ecc2d6..9ba4458 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -2,6 +2,7 @@ from datetime import datetime, timezone from typing import List, Type +from bs4 import Tag from pydantic import validate_call from jsondoc.models.block.base import BlockBase @@ -24,8 +25,8 @@ from jsondoc.models.block.types.rich_text.equation import Equation as EquationObj from jsondoc.models.block.types.rich_text.equation import RichTextEquation from jsondoc.models.block.types.rich_text.text import Link, RichTextText, Text -from jsondoc.models.block.types.table import TableBlock -from jsondoc.models.block.types.table_row import TableRowBlock +from jsondoc.models.block.types.table import Table, TableBlock +from jsondoc.models.block.types.table_row import TableRow, TableRowBlock from jsondoc.models.block.types.to_do import ToDoBlock from jsondoc.models.block.types.toggle import ToggleBlock from jsondoc.models.file.external import External @@ -288,6 +289,49 @@ def create_quote_block( ) +def create_table_row_block( + cells: List[List[RichTextBase]] = [], + id: str | None = None, + created_time=None, +) -> TableRowBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + return TableRowBlock( + id=id, + created_time=created_time, + table_row=TableRow(cells=cells), + has_children=False, + ) + + +def create_table_block( + table_rows: List[TableRowBlock] = [], + id: str | None = None, + created_time=None, + table_width: int | None = None, + has_column_header: bool = False, + has_row_header: bool = False, +) -> TableBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = datetime.now(tz=timezone.utc) + + return TableBlock( + id=id, + created_time=created_time, + children=table_rows, + table=Table( + table_width=table_width, + has_column_header=has_column_header, + has_row_header=has_row_header, + ), + ) + + def get_rich_text_from_block(block: BlockBase) -> List[RichTextBase] | None: """ Returns the rich text of a block @@ -356,4 +400,27 @@ def append_to_parent_block(parent: BlockBase, child: BlockBase) -> bool: return True -# print(create_paragraph_block(text="Hello, world!")) +def table_has_header_row(table: Tag) -> bool: + """ + Check if a table has a header row. + """ + # Check if there's a thead element + if table.find("thead"): + return True + + # Check the first row of the table + first_row = table.find("tr") + if first_row: + # If all cells in the first row are th, it's a header row + if all(cell.name == "th" for cell in first_row.find_all(["td", "th"])): + return True + + # If it's the first row in a tbody and there's no thead, it might be a header + if ( + first_row.parent.name == "tbody" + and not first_row.previous_sibling + and not table.find("thead") + ): + return True + + return False diff --git a/tests/html_jsondoc_pairs/ex1.json b/tests/html_jsondoc_pairs/ex01_basic_paragraph.json similarity index 100% rename from tests/html_jsondoc_pairs/ex1.json rename to tests/html_jsondoc_pairs/ex01_basic_paragraph.json diff --git a/tests/html_jsondoc_pairs/ex2.json b/tests/html_jsondoc_pairs/ex02_bold_paragraph.json similarity index 100% rename from tests/html_jsondoc_pairs/ex2.json rename to tests/html_jsondoc_pairs/ex02_bold_paragraph.json diff --git a/tests/html_jsondoc_pairs/ex3.json b/tests/html_jsondoc_pairs/ex03_blockquoted_paragraph.json similarity index 100% rename from tests/html_jsondoc_pairs/ex3.json rename to tests/html_jsondoc_pairs/ex03_blockquoted_paragraph.json diff --git a/tests/html_jsondoc_pairs/ex04_basic_table.json b/tests/html_jsondoc_pairs/ex04_basic_table.json new file mode 100644 index 0000000..0a0054f --- /dev/null +++ b/tests/html_jsondoc_pairs/ex04_basic_table.json @@ -0,0 +1,179 @@ +{ + "html": "
NameAgeCountry
John Doe30USA
Jane Smith25Canada
Bob Johnson35UK
", + "jsondoc": { + "object": "block", + "id": "38edfaa5-3ce1-478a-8172-2c64d6d32b1b", + "type": "table", + "created_time": "2024-09-10T14:08:38.442893Z", + "table": { + "has_column_header": true, + "has_row_header": false + }, + "children": [ + { + "object": "block", + "id": "8c17b0c5-f741-49a9-98c6-05605a9cb0ed", + "type": "table_row", + "created_time": "2024-09-10T14:08:38.442626Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "Name" + }, + "annotations": {}, + "plain_text": "Name" + } + ], + [ + { + "type": "text", + "text": { + "content": "Age" + }, + "annotations": {}, + "plain_text": "Age" + } + ], + [ + { + "type": "text", + "text": { + "content": "Country" + }, + "annotations": {}, + "plain_text": "Country" + } + ] + ] + } + }, + { + "object": "block", + "id": "86e7aca1-3626-40a3-bb41-d3df63aab639", + "type": "table_row", + "created_time": "2024-09-10T14:08:38.442732Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "John Doe" + }, + "annotations": {}, + "plain_text": "John Doe" + } + ], + [ + { + "type": "text", + "text": { + "content": "30" + }, + "annotations": {}, + "plain_text": "30" + } + ], + [ + { + "type": "text", + "text": { + "content": "USA" + }, + "annotations": {}, + "plain_text": "USA" + } + ] + ] + } + }, + { + "object": "block", + "id": "34688e59-64ce-4b98-9e45-561816f909ea", + "type": "table_row", + "created_time": "2024-09-10T14:08:38.442811Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "Jane Smith" + }, + "annotations": {}, + "plain_text": "Jane Smith" + } + ], + [ + { + "type": "text", + "text": { + "content": "25" + }, + "annotations": {}, + "plain_text": "25" + } + ], + [ + { + "type": "text", + "text": { + "content": "Canada" + }, + "annotations": {}, + "plain_text": "Canada" + } + ] + ] + } + }, + { + "object": "block", + "id": "20ea1917-b79e-4362-a0ee-61ee5b347a38", + "type": "table_row", + "created_time": "2024-09-10T14:08:38.442882Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "Bob Johnson" + }, + "annotations": {}, + "plain_text": "Bob Johnson" + } + ], + [ + { + "type": "text", + "text": { + "content": "35" + }, + "annotations": {}, + "plain_text": "35" + } + ], + [ + { + "type": "text", + "text": { + "content": "UK" + }, + "annotations": {}, + "plain_text": "UK" + } + ] + ] + } + } + ] + } +} From d2b841189ba8c6d7865c31bf16c2e7d52d836ad8 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:08:02 +0200 Subject: [PATCH 27/45] Add
support --- docs/html-conversion.md | 9 +++- jsondoc/convert/html.py | 36 ++++++++++++---- jsondoc/convert/utils.py | 35 ++++++++++------ jsondoc/models/block/__init__.py | 2 +- jsondoc/models/block/base/__init__.py | 2 +- .../types/bulleted_list_item/__init__.py | 4 +- jsondoc/models/block/types/code/__init__.py | 4 +- jsondoc/models/block/types/column/__init__.py | 2 +- .../block/types/column_list/__init__.py | 2 +- .../models/block/types/divider/__init__.py | 2 +- .../models/block/types/equation/__init__.py | 2 +- .../models/block/types/heading_1/__init__.py | 4 +- .../models/block/types/heading_2/__init__.py | 4 +- .../models/block/types/heading_3/__init__.py | 4 +- jsondoc/models/block/types/image/__init__.py | 2 +- .../types/image/external_image/__init__.py | 2 +- .../block/types/image/file_image/__init__.py | 2 +- .../types/numbered_list_item/__init__.py | 4 +- .../models/block/types/paragraph/__init__.py | 4 +- jsondoc/models/block/types/quote/__init__.py | 4 +- .../models/block/types/rich_text/__init__.py | 2 +- .../block/types/rich_text/base/__init__.py | 2 +- .../types/rich_text/equation/__init__.py | 2 +- .../block/types/rich_text/text/__init__.py | 2 +- jsondoc/models/block/types/table/__init__.py | 2 +- .../models/block/types/table_row/__init__.py | 2 +- jsondoc/models/block/types/to_do/__init__.py | 4 +- jsondoc/models/block/types/toggle/__init__.py | 4 +- jsondoc/models/file/__init__.py | 2 +- jsondoc/models/file/base/__init__.py | 2 +- jsondoc/models/file/external/__init__.py | 2 +- jsondoc/models/file/file/__init__.py | 2 +- jsondoc/models/page/__init__.py | 2 +- jsondoc/models/shared_definitions/__init__.py | 2 +- jsondoc/serialize.py | 6 ++- jsondoc/utils.py | 28 +++++++++---- .../bulleted_list_item_schema.json | 3 +- schema/block/types/code/code_schema.json | 3 +- .../types/heading_1/heading_1_schema.json | 3 +- .../types/heading_2/heading_2_schema.json | 3 +- .../types/heading_3/heading_3_schema.json | 3 +- .../numbered_list_item_schema.json | 3 +- .../types/paragraph/paragraph_schema.json | 3 +- schema/block/types/quote/quote_schema.json | 3 +- schema/block/types/to_do/to_do_schema.json | 3 +- schema/block/types/toggle/toggle_schema.json | 3 +- tests/html_jsondoc_pairs/ex05_basic_br.json | 42 +++++++++++++++++++ tests/test_html_to_jsondoc.py | 2 + 48 files changed, 186 insertions(+), 84 deletions(-) create mode 100644 tests/html_jsondoc_pairs/ex05_basic_br.json diff --git a/docs/html-conversion.md b/docs/html-conversion.md index 6c8383b..2937b7c 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -58,4 +58,11 @@ def process_tag(node): # Reconcile the children objects with the current node object return_objects: list = reconcile_children(current_node_object, children_objects) return return_objects -``` \ No newline at end of file +``` + + +## Remaining tasks + +- Convert lists `
    ` and `
      ` to `ListBlock` +- Convert line breaks `
      ` to `ParagraphBlock` +- Add sup/sub annotation? \ No newline at end of file diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 600acfa..f8c3e43 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -4,6 +4,7 @@ import re from jsondoc.convert.utils import ( + BreakElementPlaceholderBlock, append_to_parent_block, append_to_rich_text, create_code_block, @@ -45,6 +46,7 @@ from jsondoc.models.page import Page from jsondoc.models.shared_definitions import Annotations from jsondoc.rules import is_block_child_allowed +from jsondoc.utils import generate_id, get_current_time convert_heading_re = re.compile(r"convert_h(\d+)") @@ -248,6 +250,7 @@ def reconcile_to_block( and return a list of objects """ remaining_children = [] + objects = [block] # Any string will be added to the current rich text current_rich_text = None @@ -266,11 +269,28 @@ def reconcile_to_block( if not success_: remaining_children.append(child) + elif isinstance(child, BreakElementPlaceholderBlock): + # TBD: Should probably abstract away all placeholder blocks + # Create an empty version of the current block, and set it as the current parent + # A
      element basically creates an empty block of the same type as the parent + block_type = block.type + # Get corresponding field from the block + block_field = getattr(block, block_type) + init_kwargs = { + "id": generate_id(), + "created_time": child.created_time, + block_type: type(block_field)() + } + + empty_block = type(block)(**init_kwargs) + # If we don't set current_rich_text to None, then the rich_text object will be + # shared across different blocks and cause duplicate text issues + current_rich_text = None + objects.append(empty_block) + block = empty_block elif isinstance(child, BlockBase): child_allowed = is_block_child_allowed(block, child) - append_function = OVERRIDE_APPEND_FUNCTIONS.get( - (type(block), type(child)) - ) + append_function = OVERRIDE_APPEND_FUNCTIONS.get((type(block), type(child))) # We introduce the following condition instead of only # if child_allowed: @@ -284,7 +304,8 @@ def reconcile_to_block( else: remaining_children.append(child) - objects = [block] + remaining_children + objects = objects + remaining_children + return objects @@ -420,7 +441,6 @@ def is_nested_node(el): current_level_object = convert_fn(node, convert_as_inline) # print(node, repr(current_level_object)) - # if children_objects: if current_level_object is None: objects = children_objects @@ -429,9 +449,6 @@ def is_nested_node(el): elif isinstance(current_level_object, RichTextBase): objects = reconcile_to_rich_text(current_level_object, children_objects) else: - import ipdb - - ipdb.set_trace() raise Exception( f"Current node has yielded an unexpected type {type(current_level_object)}" ) @@ -569,7 +586,8 @@ def convert_br(self, el, convert_as_inline): # return "\\\n" # else: # return " \n" - return None # TBD + # return None # TBD + return BreakElementPlaceholderBlock(id="", created_time=get_current_time()) def convert_code(self, el, convert_as_inline): text = el.get_text() diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 9ba4458..0109d6e 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -1,6 +1,6 @@ import logging from datetime import datetime, timezone -from typing import List, Type +from typing import List, Literal, Type from bs4 import Tag from pydantic import validate_call @@ -32,7 +32,16 @@ from jsondoc.models.file.external import External from jsondoc.models.shared_definitions import Annotations from jsondoc.rules import is_block_child_allowed -from jsondoc.utils import generate_id +from jsondoc.utils import generate_id, get_current_time + + +class BreakElementPlaceholderBlock(BlockBase): + type: Literal["break_element_placeholder"] = "break_element_placeholder" + + +PLACEHOLDER_BLOCKS = [ + BreakElementPlaceholderBlock, +] def create_rich_text( @@ -99,12 +108,13 @@ def create_paragraph_block( text: str | None = None, id: str | None = None, created_time=None, + metadata: dict | None = None, **kwargs, ) -> ParagraphBlock: if id is None: id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() rich_text = [] if text is not None: @@ -115,6 +125,7 @@ def create_paragraph_block( created_time=created_time, paragraph=Paragraph(rich_text=rich_text), has_children=False, + metadata=metadata, ) @@ -128,7 +139,7 @@ def create_code_block( if id is None: id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() language_ = None try: @@ -158,7 +169,7 @@ def create_divider_block( if id is None: id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() return DividerBlock( id=id, @@ -178,7 +189,7 @@ def create_h1_block( id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() rich_text = [] if text is not None: @@ -202,7 +213,7 @@ def create_h2_block( id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() rich_text = [] if text is not None: @@ -226,7 +237,7 @@ def create_h3_block( id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() rich_text = [] if text is not None: @@ -249,7 +260,7 @@ def create_image_block( if id is None: id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() caption_ = None if caption is not None: @@ -275,7 +286,7 @@ def create_quote_block( if id is None: id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() rich_text = [] if text is not None: @@ -297,7 +308,7 @@ def create_table_row_block( if id is None: id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() return TableRowBlock( id=id, @@ -318,7 +329,7 @@ def create_table_block( if id is None: id = generate_id() if created_time is None: - created_time = datetime.now(tz=timezone.utc) + created_time = get_current_time() return TableBlock( id=id, diff --git a/jsondoc/models/block/__init__.py b/jsondoc/models/block/__init__.py index c6f053a..348ceeb 100644 --- a/jsondoc/models/block/__init__.py +++ b/jsondoc/models/block/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/base/__init__.py b/jsondoc/models/block/base/__init__.py index 6ef2e90..e1edd7e 100644 --- a/jsondoc/models/block/base/__init__.py +++ b/jsondoc/models/block/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/bulleted_list_item/__init__.py b/jsondoc/models/block/types/bulleted_list_item/__init__.py index b501452..df5e9e1 100644 --- a/jsondoc/models/block/types/bulleted_list_item/__init__.py +++ b/jsondoc/models/block/types/bulleted_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -18,7 +18,7 @@ class BulletedListItem(BaseModel): extra='forbid', arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None diff --git a/jsondoc/models/block/types/code/__init__.py b/jsondoc/models/block/types/code/__init__.py index 3dee9a2..b8dcfb7 100644 --- a/jsondoc/models/block/types/code/__init__.py +++ b/jsondoc/models/block/types/code/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -94,7 +94,7 @@ class Code(BaseModel): arbitrary_types_allowed=True, ) caption: Optional[List[RichTextBase]] = None - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] language: Optional[Language] = None diff --git a/jsondoc/models/block/types/column/__init__.py b/jsondoc/models/block/types/column/__init__.py index a2c1f1a..90cbb06 100644 --- a/jsondoc/models/block/types/column/__init__.py +++ b/jsondoc/models/block/types/column/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/column_list/__init__.py b/jsondoc/models/block/types/column_list/__init__.py index 302f1c4..cc39ad1 100644 --- a/jsondoc/models/block/types/column_list/__init__.py +++ b/jsondoc/models/block/types/column_list/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/divider/__init__.py b/jsondoc/models/block/types/divider/__init__.py index 8132168..124b323 100644 --- a/jsondoc/models/block/types/divider/__init__.py +++ b/jsondoc/models/block/types/divider/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/equation/__init__.py b/jsondoc/models/block/types/equation/__init__.py index e3622b4..1479963 100644 --- a/jsondoc/models/block/types/equation/__init__.py +++ b/jsondoc/models/block/types/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/heading_1/__init__.py b/jsondoc/models/block/types/heading_1/__init__.py index eee6006..10fb530 100644 --- a/jsondoc/models/block/types/heading_1/__init__.py +++ b/jsondoc/models/block/types/heading_1/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -18,7 +18,7 @@ class Heading1(BaseModel): extra='forbid', arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None is_toggleable: Optional[bool] = None diff --git a/jsondoc/models/block/types/heading_2/__init__.py b/jsondoc/models/block/types/heading_2/__init__.py index bbc3dee..dcdf449 100644 --- a/jsondoc/models/block/types/heading_2/__init__.py +++ b/jsondoc/models/block/types/heading_2/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -17,7 +17,7 @@ class Heading2(BaseModel): model_config = ConfigDict( arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None is_toggleable: Optional[bool] = None diff --git a/jsondoc/models/block/types/heading_3/__init__.py b/jsondoc/models/block/types/heading_3/__init__.py index 8b3a408..f585a1b 100644 --- a/jsondoc/models/block/types/heading_3/__init__.py +++ b/jsondoc/models/block/types/heading_3/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -17,7 +17,7 @@ class Heading3(BaseModel): model_config = ConfigDict( arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None is_toggleable: Optional[bool] = None diff --git a/jsondoc/models/block/types/image/__init__.py b/jsondoc/models/block/types/image/__init__.py index eae37ec..d02a033 100644 --- a/jsondoc/models/block/types/image/__init__.py +++ b/jsondoc/models/block/types/image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/external_image/__init__.py b/jsondoc/models/block/types/image/external_image/__init__.py index 1f322a5..bf9761e 100644 --- a/jsondoc/models/block/types/image/external_image/__init__.py +++ b/jsondoc/models/block/types/image/external_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/image/file_image/__init__.py b/jsondoc/models/block/types/image/file_image/__init__.py index 607bef4..19e0f35 100644 --- a/jsondoc/models/block/types/image/file_image/__init__.py +++ b/jsondoc/models/block/types/image/file_image/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/numbered_list_item/__init__.py b/jsondoc/models/block/types/numbered_list_item/__init__.py index 5a88ccb..8da38c5 100644 --- a/jsondoc/models/block/types/numbered_list_item/__init__.py +++ b/jsondoc/models/block/types/numbered_list_item/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -18,7 +18,7 @@ class NumberedListItem(BaseModel): extra='forbid', arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None diff --git a/jsondoc/models/block/types/paragraph/__init__.py b/jsondoc/models/block/types/paragraph/__init__.py index 72d073b..1954135 100644 --- a/jsondoc/models/block/types/paragraph/__init__.py +++ b/jsondoc/models/block/types/paragraph/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -18,7 +18,7 @@ class Paragraph(BaseModel): extra='forbid', arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None diff --git a/jsondoc/models/block/types/quote/__init__.py b/jsondoc/models/block/types/quote/__init__.py index 7078254..cf1801a 100644 --- a/jsondoc/models/block/types/quote/__init__.py +++ b/jsondoc/models/block/types/quote/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -18,7 +18,7 @@ class Quote(BaseModel): extra='forbid', arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None diff --git a/jsondoc/models/block/types/rich_text/__init__.py b/jsondoc/models/block/types/rich_text/__init__.py index c82dc69..f6a71ac 100644 --- a/jsondoc/models/block/types/rich_text/__init__.py +++ b/jsondoc/models/block/types/rich_text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/base/__init__.py b/jsondoc/models/block/types/rich_text/base/__init__.py index ce4a93a..c610350 100644 --- a/jsondoc/models/block/types/rich_text/base/__init__.py +++ b/jsondoc/models/block/types/rich_text/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/equation/__init__.py b/jsondoc/models/block/types/rich_text/equation/__init__.py index 1f948f5..f454aed 100644 --- a/jsondoc/models/block/types/rich_text/equation/__init__.py +++ b/jsondoc/models/block/types/rich_text/equation/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/rich_text/text/__init__.py b/jsondoc/models/block/types/rich_text/text/__init__.py index 630bbec..4290492 100644 --- a/jsondoc/models/block/types/rich_text/text/__init__.py +++ b/jsondoc/models/block/types/rich_text/text/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table/__init__.py b/jsondoc/models/block/types/table/__init__.py index c181fa8..9b59aa9 100644 --- a/jsondoc/models/block/types/table/__init__.py +++ b/jsondoc/models/block/types/table/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/table_row/__init__.py b/jsondoc/models/block/types/table_row/__init__.py index f5cac2b..bbded9f 100644 --- a/jsondoc/models/block/types/table_row/__init__.py +++ b/jsondoc/models/block/types/table_row/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/block/types/to_do/__init__.py b/jsondoc/models/block/types/to_do/__init__.py index bbbc2aa..14441bf 100644 --- a/jsondoc/models/block/types/to_do/__init__.py +++ b/jsondoc/models/block/types/to_do/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -18,7 +18,7 @@ class ToDo(BaseModel): extra='forbid', arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] checked: bool color: Optional[Color] = None diff --git a/jsondoc/models/block/types/toggle/__init__.py b/jsondoc/models/block/types/toggle/__init__.py index ca055a0..6eeda70 100644 --- a/jsondoc/models/block/types/toggle/__init__.py +++ b/jsondoc/models/block/types/toggle/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:27+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations @@ -17,7 +17,7 @@ class Toggle(BaseModel): model_config = ConfigDict( arbitrary_types_allowed=True, ) - rich_text: List[RichTextBase] + rich_text: Optional[List[RichTextBase]] = [] color: Optional[Color] = None diff --git a/jsondoc/models/file/__init__.py b/jsondoc/models/file/__init__.py index 5605d7d..d485070 100644 --- a/jsondoc/models/file/__init__.py +++ b/jsondoc/models/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/base/__init__.py b/jsondoc/models/file/base/__init__.py index 1da9957..b17455c 100644 --- a/jsondoc/models/file/base/__init__.py +++ b/jsondoc/models/file/base/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/external/__init__.py b/jsondoc/models/file/external/__init__.py index ff2abd4..bf00a91 100644 --- a/jsondoc/models/file/external/__init__.py +++ b/jsondoc/models/file/external/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/file/file/__init__.py b/jsondoc/models/file/file/__init__.py index a06fa9b..410b2f7 100644 --- a/jsondoc/models/file/file/__init__.py +++ b/jsondoc/models/file/file/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/page/__init__.py b/jsondoc/models/page/__init__.py index bc66e72..ffb67fa 100644 --- a/jsondoc/models/page/__init__.py +++ b/jsondoc/models/page/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/models/shared_definitions/__init__.py b/jsondoc/models/shared_definitions/__init__.py index 52805b7..b952d34 100644 --- a/jsondoc/models/shared_definitions/__init__.py +++ b/jsondoc/models/shared_definitions/__init__.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: example.json -# timestamp: 2024-09-08T11:45:26+00:00 +# timestamp: 2024-09-10T15:45:21+00:00 from __future__ import annotations diff --git a/jsondoc/serialize.py b/jsondoc/serialize.py index 250fd0f..093896e 100644 --- a/jsondoc/serialize.py +++ b/jsondoc/serialize.py @@ -241,8 +241,10 @@ def load_page(obj: Union[str, Dict[str, Any]]) -> Page: @validate_call -def load_jsondoc(obj: Union[str, Dict[str, Any]]) -> Page | BlockBase: - if isinstance(obj, str): +def load_jsondoc(obj: Union[str, Dict[str, Any], List[Dict[str, Any]]]) -> Page | BlockBase: + if isinstance(obj, list): + return [load_jsondoc(block) for block in obj] + elif isinstance(obj, str): obj = json.loads(obj) object_ = obj.get("object") diff --git a/jsondoc/utils.py b/jsondoc/utils.py index c0fcc6f..4bc1fae 100644 --- a/jsondoc/utils.py +++ b/jsondoc/utils.py @@ -1,3 +1,4 @@ +from datetime import datetime, timezone import difflib import json import logging @@ -16,6 +17,10 @@ def generate_id() -> str: return str(uuid.uuid4()) +def get_current_time() -> datetime: + return datetime.now(tz=timezone.utc) + + def replace_refs_with_arbitrary_object(data): if isinstance(data, dict): if "$ref" in data: @@ -120,12 +125,17 @@ def set_dict_recursive(d: dict | list, key: str, value: str): """ Set all values that match a key in a nested dictionary or list. """ - for k, v in d.items(): - if isinstance(v, dict): - set_dict_recursive(v, key, value) - elif isinstance(v, list): - for item in v: - if isinstance(item, dict): - set_dict_recursive(item, key, value) - elif k == key: - d[k] = value + if isinstance(d, dict): + for k, v in d.items(): + if isinstance(v, dict): + set_dict_recursive(v, key, value) + elif isinstance(v, list): + for item in v: + if isinstance(item, dict): + set_dict_recursive(item, key, value) + elif k == key: + d[k] = value + elif isinstance(d, list): + for item in d: + if isinstance(item, dict): + set_dict_recursive(item, key, value) diff --git a/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json b/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json index 633af45..d064a13 100644 --- a/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json +++ b/schema/block/types/bulleted_list_item/bulleted_list_item_schema.json @@ -18,7 +18,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/schema/block/types/code/code_schema.json b/schema/block/types/code/code_schema.json index c3bdb29..d0afc9a 100644 --- a/schema/block/types/code/code_schema.json +++ b/schema/block/types/code/code_schema.json @@ -23,7 +23,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "language": { "type": "string", diff --git a/schema/block/types/heading_1/heading_1_schema.json b/schema/block/types/heading_1/heading_1_schema.json index a973e84..1b45d67 100644 --- a/schema/block/types/heading_1/heading_1_schema.json +++ b/schema/block/types/heading_1/heading_1_schema.json @@ -17,7 +17,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/schema/block/types/heading_2/heading_2_schema.json b/schema/block/types/heading_2/heading_2_schema.json index abdfa61..9f07437 100644 --- a/schema/block/types/heading_2/heading_2_schema.json +++ b/schema/block/types/heading_2/heading_2_schema.json @@ -17,7 +17,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/schema/block/types/heading_3/heading_3_schema.json b/schema/block/types/heading_3/heading_3_schema.json index 52bab87..04de8c7 100644 --- a/schema/block/types/heading_3/heading_3_schema.json +++ b/schema/block/types/heading_3/heading_3_schema.json @@ -17,7 +17,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/schema/block/types/numbered_list_item/numbered_list_item_schema.json b/schema/block/types/numbered_list_item/numbered_list_item_schema.json index 25c4d24..262b9a4 100644 --- a/schema/block/types/numbered_list_item/numbered_list_item_schema.json +++ b/schema/block/types/numbered_list_item/numbered_list_item_schema.json @@ -18,7 +18,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/schema/block/types/paragraph/paragraph_schema.json b/schema/block/types/paragraph/paragraph_schema.json index f9c570d..4bbc446 100644 --- a/schema/block/types/paragraph/paragraph_schema.json +++ b/schema/block/types/paragraph/paragraph_schema.json @@ -17,7 +17,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/schema/block/types/quote/quote_schema.json b/schema/block/types/quote/quote_schema.json index a2fcd89..b6a2513 100644 --- a/schema/block/types/quote/quote_schema.json +++ b/schema/block/types/quote/quote_schema.json @@ -17,7 +17,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/schema/block/types/to_do/to_do_schema.json b/schema/block/types/to_do/to_do_schema.json index 5f02b54..d67a2cd 100644 --- a/schema/block/types/to_do/to_do_schema.json +++ b/schema/block/types/to_do/to_do_schema.json @@ -18,7 +18,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "checked": { "type": "boolean" diff --git a/schema/block/types/toggle/toggle_schema.json b/schema/block/types/toggle/toggle_schema.json index d0180f7..9d1ab43 100644 --- a/schema/block/types/toggle/toggle_schema.json +++ b/schema/block/types/toggle/toggle_schema.json @@ -17,7 +17,8 @@ "type": "array", "items": { "$ref": "/block/types/rich_text/rich_text_schema.json" - } + }, + "default": [] }, "color": { "$ref": "/shared_definitions/shared_definitions_schema.json#/$defs/color" diff --git a/tests/html_jsondoc_pairs/ex05_basic_br.json b/tests/html_jsondoc_pairs/ex05_basic_br.json new file mode 100644 index 0000000..395c792 --- /dev/null +++ b/tests/html_jsondoc_pairs/ex05_basic_br.json @@ -0,0 +1,42 @@ +{ + "html": "

      This is the first line.
      This is the second line.

      ", + "jsondoc": [ + { + "object": "block", + "id": "3bbab45c-f5b2-4809-b39f-2f63b4304726", + "type": "paragraph", + "created_time": "2024-09-10T16:03:20.216781Z", + "has_children": false, + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is the first line." + }, + "annotations": {}, + "plain_text": "This is the first line." + } + ] + } + }, + { + "object": "block", + "id": "0f6f7f81-4690-4e9b-82fc-66232ef38d86", + "type": "paragraph", + "created_time": "2024-09-10T16:03:20.216170Z", + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is the second line." + }, + "annotations": {}, + "plain_text": "This is the second line." + } + ] + } + } + ] +} diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index a7f806d..187d1fc 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -83,8 +83,10 @@ def test_examples(): json_files = [f for f in os.listdir(html_jsondoc_pairs_dir) if f.endswith(".json")] for json_file in json_files: + print(f"Processing {json_file}...", end="") json_path = html_jsondoc_pairs_dir / json_file _process_example(json_path) + print("PASS") if __name__ == "__main__": From 914f23ca5c04789ef7027d5a347d6a898b04b2d6 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 11 Sep 2024 00:00:11 +0200 Subject: [PATCH 28/45] Add
        and
          support --- docs/html-conversion.md | 7 +- jsondoc/convert/html.py | 71 +++++--------- jsondoc/convert/utils.py | 53 ++++++++++- tests/html_jsondoc_pairs/ex06_basic_ul.json | 100 ++++++++++++++++++++ tests/html_jsondoc_pairs/ex07_basic_ol.json | 100 ++++++++++++++++++++ 5 files changed, 279 insertions(+), 52 deletions(-) create mode 100644 tests/html_jsondoc_pairs/ex06_basic_ul.json create mode 100644 tests/html_jsondoc_pairs/ex07_basic_ol.json diff --git a/docs/html-conversion.md b/docs/html-conversion.md index 2937b7c..30663d4 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -63,6 +63,7 @@ def process_tag(node): ## Remaining tasks -- Convert lists `
            ` and `
              ` to `ListBlock` -- Convert line breaks `
              ` to `ParagraphBlock` -- Add sup/sub annotation? \ No newline at end of file +- [x] Convert lists `
                ` and `
                  ` +- [x] Convert line breaks `
                  ` +- [ ] Convert `
` and `
` +- [ ] Add sup/sub annotation? \ No newline at end of file diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index f8c3e43..905b9ca 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -7,12 +7,14 @@ BreakElementPlaceholderBlock, append_to_parent_block, append_to_rich_text, + create_bullet_list_item_block, create_code_block, create_divider_block, create_h1_block, create_h2_block, create_h3_block, create_image_block, + create_numbered_list_item_block, create_paragraph_block, create_quote_block, create_rich_text, @@ -279,7 +281,7 @@ def reconcile_to_block( init_kwargs = { "id": generate_id(), "created_time": child.created_time, - block_type: type(block_field)() + block_type: type(block_field)(), } empty_block = type(block)(**init_kwargs) @@ -370,7 +372,6 @@ def process_tag( Convert a BeautifulSoup node to JSON-DOC. Recurses through the children nodes and converts them to JSON-DOC corresponding current block type can have children or not. - """ # text = "" objects = [] @@ -426,8 +427,9 @@ def is_nested_node(el): continue elif isinstance(el, NavigableString): # text += self.process_text(el) - children_objects.append(self.process_text(el)) - pass + processed_text = self.process_text(el) + if processed_text: + children_objects.append(processed_text) else: # text += self.process_tag(el, convert_children_as_inline) new_objects = self.process_tag(el, convert_children_as_inline) @@ -441,6 +443,7 @@ def is_nested_node(el): current_level_object = convert_fn(node, convert_as_inline) # print(node, repr(current_level_object)) + # import ipdb; ipdb.set_trace() if current_level_object is None: objects = children_objects @@ -474,6 +477,11 @@ def process_text(self, el): ): text = text.rstrip() + # Strip preceding and trailing only newlines + text = text.strip("\n") + if len(text) == 0: + return None + return text # return create_rich_text(text=text) @@ -579,14 +587,9 @@ def convert_blockquote(self, el, convert_as_inline): return create_quote_block() def convert_br(self, el, convert_as_inline): - # if convert_as_inline: - # return "" + if convert_as_inline: + return None - # if self.options["newline_style"].lower() == BACKSLASH: - # return "\\\n" - # else: - # return " \n" - # return None # TBD return BreakElementPlaceholderBlock(id="", created_time=get_current_time()) def convert_code(self, el, convert_as_inline): @@ -695,46 +698,21 @@ def convert_img(self, el, convert_as_inline): return create_image_block(url=src, caption=alt) def convert_list(self, el, convert_as_inline): - - # # Converting a list to inline is undefined. - # # Ignoring convert_to_inline for list. - - # nested = False - # before_paragraph = False - # if el.next_sibling and el.next_sibling.name not in ["ul", "ol"]: - # before_paragraph = True - # while el: - # if el.name == "li": - # nested = True - # break - # el = el.parent - # if nested: - # # remove trailing newline if nested - # return "\n" + self.indent(text, 1).rstrip() - # return text + ("\n" if before_paragraph else "") - return None # TBD + """ + This is applied to
    and
      tags. We simply return None, because + there is no need for a container block for list items in JSON-DOC. + """ + return None convert_ul = convert_list convert_ol = convert_list def convert_li(self, el, convert_as_inline): - # parent = el.parent - # if parent is not None and parent.name == "ol": - # if parent.get("start") and str(parent.get("start")).isnumeric(): - # start = int(parent.get("start")) - # else: - # start = 1 - # bullet = "%s." % (start + parent.index(el)) - # else: - # depth = -1 - # while el: - # if el.name == "ul": - # depth += 1 - # el = el.parent - # bullets = self.options["bullets"] - # bullet = bullets[depth % len(bullets)] - # return "%s %s\n" % (bullet, (text or "").strip()) - return None # TBD + parent = el.parent + if parent is not None and parent.name == "ol": + return create_numbered_list_item_block() + else: + return create_bullet_list_item_block() def convert_p(self, el, convert_as_inline): # text = el.get_text() @@ -830,7 +808,6 @@ def convert_th(self, el, convert_as_inline): """ Table header cell """ - # TBD: Somehow convey header info to table block return create_paragraph_block() def convert_tr(self, el, convert_as_inline): diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 0109d6e..76483b9 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -6,7 +6,10 @@ from pydantic import validate_call from jsondoc.models.block.base import BlockBase -from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock +from jsondoc.models.block.types.bulleted_list_item import ( + BulletedListItem, + BulletedListItemBlock, +) from jsondoc.models.block.types.code import Code, CodeBlock, Language from jsondoc.models.block.types.column import ColumnBlock from jsondoc.models.block.types.column_list import ColumnListBlock @@ -18,7 +21,7 @@ from jsondoc.models.block.types.image import ImageBlock from jsondoc.models.block.types.image.external_image import ExternalImage from jsondoc.models.block.types.image.file_image import FileImage -from jsondoc.models.block.types.numbered_list_item import NumberedListItemBlock +from jsondoc.models.block.types.numbered_list_item import NumberedListItem, NumberedListItemBlock from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock from jsondoc.models.block.types.quote import Quote, QuoteBlock from jsondoc.models.block.types.rich_text.base import RichTextBase @@ -129,6 +132,52 @@ def create_paragraph_block( ) +def create_bullet_list_item_block( + text: str | None = None, + id: str | None = None, + created_time=None, + **kwargs, +) -> BulletedListItemBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = get_current_time() + + rich_text = [] + if text is not None: + rich_text.append(create_rich_text(text, **kwargs)) + + return BulletedListItemBlock( + id=id, + created_time=created_time, + bulleted_list_item=BulletedListItem(rich_text=rich_text), + has_children=False, + ) + + +def create_numbered_list_item_block( + text: str | None = None, + id: str | None = None, + created_time=None, + **kwargs, +) -> NumberedListItemBlock: + if id is None: + id = generate_id() + if created_time is None: + created_time = get_current_time() + + rich_text = [] + if text is not None: + rich_text.append(create_rich_text(text, **kwargs)) + + return NumberedListItemBlock( + id=id, + created_time=created_time, + numbered_list_item=NumberedListItem(rich_text=rich_text), + has_children=False, + ) + + def create_code_block( code: str | None = None, language: str | None = None, diff --git a/tests/html_jsondoc_pairs/ex06_basic_ul.json b/tests/html_jsondoc_pairs/ex06_basic_ul.json new file mode 100644 index 0000000..827e1af --- /dev/null +++ b/tests/html_jsondoc_pairs/ex06_basic_ul.json @@ -0,0 +1,100 @@ +{ + "html": "
      • Apples
      • Bananas
      • Milk
      • Bread
      • Eggs
      ", + "jsondoc": [ + { + "object": "block", + "id": "76686868-1db8-463f-93f0-a651ba5aed85", + "type": "bulleted_list_item", + "created_time": "2024-09-10T21:45:51.602464Z", + "has_children": false, + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Apples" + }, + "annotations": {}, + "plain_text": "Apples" + } + ] + } + }, + { + "object": "block", + "id": "1a6ad38d-dd94-42ca-af19-612cfa859e57", + "type": "bulleted_list_item", + "created_time": "2024-09-10T21:45:52.401377Z", + "has_children": false, + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Bananas" + }, + "annotations": {}, + "plain_text": "Bananas" + } + ] + } + }, + { + "object": "block", + "id": "1bd29dba-7cbf-42f2-8e68-d83333856537", + "type": "bulleted_list_item", + "created_time": "2024-09-10T21:45:52.665542Z", + "has_children": false, + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Milk" + }, + "annotations": {}, + "plain_text": "Milk" + } + ] + } + }, + { + "object": "block", + "id": "205c98eb-b7a0-42b4-8eea-f8dc3849d338", + "type": "bulleted_list_item", + "created_time": "2024-09-10T21:45:52.911620Z", + "has_children": false, + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Bread" + }, + "annotations": {}, + "plain_text": "Bread" + } + ] + } + }, + { + "object": "block", + "id": "15ce4101-06ed-4628-849b-976232d9c3dd", + "type": "bulleted_list_item", + "created_time": "2024-09-10T21:45:53.167066Z", + "has_children": false, + "bulleted_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Eggs" + }, + "annotations": {}, + "plain_text": "Eggs" + } + ] + } + } + ] +} diff --git a/tests/html_jsondoc_pairs/ex07_basic_ol.json b/tests/html_jsondoc_pairs/ex07_basic_ol.json new file mode 100644 index 0000000..20d4d10 --- /dev/null +++ b/tests/html_jsondoc_pairs/ex07_basic_ol.json @@ -0,0 +1,100 @@ +{ + "html": "
      1. Apples
      2. Bananas
      3. Milk
      4. Bread
      5. Eggs
      ", + "jsondoc": [ + { + "object": "block", + "id": "76686868-1db8-463f-93f0-a651ba5aed85", + "type": "numbered_list_item", + "created_time": "2024-09-10T21:45:51.602464Z", + "has_children": false, + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Apples" + }, + "annotations": {}, + "plain_text": "Apples" + } + ] + } + }, + { + "object": "block", + "id": "1a6ad38d-dd94-42ca-af19-612cfa859e57", + "type": "numbered_list_item", + "created_time": "2024-09-10T21:45:52.401377Z", + "has_children": false, + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Bananas" + }, + "annotations": {}, + "plain_text": "Bananas" + } + ] + } + }, + { + "object": "block", + "id": "1bd29dba-7cbf-42f2-8e68-d83333856537", + "type": "numbered_list_item", + "created_time": "2024-09-10T21:45:52.665542Z", + "has_children": false, + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Milk" + }, + "annotations": {}, + "plain_text": "Milk" + } + ] + } + }, + { + "object": "block", + "id": "205c98eb-b7a0-42b4-8eea-f8dc3849d338", + "type": "numbered_list_item", + "created_time": "2024-09-10T21:45:52.911620Z", + "has_children": false, + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Bread" + }, + "annotations": {}, + "plain_text": "Bread" + } + ] + } + }, + { + "object": "block", + "id": "15ce4101-06ed-4628-849b-976232d9c3dd", + "type": "numbered_list_item", + "created_time": "2024-09-10T21:45:53.167066Z", + "has_children": false, + "numbered_list_item": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "Eggs" + }, + "annotations": {}, + "plain_text": "Eggs" + } + ] + } + } + ] +} From 3b5d92de2b9afc362580b32efd994c3306ef891e Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 11 Sep 2024 14:51:28 +0200 Subject: [PATCH 29/45] Implement create_page() --- docs/html-conversion.md | 3 ++ jsondoc/convert/html.py | 87 +++++++++++++++++++++++++++++++---- jsondoc/convert/utils.py | 75 ++++++++++++++++++++++++++++-- tests/test_html_to_jsondoc.py | 2 +- 4 files changed, 153 insertions(+), 14 deletions(-) diff --git a/docs/html-conversion.md b/docs/html-conversion.md index 30663d4..cb8f1a3 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -66,4 +66,7 @@ def process_tag(node): - [x] Convert lists `
        ` and `
          ` - [x] Convert line breaks `
          ` - [ ] Convert `
` and `
` +- [ ] Table cells with colspan/rowspan +- [ ] Residual strings or newlines in the final output list +- [ ] Force_page=true - [ ] Add sup/sub annotation? \ No newline at end of file diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 905b9ca..aa71efc 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -15,13 +15,14 @@ create_h3_block, create_image_block, create_numbered_list_item_block, + create_page, create_paragraph_block, create_quote_block, create_rich_text, create_table_block, create_table_row_block, get_rich_text_from_block, - table_has_header_row, + html_table_has_header_row, try_append_rich_text_to_block, ) from jsondoc.models.block.base import BlockBase @@ -352,16 +353,30 @@ def convert(self, html: str | bytes) -> Page | BlockBase | List[BlockBase]: return self.convert_soup(soup) def convert_soup( - self, soup, force_page=False + self, soup: BeautifulSoup, force_page=False ) -> Page | BlockBase | List[BlockBase]: - ret = self.process_tag(soup, convert_as_inline=False, children_only=True) - if isinstance(ret, list): - # return - # TODO: create a page and add all the blocks to it if force_page = True - # pass - if len(ret) == 1: - ret = ret[0] + children = self.process_tag(soup, convert_as_inline=False, children_only=True) + + is_page = self._is_soup_page(soup) + + ret = None + if is_page or force_page: + title = self._get_html_title(soup) + # Ensure that children is a list + if not isinstance(children, list): + children = [children] + + # Create a page and add all the blocks to it + ret = create_page( + title=title, + children=children, + ) + else: + ret = children + if isinstance(ret, list): + if len(ret) == 1: + ret = ret[0] return ret @@ -458,6 +473,58 @@ def is_nested_node(el): return objects + @staticmethod + def _get_html_title(soup: BeautifulSoup) -> str | None: + """ + Extracts the title from the HTML document. + + :param soup: BeautifulSoup object of the HTML document + :return: The title string or None if not found + """ + title_tag = soup.find("title") + if title_tag and title_tag.string: + return title_tag.string.strip() + + # If no title tag, check for the first h1 + h1_tag = soup.find("h1") + if h1_tag: + return h1_tag.get_text(strip=True) + + return None + + @staticmethod + def _is_soup_page(soup: BeautifulSoup) -> bool: + """ + Determines if the BeautifulSoup object represents a JSON-DOC page. + + :param soup: BeautifulSoup object + :return: Boolean indicating if it's a JSON-DOC page + """ + # Check for DOCTYPE + if not soup.contents or not isinstance(soup.contents[0], Doctype): + return False + + # Check for tag + html_tag = soup.find("html") + if not html_tag: + return False + + # # Check for and tags + # head_tag = soup.find('head') + # body_tag = soup.find('body') + # if not (head_tag and body_tag): + # return False + + # # Check for essential head elements + # if not (head_tag.find('title') or head_tag.find('meta')): + # return False + + # # Check if body has some content + # if not body_tag.contents: + # return False + + return True + def process_text(self, el): text = str(el) or "" @@ -780,7 +847,7 @@ def convert_style(self, el, convert_as_inline): def convert_table(self, el, convert_as_inline): # return "\n\n" + text + "\n" - has_column_header = table_has_header_row(el) + has_column_header = html_table_has_header_row(el) return create_table_block( has_column_header=has_column_header, ) diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 76483b9..62348e7 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -21,7 +21,10 @@ from jsondoc.models.block.types.image import ImageBlock from jsondoc.models.block.types.image.external_image import ExternalImage from jsondoc.models.block.types.image.file_image import FileImage -from jsondoc.models.block.types.numbered_list_item import NumberedListItem, NumberedListItemBlock +from jsondoc.models.block.types.numbered_list_item import ( + NumberedListItem, + NumberedListItemBlock, +) from jsondoc.models.block.types.paragraph import Paragraph, ParagraphBlock from jsondoc.models.block.types.quote import Quote, QuoteBlock from jsondoc.models.block.types.rich_text.base import RichTextBase @@ -33,6 +36,7 @@ from jsondoc.models.block.types.to_do import ToDoBlock from jsondoc.models.block.types.toggle import ToggleBlock from jsondoc.models.file.external import External +from jsondoc.models.page import CreatedBy, LastEditedBy, Page, Parent, Properties, Title from jsondoc.models.shared_definitions import Annotations from jsondoc.rules import is_block_child_allowed from jsondoc.utils import generate_id, get_current_time @@ -308,6 +312,7 @@ def create_image_block( ) -> ImageBlock: if id is None: id = generate_id() + if created_time is None: created_time = get_current_time() @@ -334,6 +339,7 @@ def create_quote_block( ) -> QuoteBlock: if id is None: id = generate_id() + if created_time is None: created_time = get_current_time() @@ -356,6 +362,7 @@ def create_table_row_block( ) -> TableRowBlock: if id is None: id = generate_id() + if created_time is None: created_time = get_current_time() @@ -377,6 +384,7 @@ def create_table_block( ) -> TableBlock: if id is None: id = generate_id() + if created_time is None: created_time = get_current_time() @@ -392,6 +400,67 @@ def create_table_block( ) +def create_page( + id: str | None = None, + created_time=None, + created_by: str | None = None, + last_edited_time: datetime | None = None, + last_edited_by: str | None = None, + children: List[BlockBase] = [], + title: str | List[RichTextBase] | None = None, + archived: bool | None = None, + in_trash: bool | None = None, + # parent: str | None = None, + # icon # TBD +) -> Page: + """ + Creates a page with the given blocks + """ + if id is None: + id = generate_id() + + if created_time is None: + created_time = get_current_time() + + created_by_ = None + if created_by is not None: + created_by_ = CreatedBy(id=created_by) + + last_edited_by_ = None + if last_edited_by is not None: + last_edited_by_ = LastEditedBy(id=last_edited_by) + + if last_edited_time is not None: + # Ensure that it has timezone information + if last_edited_time.tzinfo is None: + raise ValueError("last_edited_time must be timezone-aware") + + title_ = None + if title is not None: + # Create rich text if title is a string + if isinstance(title, str): + title = [create_rich_text(title)] + + title_ = Title(title=title) + + properties = Properties(title=title_) + + # if parent is not None: + # parent_ = Parent(type="page_id", page_id=parent) + + return Page( + id=id, + created_time=created_time, + created_by=created_by_, + last_edited_time=last_edited_time, + last_edited_by=last_edited_by_, + children=children, + properties=properties, + archived=archived, + in_trash=in_trash, + ) + + def get_rich_text_from_block(block: BlockBase) -> List[RichTextBase] | None: """ Returns the rich text of a block @@ -460,9 +529,9 @@ def append_to_parent_block(parent: BlockBase, child: BlockBase) -> bool: return True -def table_has_header_row(table: Tag) -> bool: +def html_table_has_header_row(table: Tag) -> bool: """ - Check if a table has a header row. + Check if an HTML table has a header row. """ # Check if there's a thead element if table.find("thead"): diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index 187d1fc..fd9d2bd 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -90,5 +90,5 @@ def test_examples(): if __name__ == "__main__": - test_examples() + # test_examples() test_convert_html_all_elements() From 75363de790bcdfbff164bca471e56a767546b744 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:42:01 +0200 Subject: [PATCH 30/45] Handle table captions --- docs/differences-from-notion.md | 7 +- docs/html-conversion.md | 8 + jsondoc/bin/convert_jsondoc.py | 1 + jsondoc/convert/html.py | 150 ++++++++---- jsondoc/convert/markdown.py | 4 +- jsondoc/convert/utils.py | 148 +++++++++++- .../ex06_caption_table.json | 214 ++++++++++++++++++ ...{ex06_basic_ul.json => ex07_basic_ul.json} | 0 ...{ex07_basic_ol.json => ex08_basic_ol.json} | 0 tests/test_html_to_jsondoc.py | 8 +- 10 files changed, 483 insertions(+), 57 deletions(-) create mode 100644 tests/html_jsondoc_pairs/ex06_caption_table.json rename tests/html_jsondoc_pairs/{ex06_basic_ul.json => ex07_basic_ul.json} (100%) rename tests/html_jsondoc_pairs/{ex07_basic_ol.json => ex08_basic_ol.json} (100%) diff --git a/docs/differences-from-notion.md b/docs/differences-from-notion.md index f5a6f09..2ce916c 100644 --- a/docs/differences-from-notion.md +++ b/docs/differences-from-notion.md @@ -2,4 +2,9 @@ - TBD: List which ones - Certain fields can be omitted, which would fall back to default values. - Metadata field is present in blocks. -- grep `// notion-diverge` \ No newline at end of file +- grep `// notion-diverge` + + +## Ideas + +- Let TableBlocks have caption? In Notion, they cannot have captions. \ No newline at end of file diff --git a/docs/html-conversion.md b/docs/html-conversion.md index cb8f1a3..97922cc 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -60,6 +60,14 @@ def process_tag(node): return return_objects ``` +## Placeholder blocks + +Some HTML elements are not guaranteed to be converted to a JSON-DOC block: + +- For example, in JSON-DOC, images can have captions, but tables cannot. So HTML `
` elements needs to be handled separately. +- HTML `
` elements do not resolve to a JSON-DOC block, but instead trigger a split in a parent block which can contain rich text. + +To conditionally handle these elements, we create a corresponding placeholder block and handle them in various ways while the tree is being processed. ## Remaining tasks diff --git a/jsondoc/bin/convert_jsondoc.py b/jsondoc/bin/convert_jsondoc.py index 2d6b4e1..fdc50e2 100644 --- a/jsondoc/bin/convert_jsondoc.py +++ b/jsondoc/bin/convert_jsondoc.py @@ -36,6 +36,7 @@ def convert_to_jsondoc(input_file, output_file=None, indent=None): else: # Print to terminal print(serialized_jsondoc) + # import ipdb; ipdb.set_trace() # print(jsondoc_to_markdown(jsondoc)) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index aa71efc..1af82b1 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -1,13 +1,20 @@ +from types import NoneType from typing import List, Union from bs4 import BeautifulSoup, NavigableString, Comment, Doctype from textwrap import fill import re +from pydantic import BaseModel + from jsondoc.convert.utils import ( BreakElementPlaceholderBlock, + CaptionPlaceholderBlock, + CellPlaceholderBlock, append_to_parent_block, append_to_rich_text, + block_supports_rich_text, create_bullet_list_item_block, + create_cell_placeholder_block, create_code_block, create_divider_block, create_h1_block, @@ -22,8 +29,9 @@ create_table_block, create_table_row_block, get_rich_text_from_block, + run_final_block_checks, html_table_has_header_row, - try_append_rich_text_to_block, + append_rich_text_to_block, ) from jsondoc.models.block.base import BlockBase from jsondoc.models.block.types.bulleted_list_item import BulletedListItemBlock @@ -77,6 +85,12 @@ RICH_TEXT_TYPE = Union[RichTextBase, RichTextEquation] +class ConvertOutput(BaseModel): + main_object: BlockBase | RichTextBase + prev_objects: List[BlockBase | RichTextBase] = [] + next_objects: List[BlockBase | RichTextBase] = [] + + def chomp(text): """ If the text in an inline tag like b, a, or em contains a leading or trailing @@ -115,13 +129,13 @@ def implementation(self, el, convert_as_inline): annotations = markup_fn(self) if el.find_parent(["pre", "code", "kbd", "samp"]): - return create_rich_text() + return ConvertOutput(main_object=create_rich_text()) # prefix, suffix, text = chomp(text) # if not text: # return None - return create_rich_text(annotations=annotations) + return ConvertOutput(main_object=create_rich_text(annotations=annotations)) # return [ # # create_rich_text(text=prefix), @@ -177,6 +191,9 @@ def apply_parent_annotations( def apply_annotations_to_block(annotations_to_apply: Annotations, block: BlockBase): + """ + Recursively applies the annotations to the block and its children + """ if hasattr(block, "children") and isinstance(block.children, list): for child in block.children: apply_annotations_to_block(annotations_to_apply, child) @@ -188,8 +205,9 @@ def apply_annotations_to_block(annotations_to_apply: Annotations, block: BlockBa apply_parent_annotations(annotations_to_apply, rich_text.annotations) -def append_paragraph_block_to_table_row_block( - parent_table_row_block: TableRowBlock, child_paragraph_block: ParagraphBlock +def append_caption_block_to_table_row_block( + parent_table_row_block: TableRowBlock, + child_paragraph_block: CellPlaceholderBlock, ): """ Appends a paragraph block to a table row block @@ -208,7 +226,7 @@ def append_paragraph_block_to_table_row_block( # Maps pairs of (parent_block_type, child_block_type) to a function # that appends the child block to the parent block OVERRIDE_APPEND_FUNCTIONS = { - (TableRowBlock, ParagraphBlock): append_paragraph_block_to_table_row_block, + (TableRowBlock, CellPlaceholderBlock): append_caption_block_to_table_row_block, } @@ -261,15 +279,19 @@ def reconcile_to_block( if isinstance(child, str): if current_rich_text is None: current_rich_text = create_rich_text() + append_to_rich_text(current_rich_text, child) - success_ = try_append_rich_text_to_block(block, current_rich_text) - if not success_: + + if block_supports_rich_text(block): + append_rich_text_to_block(block, current_rich_text) + else: remaining_children.append(current_rich_text) elif isinstance(child, RichTextBase): - success_ = try_append_rich_text_to_block(block, child) - current_rich_text = None - if not success_: + if block_supports_rich_text(block): + append_rich_text_to_block(block, child) + current_rich_text = None + else: remaining_children.append(child) elif isinstance(child, BreakElementPlaceholderBlock): @@ -357,7 +379,7 @@ def convert_soup( ) -> Page | BlockBase | List[BlockBase]: children = self.process_tag(soup, convert_as_inline=False, children_only=True) - + children = run_final_block_checks(children) is_page = self._is_soup_page(soup) ret = None @@ -391,8 +413,7 @@ def process_tag( # text = "" objects = [] - # markdown headings or cells can't include - # block elements (elements w/newlines) + # Headings or cells can't include block elements (elements w/newlines) is_heading = html_heading_re.match(node.name) is not None is_cell = node.name in ["td", "th"] convert_children_as_inline = convert_as_inline @@ -451,11 +472,21 @@ def is_nested_node(el): children_objects += new_objects current_level_object = None + current_level_prev_objects = [] + current_level_next_objects = [] if not children_only: convert_fn = getattr(self, "convert_%s" % node.name, None) if convert_fn and self.should_convert_tag(node.name): # text = convert_fn(node, text, convert_as_inline) - current_level_object = convert_fn(node, convert_as_inline) + # current_level_object = convert_fn(node, convert_as_inline) + convert_output = convert_fn(node, convert_as_inline) + assert isinstance( + convert_output, (ConvertOutput, NoneType) + ), f"Convert function {convert_fn} must return a ConvertOutput or None" + if convert_output is not None: + current_level_object = convert_output.main_object + current_level_prev_objects = convert_output.prev_objects + current_level_next_objects = convert_output.next_objects # print(node, repr(current_level_object)) # import ipdb; ipdb.set_trace() @@ -471,6 +502,7 @@ def is_nested_node(el): f"Current node has yielded an unexpected type {type(current_level_object)}" ) + objects = current_level_prev_objects + objects + current_level_next_objects return objects @staticmethod @@ -626,7 +658,7 @@ def convert_a(self, el, convert_as_inline): # create_rich_text(text=text, url=href), # create_rich_text(text=suffix), # ] - return create_rich_text(text=text, url=href) + return ConvertOutput(main_object=create_rich_text(text=text, url=href)) # return ( # "%s[%s](%s%s)%s" % (prefix, text, href, title_part, suffix) # if href @@ -651,13 +683,17 @@ def convert_blockquote(self, el, convert_as_inline): # return create_rich_text(text=text) # TODO: If text has newlines, split them and add 2, 3, ... lines as children - return create_quote_block() + return ConvertOutput(main_object=create_quote_block()) def convert_br(self, el, convert_as_inline): if convert_as_inline: return None - return BreakElementPlaceholderBlock(id="", created_time=get_current_time()) + return ConvertOutput( + main_object=BreakElementPlaceholderBlock( + id="", created_time=get_current_time() + ) + ) def convert_code(self, el, convert_as_inline): text = el.get_text() @@ -668,7 +704,7 @@ def convert_code(self, el, convert_as_inline): # ) # return converter(self, el, convert_as_inline) if el.parent.name == "pre": - return create_rich_text() + return ConvertOutput(main_object=create_rich_text()) converter = abstract_inline_conversion(lambda self: Annotations(code=True)) return converter(self, el, convert_as_inline) @@ -704,46 +740,46 @@ def convert_h1(self, el, convert_as_inline): if convert_as_inline: return create_rich_text() - return create_h1_block() + return ConvertOutput(main_object=create_h1_block()) def convert_h2(self, el, convert_as_inline): # text = el.get_text() if convert_as_inline: return create_rich_text() - return create_h2_block() + return ConvertOutput(main_object=create_h2_block()) def convert_h3(self, el, convert_as_inline): # text = el.get_text() if convert_as_inline: return create_rich_text() - return create_h3_block() + return ConvertOutput(main_object=create_h3_block()) def convert_h4(self, el, convert_as_inline): # text = el.get_text() if convert_as_inline: return create_rich_text() - return create_paragraph_block() + return ConvertOutput(main_object=create_paragraph_block()) def convert_h5(self, el, convert_as_inline): # text = el.get_text() if convert_as_inline: return create_rich_text() - return create_paragraph_block() + return ConvertOutput(main_object=create_paragraph_block()) def convert_h6(self, el, convert_as_inline): # text = el.get_text() if convert_as_inline: return create_rich_text() - return create_paragraph_block() + return ConvertOutput(main_object=create_paragraph_block()) def convert_hr(self, el, convert_as_inline): # return "\n\n---\n\n" - return create_divider_block() + return ConvertOutput(main_object=create_divider_block()) convert_i = convert_em @@ -762,7 +798,7 @@ def convert_img(self, el, convert_as_inline): # return alt # return "![%s](%s%s)" % (alt, src, title_part) - return create_image_block(url=src, caption=alt) + return ConvertOutput(main_object=create_image_block(url=src, caption=alt)) def convert_list(self, el, convert_as_inline): """ @@ -777,9 +813,9 @@ def convert_list(self, el, convert_as_inline): def convert_li(self, el, convert_as_inline): parent = el.parent if parent is not None and parent.name == "ol": - return create_numbered_list_item_block() + return ConvertOutput(main_object=create_numbered_list_item_block()) else: - return create_bullet_list_item_block() + return ConvertOutput(main_object=create_bullet_list_item_block()) def convert_p(self, el, convert_as_inline): # text = el.get_text() @@ -800,7 +836,7 @@ def convert_p(self, el, convert_as_inline): # rich_text=[RichTextText()], # ) # ) - return create_paragraph_block() + return ConvertOutput(main_object=create_paragraph_block()) def convert_pre(self, el, convert_as_inline): text = el.get_text() @@ -820,7 +856,9 @@ def convert_pre(self, el, convert_as_inline): if self.options["code_language_callback"]: code_language = self.options["code_language_callback"](el) or code_language - return create_code_block(code=text, language=code_language) + return ConvertOutput( + main_object=create_code_block(code=text, language=code_language) + ) def convert_script(self, el, convert_as_inline): return None @@ -848,13 +886,22 @@ def convert_style(self, el, convert_as_inline): def convert_table(self, el, convert_as_inline): # return "\n\n" + text + "\n" has_column_header = html_table_has_header_row(el) - return create_table_block( - has_column_header=has_column_header, + return ConvertOutput( + main_object=create_table_block( + has_column_header=has_column_header, + ) ) def convert_caption(self, el, convert_as_inline): # return text + "\n" - return None # TBD + return ConvertOutput( + main_object=CaptionPlaceholderBlock( + id="", + created_time=get_current_time(), + type="caption_placeholder", + rich_text=[], + ) + ) def convert_figcaption(self, el, convert_as_inline): # return "\n\n" + text + "\n\n" @@ -869,19 +916,40 @@ def convert_td(self, el, convert_as_inline): While paragraph block child is being reconciled to a table row block, paragraph_block.rich_text will be extracted to form table_row.cells. """ - return create_paragraph_block() + # Get colspan + colspan = el.get("colspan", 1) + # Get rowspan + # rowspan = el.get("rowspan", 1) + # We need to come up with a much different way to handle rowspan + + next_objects = [] + if colspan > 1: + next_objects = [create_cell_placeholder_block() for _ in range(colspan - 1)] + + return ConvertOutput( + main_object=create_cell_placeholder_block(), + next_objects=next_objects, + ) - def convert_th(self, el, convert_as_inline): - """ - Table header cell - """ - return create_paragraph_block() + convert_th = convert_td + # def convert_th(self, el, convert_as_inline): + # """ + # Table header cell + # """ + # return ConvertOutput( + # main_object=CellPlaceholderBlock( + # id="", + # created_time=get_current_time(), + # type="cell_placeholder", + # rich_text=[], + # ) + # ) def convert_tr(self, el, convert_as_inline): """ Table row """ - return create_table_row_block() + return ConvertOutput(main_object=create_table_row_block()) def html_to_jsondoc(html: str | bytes, **options) -> Page | BlockBase | List[BlockBase]: diff --git a/jsondoc/convert/markdown.py b/jsondoc/convert/markdown.py index bc04aa6..ae8a36a 100644 --- a/jsondoc/convert/markdown.py +++ b/jsondoc/convert/markdown.py @@ -110,7 +110,7 @@ def convert_tag(block, convert_as_inline): raise AttributeError(attr) @validate_call - def convert(self, obj: str | dict | BlockBase | Page) -> str: + def convert(self, obj: str | dict | BlockBase | List[BlockBase] | Page) -> str: if isinstance(obj, (str, dict)): jsondoc = load_jsondoc(obj) @@ -121,6 +121,8 @@ def convert(self, obj: str | dict | BlockBase | Page) -> str: return self.convert_page(jsondoc) elif isinstance(jsondoc, BlockBase): return self.convert_block(jsondoc, False) + elif isinstance(jsondoc, list): + return "\n\n".join(self.convert_block(block, False) for block in jsondoc) else: raise ValueError(f"Invalid object type: {type(jsondoc)}") diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 62348e7..1be25b9 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -1,6 +1,6 @@ import logging from datetime import datetime, timezone -from typing import List, Literal, Type +from typing import List, Literal, Optional, Type from bs4 import Tag from pydantic import validate_call @@ -46,8 +46,38 @@ class BreakElementPlaceholderBlock(BlockBase): type: Literal["break_element_placeholder"] = "break_element_placeholder" +class CaptionPlaceholderBlock(BlockBase): + type: Literal["caption_placeholder"] = "caption_placeholder" + rich_text: Optional[List[RichTextBase]] = None + + +class CellPlaceholderBlock(BlockBase): + type: Literal["cell_placeholder"] = "cell_placeholder" + rich_text: Optional[List[RichTextBase]] = None + + PLACEHOLDER_BLOCKS = [ BreakElementPlaceholderBlock, + CaptionPlaceholderBlock, + CellPlaceholderBlock, +] + +BLOCKS_WITH_RICH_TEXT: List[Type[BlockBase]] = [ + ParagraphBlock, + CodeBlock, + Heading1Block, + Heading2Block, + Heading3Block, + QuoteBlock, + BulletedListItemBlock, + NumberedListItemBlock, + ToDoBlock, + ToggleBlock, +] + +PLACEHOLDER_BLOCKS_WITH_RICH_TEXT: List[Type[BlockBase]] = [ + CaptionPlaceholderBlock, + CellPlaceholderBlock, ] @@ -461,47 +491,97 @@ def create_page( ) -def get_rich_text_from_block(block: BlockBase) -> List[RichTextBase] | None: +def create_cell_placeholder_block(): + return CellPlaceholderBlock( + id="", + created_time=get_current_time(), + type="cell_placeholder", + rich_text=[], + ) + + +def get_rich_text_from_block(block: BlockBase) -> List[RichTextBase]: """ - Returns the rich text of a block + Returns the rich text of a block. If the block does not support rich text, + it will raise a ValueError. If the rich text is not initialized, it will + initialize it to an empty list and return that. """ + if not block_supports_rich_text(block): + raise ValueError(f"Block of type {type(block)} does not support rich text") + ret = None if isinstance(block, ParagraphBlock): + if block.paragraph.rich_text is None: + block.paragraph.rich_text = [] ret = block.paragraph.rich_text elif isinstance(block, CodeBlock): + if block.code.rich_text is None: + block.code.rich_text = [] ret = block.code.rich_text elif isinstance(block, Heading1Block): + if block.heading_1.rich_text is None: + block.heading_1.rich_text = [] ret = block.heading_1.rich_text elif isinstance(block, Heading2Block): + if block.heading_2.rich_text is None: + block.heading_2.rich_text = [] ret = block.heading_2.rich_text elif isinstance(block, Heading3Block): + if block.heading_3.rich_text is None: + block.heading_3.rich_text = [] ret = block.heading_3.rich_text elif isinstance(block, QuoteBlock): + if block.quote.rich_text is None: + block.quote.rich_text = [] ret = block.quote.rich_text elif isinstance(block, BulletedListItemBlock): + if block.bulleted_list_item.rich_text is None: + block.bulleted_list_item.rich_text = [] ret = block.bulleted_list_item.rich_text elif isinstance(block, NumberedListItemBlock): + if block.numbered_list_item.rich_text is None: + block.numbered_list_item.rich_text = [] ret = block.numbered_list_item.rich_text elif isinstance(block, ToDoBlock): + if block.to_do.rich_text is None: + block.to_do.rich_text = [] ret = block.to_do.rich_text elif isinstance(block, ToggleBlock): + if block.toggle.rich_text is None: + block.toggle.rich_text = [] ret = block.toggle.rich_text + # Placeholder blocks with rich text + elif isinstance(block, CaptionPlaceholderBlock): + if block.rich_text is None: + block.rich_text = [] + ret = block.rich_text + elif isinstance(block, CellPlaceholderBlock): + if block.rich_text is None: + block.rich_text = [] + ret = block.rich_text + else: + raise Exception( + f"Unsupported block type: {type(block)}. " + "This should not happen as long as this function implements all block types." + ) # else: # raise ValueError(f"Unsupported block type: {type(block)}") return ret -def try_append_rich_text_to_block(block: BlockBase, rich_text: RichTextBase) -> bool: - if not isinstance(block, BlockBase) or not isinstance(rich_text, RichTextBase): - return False +def append_rich_text_to_block(block: BlockBase, rich_text: RichTextBase): + # if not isinstance(block, BlockBase) or not isinstance(rich_text, RichTextBase): + # return False + if not block_supports_rich_text(block): + raise ValueError(f"Block of type {type(block)} does not support rich text") rich_text_list = get_rich_text_from_block(block) - if rich_text_list is not None: - rich_text_list.append(rich_text) - return True + rich_text_list.append(rich_text) - return False + +def block_supports_rich_text(block: BlockBase) -> bool: + return type(block) in BLOCKS_WITH_RICH_TEXT + PLACEHOLDER_BLOCKS_WITH_RICH_TEXT @validate_call @@ -553,3 +633,51 @@ def html_table_has_header_row(table: Tag) -> bool: return True return False + + +def ensure_table_cell_count(table: TableBlock): + """ + Ensures that the table has the same number of cells in each row. + """ + # Get the maximum number of cells in all rows + max_cells = max(len(row.table_row.cells) for row in table.children) + for row in table.children: + n_diff = max_cells - len(row.table_row.cells) + # Append empty cells to the row + for _ in range(n_diff): + row.table_row.cells.append([]) + + +def _final_block_check(block: BlockBase): + if isinstance(block, CaptionPlaceholderBlock): + # Convert caption to a paragraph block + + ret = create_paragraph_block() + ret.paragraph.rich_text = block.rich_text + return ret + elif isinstance(block, BreakElementPlaceholderBlock): + # These should be handled in reconcile_* functions + return None + elif isinstance(block, TableBlock): + ensure_table_cell_count(block) + + return block + + +def run_final_block_checks(blocks: List[BlockBase]): + """ + Runs final checks on blocks after the main conversion is complete. + + E.g. Handles residual placeholder blocks after the main conversion is complete. + This is needed because some placeholder blocks need to be handled in a special way. + """ + ret = [] + for block in blocks: + if isinstance(getattr(block, "children", None), list): + block.children = run_final_block_checks(block.children) + + handled_block = _final_block_check(block) + if handled_block is not None: + ret.append(handled_block) + + return ret diff --git a/tests/html_jsondoc_pairs/ex06_caption_table.json b/tests/html_jsondoc_pairs/ex06_caption_table.json new file mode 100644 index 0000000..1252349 --- /dev/null +++ b/tests/html_jsondoc_pairs/ex06_caption_table.json @@ -0,0 +1,214 @@ +// This example should also work when
This is a caption for a table
IDNameDateAddress
#999-32acFirst Name13 May, 2013999 Spruce Lane, Somewhere, CA 94101
#888-32ddSample Name17 May, 1984999 Spruce Lane, Somewhere, CA 94101
Table footer info
", + "jsondoc": [ + { + "object": "block", + "id": "837b2347-ff8f-4c45-a07d-372425837ec5", + "type": "table", + "created_time": "2024-09-11T17:37:36.129629Z", + "table": { + "has_column_header": true, + "has_row_header": false + }, + "children": [ + { + "object": "block", + "id": "e39fc489-b2e8-49dc-8d78-4b33b7da276e", + "type": "table_row", + "created_time": "2024-09-11T17:37:36.128975Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "ID" + }, + "annotations": {}, + "plain_text": "ID" + } + ], + [ + { + "type": "text", + "text": { + "content": "Name" + }, + "annotations": {}, + "plain_text": "Name" + } + ], + [ + { + "type": "text", + "text": { + "content": "Date" + }, + "annotations": {}, + "plain_text": "Date" + } + ], + [ + { + "type": "text", + "text": { + "content": "Address" + }, + "annotations": {}, + "plain_text": "Address" + } + ] + ] + } + }, + { + "object": "block", + "id": "fbaf9cec-56a2-420e-adb0-c3713f9d1ee5", + "type": "table_row", + "created_time": "2024-09-11T17:37:36.129406Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "#999-32ac" + }, + "annotations": {}, + "plain_text": "#999-32ac" + } + ], + [ + { + "type": "text", + "text": { + "content": "First Name" + }, + "annotations": {}, + "plain_text": "First Name" + } + ], + [ + { + "type": "text", + "text": { + "content": "13 May, 2013" + }, + "annotations": {}, + "plain_text": "13 May, 2013" + } + ], + [ + { + "type": "text", + "text": { + "content": "999 Spruce Lane, Somewhere, CA 94101" + }, + "annotations": {}, + "plain_text": "999 Spruce Lane, Somewhere, CA 94101" + } + ] + ] + } + }, + { + "object": "block", + "id": "e7e6d08a-8a56-4a91-bf12-1b912dbe3a1f", + "type": "table_row", + "created_time": "2024-09-11T17:37:36.129535Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "#888-32dd" + }, + "annotations": {}, + "plain_text": "#888-32dd" + } + ], + [ + { + "type": "text", + "text": { + "content": "Sample Name" + }, + "annotations": {}, + "plain_text": "Sample Name" + } + ], + [ + { + "type": "text", + "text": { + "content": "17 May, 1984" + }, + "annotations": {}, + "plain_text": "17 May, 1984" + } + ], + [ + { + "type": "text", + "text": { + "content": "999 Spruce Lane, Somewhere, CA 94101" + }, + "annotations": {}, + "plain_text": "999 Spruce Lane, Somewhere, CA 94101" + } + ] + ] + } + }, + { + "object": "block", + "id": "c28ddc6c-51af-4b57-b32b-2967a5c7867f", + "type": "table_row", + "created_time": "2024-09-11T17:37:36.129591Z", + "has_children": false, + "table_row": { + "cells": [ + [ + { + "type": "text", + "text": { + "content": "Table footer info" + }, + "annotations": {}, + "plain_text": "Table footer info" + } + ], + [], + [], + [] + ] + } + } + ] + }, + { + "object": "block", + "id": "d0d57490-d0d1-4db8-a5be-d254415324b2", + "type": "paragraph", + "created_time": "2024-09-11T17:37:36.129737Z", + "has_children": false, + "paragraph": { + "rich_text": [ + { + "type": "text", + "text": { + "content": "This is a caption for a table" + }, + "annotations": {}, + "plain_text": "This is a caption for a table" + } + ] + } + } + ] +} diff --git a/tests/html_jsondoc_pairs/ex06_basic_ul.json b/tests/html_jsondoc_pairs/ex07_basic_ul.json similarity index 100% rename from tests/html_jsondoc_pairs/ex06_basic_ul.json rename to tests/html_jsondoc_pairs/ex07_basic_ul.json diff --git a/tests/html_jsondoc_pairs/ex07_basic_ol.json b/tests/html_jsondoc_pairs/ex08_basic_ol.json similarity index 100% rename from tests/html_jsondoc_pairs/ex07_basic_ol.json rename to tests/html_jsondoc_pairs/ex08_basic_ol.json diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index fd9d2bd..048f6ef 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -7,7 +7,7 @@ from jsondoc.convert.html import html_to_jsondoc from jsondoc.convert.markdown import jsondoc_to_markdown from jsondoc.serialize import jsondoc_dump_json, load_jsondoc -from jsondoc.utils import diff_jsonable_dict, set_dict_recursive +from jsondoc.utils import diff_jsonable_dict, load_json_file, set_dict_recursive def test_convert_html_all_elements(): @@ -56,8 +56,8 @@ def compare_jsondoc(jsondoc1: BaseModel, jsondoc2: BaseModel) -> bool: def _process_example(json_path): - with open(json_path, "r") as f: - data = json.load(f) + # with open(json_path, "r") as f: + data = load_json_file(json_path) html_source = data.get("html") jsondoc_target = data.get("jsondoc") @@ -90,5 +90,5 @@ def test_examples(): if __name__ == "__main__": - # test_examples() + test_examples() test_convert_html_all_elements() From b0f79b131069741907f2358278556960c5a71cdf Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:58:50 +0200 Subject: [PATCH 31/45] Can convert html_all_elements.html --- docs/html-conversion.md | 4 ++-- jsondoc/convert/html.py | 6 +++--- jsondoc/convert/markdown.py | 33 +++++++++++++++++++++------------ jsondoc/convert/utils.py | 30 ++++++++++++++++++++---------- tests/test_html_to_jsondoc.py | 5 +---- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/docs/html-conversion.md b/docs/html-conversion.md index 97922cc..c62548a 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -75,6 +75,6 @@ To conditionally handle these elements, we create a corresponding placeholder bl - [x] Convert line breaks `
` - [ ] Convert `` and `
` - [ ] Table cells with colspan/rowspan -- [ ] Residual strings or newlines in the final output list -- [ ] Force_page=true +- [x] Residual strings or newlines in the final output list +- [x] Force_page=true - [ ] Add sup/sub annotation? \ No newline at end of file diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index 1af82b1..e80a5d8 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -29,7 +29,7 @@ create_table_block, create_table_row_block, get_rich_text_from_block, - run_final_block_checks, + run_final_block_transformations, html_table_has_header_row, append_rich_text_to_block, ) @@ -379,7 +379,7 @@ def convert_soup( ) -> Page | BlockBase | List[BlockBase]: children = self.process_tag(soup, convert_as_inline=False, children_only=True) - children = run_final_block_checks(children) + children = run_final_block_transformations(children) is_page = self._is_soup_page(soup) ret = None @@ -483,13 +483,13 @@ def is_nested_node(el): assert isinstance( convert_output, (ConvertOutput, NoneType) ), f"Convert function {convert_fn} must return a ConvertOutput or None" + if convert_output is not None: current_level_object = convert_output.main_object current_level_prev_objects = convert_output.prev_objects current_level_next_objects = convert_output.next_objects # print(node, repr(current_level_object)) - # import ipdb; ipdb.set_trace() if current_level_object is None: objects = children_objects diff --git a/jsondoc/convert/markdown.py b/jsondoc/convert/markdown.py index ae8a36a..73d8155 100644 --- a/jsondoc/convert/markdown.py +++ b/jsondoc/convert/markdown.py @@ -248,8 +248,9 @@ def convert_rich_text_list_to_markdown( def convert_paragraph_block( self, block: ParagraphBlock, convert_as_inline: bool ) -> str: - rich_text = get_rich_text_from_block(block) - if rich_text is None: + try: + rich_text = get_rich_text_from_block(block) + except ValueError: return "" # Code blocks are kept verbatim @@ -274,11 +275,15 @@ def convert_divider_block( return "\n\n---\n\n" def convert_code_block(self, block: CodeBlock, convert_as_inline: bool) -> str: - rich_text = get_rich_text_from_block(block) - if rich_text is None: + try: + rich_text = get_rich_text_from_block(block) + except ValueError: return "" - language = block.code.language.value + try: + language = block.code.language.value + except AttributeError: + language = "" # if self.options["code_language_callback"]: # language = self.options["code_language_callback"](language) @@ -300,10 +305,12 @@ def convert_heading_n_block( block: Heading1Block | Heading2Block | Heading3Block, convert_as_inline: bool, ): - - rich_text = get_rich_text_from_block(block) - if rich_text is None: + try: + rich_text = get_rich_text_from_block(block) + except ValueError: return "" + # if rich_text is None: + # return "" text = self.convert_rich_text_list_to_markdown(rich_text, escape=True) text += self._get_children_content(block, convert_as_inline) @@ -324,9 +331,9 @@ def convert_heading_n_block( return "%s %s\n\n" % (hashes, text) def convert_quote_block(self, block: QuoteBlock, convert_as_inline: bool) -> str: - rich_text = get_rich_text_from_block(block) - - if rich_text is None: + try: + rich_text = get_rich_text_from_block(block) + except ValueError: return "" # Code blocks are kept verbatim @@ -382,7 +389,9 @@ def convert_table_block(self, block: TableBlock, convert_as_inline: bool) -> str is_headrow = block.table.has_column_header and n == 0 text += self._convert_table_row_block( - row, convert_as_inline, is_headrow=is_headrow, + row, + convert_as_inline, + is_headrow=is_headrow, ) return "\n\n" + text + "\n" diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index 1be25b9..a1417e8 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -1,5 +1,6 @@ import logging from datetime import datetime, timezone +import re from typing import List, Literal, Optional, Type from bs4 import Tag @@ -41,6 +42,8 @@ from jsondoc.rules import is_block_child_allowed from jsondoc.utils import generate_id, get_current_time +all_whitespace_re = re.compile(r"[\s]+") + class BreakElementPlaceholderBlock(BlockBase): type: Literal["break_element_placeholder"] = "break_element_placeholder" @@ -648,23 +651,30 @@ def ensure_table_cell_count(table: TableBlock): row.table_row.cells.append([]) -def _final_block_check(block: BlockBase): - if isinstance(block, CaptionPlaceholderBlock): +def _final_block_transformation(obj: BlockBase | str | RichTextBase): + if isinstance(obj, CaptionPlaceholderBlock): # Convert caption to a paragraph block ret = create_paragraph_block() - ret.paragraph.rich_text = block.rich_text + ret.paragraph.rich_text = obj.rich_text return ret - elif isinstance(block, BreakElementPlaceholderBlock): + elif isinstance(obj, BreakElementPlaceholderBlock): # These should be handled in reconcile_* functions return None - elif isinstance(block, TableBlock): - ensure_table_cell_count(block) + elif isinstance(obj, TableBlock): + ensure_table_cell_count(obj) + elif isinstance(obj, str): + text_ = all_whitespace_re.sub(" ", obj) + return create_paragraph_block(text=text_) + elif isinstance(obj, RichTextBase): + new_obj_ = create_paragraph_block() + new_obj_.paragraph.rich_text = [obj] + return new_obj_ - return block + return obj -def run_final_block_checks(blocks: List[BlockBase]): +def run_final_block_transformations(blocks: List[BlockBase]): """ Runs final checks on blocks after the main conversion is complete. @@ -674,9 +684,9 @@ def run_final_block_checks(blocks: List[BlockBase]): ret = [] for block in blocks: if isinstance(getattr(block, "children", None), list): - block.children = run_final_block_checks(block.children) + block.children = run_final_block_transformations(block.children) - handled_block = _final_block_check(block) + handled_block = _final_block_transformation(block) if handled_block is not None: ret.append(handled_block) diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index 048f6ef..a30f24f 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -21,10 +21,7 @@ def test_convert_html_all_elements(): # print("\n\nConverted to markdown:\n\n") # print(jsondoc_to_markdown(ret[0])) - # print(jsondoc_to_markdown(ret)) - import ipdb - - ipdb.set_trace() + print(jsondoc_to_markdown(ret)) def compare_jsondoc(jsondoc1: BaseModel, jsondoc2: BaseModel) -> bool: From f1b9d2fc0dfcb56efefe8e5573de6e8e9635154a Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 11 Sep 2024 22:32:35 +0200 Subject: [PATCH 32/45] Handle image captions --- jsondoc/convert/html.py | 91 ++++++++++++++++--- jsondoc/convert/utils.py | 19 +++- ...ph.json => test_blockquote_paragraph.json} | 0 ...{ex05_basic_br.json => test_br_basic.json} | 0 .../test_figure_image_with_caption.json | 36 ++++++++ ...{ex08_basic_ol.json => test_ol_basic.json} | 0 ...ragraph.json => test_paragraph_basic.json} | 0 ...aragraph.json => test_paragraph_bold.json} | 0 ...basic_table.json => test_table_basic.json} | 0 ...able.json => test_table_with_caption.json} | 0 ...{ex07_basic_ul.json => test_ul_basic.json} | 0 tests/test_html_to_jsondoc.py | 3 +- 12 files changed, 132 insertions(+), 17 deletions(-) rename tests/html_jsondoc_pairs/{ex03_blockquoted_paragraph.json => test_blockquote_paragraph.json} (100%) rename tests/html_jsondoc_pairs/{ex05_basic_br.json => test_br_basic.json} (100%) create mode 100644 tests/html_jsondoc_pairs/test_figure_image_with_caption.json rename tests/html_jsondoc_pairs/{ex08_basic_ol.json => test_ol_basic.json} (100%) rename tests/html_jsondoc_pairs/{ex01_basic_paragraph.json => test_paragraph_basic.json} (100%) rename tests/html_jsondoc_pairs/{ex02_bold_paragraph.json => test_paragraph_bold.json} (100%) rename tests/html_jsondoc_pairs/{ex04_basic_table.json => test_table_basic.json} (100%) rename tests/html_jsondoc_pairs/{ex06_caption_table.json => test_table_with_caption.json} (100%) rename tests/html_jsondoc_pairs/{ex07_basic_ul.json => test_ul_basic.json} (100%) diff --git a/jsondoc/convert/html.py b/jsondoc/convert/html.py index e80a5d8..df10d58 100644 --- a/jsondoc/convert/html.py +++ b/jsondoc/convert/html.py @@ -10,6 +10,7 @@ BreakElementPlaceholderBlock, CaptionPlaceholderBlock, CellPlaceholderBlock, + FigurePlaceholderBlock, append_to_parent_block, append_to_rich_text, block_supports_rich_text, @@ -222,11 +223,53 @@ def append_caption_block_to_table_row_block( parent_table_row_block.table_row.cells.append(child_rich_text) +def append_caption_block_to_image_block( + parent_image_block: ImageBlock, + child_paragraph_block: CaptionPlaceholderBlock, +): + """ + Sets a caption for an image block + """ + parent_image_block.image.caption = child_paragraph_block.rich_text + + +def override_reconcile_to_figure_placeholder_block( + block: FigurePlaceholderBlock, children: List[CHILDREN_TYPE] +): + """ + Given a figure placeholder block and a list of children, + this function will reconcile the children to the block + and return a list of objects + """ + image_block = None + caption_block = None + + ret = [] + for child in children: + if isinstance(child, CaptionPlaceholderBlock): + caption_block = child + continue + elif isinstance(child, ImageBlock): + image_block = child + + ret.append(child) + + if image_block is not None and caption_block is not None: + image_block.image.caption = caption_block.rich_text + + return ret + + # Override append functions # Maps pairs of (parent_block_type, child_block_type) to a function # that appends the child block to the parent block OVERRIDE_APPEND_FUNCTIONS = { (TableRowBlock, CellPlaceholderBlock): append_caption_block_to_table_row_block, + (ImageBlock, CaptionPlaceholderBlock): append_caption_block_to_image_block, +} + +OVERRIDE_RECONCILE_FUNCTIONS = { + FigurePlaceholderBlock: override_reconcile_to_figure_placeholder_block, } @@ -270,6 +313,10 @@ def reconcile_to_block( this function will reconcile the children to the block and return a list of objects """ + override_reconcile_fn = OVERRIDE_RECONCILE_FUNCTIONS.get(type(block)) + if override_reconcile_fn: + return override_reconcile_fn(block, children) + remaining_children = [] objects = [block] @@ -462,7 +509,6 @@ def is_nested_node(el): if isinstance(el, Comment) or isinstance(el, Doctype): continue elif isinstance(el, NavigableString): - # text += self.process_text(el) processed_text = self.process_text(el) if processed_text: children_objects.append(processed_text) @@ -571,13 +617,24 @@ def process_text(self, el): # remove trailing whitespaces if any of the following condition is true: # - current text node is the last node in li # - current text node is followed by an embedded list - if el.parent.name == "li" and ( - not el.next_sibling or el.next_sibling.name in ["ul", "ol"] - ): - text = text.rstrip() + # if el.parent.name == "li" and ( + # not el.next_sibling or el.next_sibling.name in ["ul", "ol"] + # ): + # text = text.rstrip() # Strip preceding and trailing only newlines - text = text.strip("\n") + # text = text.strip("\n") + # Match whitespace at the end of string and convert it to a single space + text = re.sub(r"\s+$", " ", text) + # Match whitespace at the beginning of string and convert it to a single space + text = re.sub(r"^\s+", " ", text) + + if not el.previous_sibling: + text = text.lstrip() + + if not el.next_sibling: + text = text.rstrip() + if len(text) == 0: return None @@ -798,7 +855,12 @@ def convert_img(self, el, convert_as_inline): # return alt # return "![%s](%s%s)" % (alt, src, title_part) - return ConvertOutput(main_object=create_image_block(url=src, caption=alt)) + return ConvertOutput( + main_object=create_image_block( + url=src, + # caption=alt, + ) + ) def convert_list(self, el, convert_as_inline): """ @@ -893,7 +955,6 @@ def convert_table(self, el, convert_as_inline): ) def convert_caption(self, el, convert_as_inline): - # return text + "\n" return ConvertOutput( main_object=CaptionPlaceholderBlock( id="", @@ -903,9 +964,17 @@ def convert_caption(self, el, convert_as_inline): ) ) - def convert_figcaption(self, el, convert_as_inline): - # return "\n\n" + text + "\n\n" - return None # TBD + convert_figcaption = convert_caption + + def convert_figure(self, el, convert_as_inline): + return ConvertOutput( + main_object=FigurePlaceholderBlock( + id="", + created_time=get_current_time(), + type="figure_placeholder", + rich_text=[], + ) + ) def convert_td(self, el, convert_as_inline): """ diff --git a/jsondoc/convert/utils.py b/jsondoc/convert/utils.py index a1417e8..076121d 100644 --- a/jsondoc/convert/utils.py +++ b/jsondoc/convert/utils.py @@ -45,24 +45,33 @@ all_whitespace_re = re.compile(r"[\s]+") -class BreakElementPlaceholderBlock(BlockBase): +class PlaceholderBlock(BlockBase): + pass + + +class BreakElementPlaceholderBlock(PlaceholderBlock): type: Literal["break_element_placeholder"] = "break_element_placeholder" -class CaptionPlaceholderBlock(BlockBase): +class CaptionPlaceholderBlock(PlaceholderBlock): type: Literal["caption_placeholder"] = "caption_placeholder" rich_text: Optional[List[RichTextBase]] = None -class CellPlaceholderBlock(BlockBase): +class CellPlaceholderBlock(PlaceholderBlock): type: Literal["cell_placeholder"] = "cell_placeholder" rich_text: Optional[List[RichTextBase]] = None -PLACEHOLDER_BLOCKS = [ +class FigurePlaceholderBlock(PlaceholderBlock): + type: Literal["figure_placeholder"] = "figure_placeholder" + + +PLACEHOLDER_BLOCKS: List[Type[PlaceholderBlock]] = [ BreakElementPlaceholderBlock, CaptionPlaceholderBlock, CellPlaceholderBlock, + FigurePlaceholderBlock, ] BLOCKS_WITH_RICH_TEXT: List[Type[BlockBase]] = [ @@ -78,7 +87,7 @@ class CellPlaceholderBlock(BlockBase): ToggleBlock, ] -PLACEHOLDER_BLOCKS_WITH_RICH_TEXT: List[Type[BlockBase]] = [ +PLACEHOLDER_BLOCKS_WITH_RICH_TEXT: List[Type[PlaceholderBlock]] = [ CaptionPlaceholderBlock, CellPlaceholderBlock, ] diff --git a/tests/html_jsondoc_pairs/ex03_blockquoted_paragraph.json b/tests/html_jsondoc_pairs/test_blockquote_paragraph.json similarity index 100% rename from tests/html_jsondoc_pairs/ex03_blockquoted_paragraph.json rename to tests/html_jsondoc_pairs/test_blockquote_paragraph.json diff --git a/tests/html_jsondoc_pairs/ex05_basic_br.json b/tests/html_jsondoc_pairs/test_br_basic.json similarity index 100% rename from tests/html_jsondoc_pairs/ex05_basic_br.json rename to tests/html_jsondoc_pairs/test_br_basic.json diff --git a/tests/html_jsondoc_pairs/test_figure_image_with_caption.json b/tests/html_jsondoc_pairs/test_figure_image_with_caption.json new file mode 100644 index 0000000..2a36ff8 --- /dev/null +++ b/tests/html_jsondoc_pairs/test_figure_image_with_caption.json @@ -0,0 +1,36 @@ +{ + "html": "
\"Figure
Photo of the sky at night. Original by @mrmrs
", + "jsondoc": { + "object": "block", + "id": "7a0f25b0-4a54-4762-a27b-a4f0708e1c0b", + "type": "image", + "created_time": "2024-09-11T20:29:31.222412Z", + "image": { + "type": "external", + "external": { + "url": "http://placekitten.com/g/960/320" + }, + "caption": [ + { + "type": "text", + "text": { + "content": "Photo of the sky at night. Original by " + }, + "annotations": {}, + "plain_text": "Photo of the sky at night. Original by " + }, + { + "type": "text", + "text": { + "content": "@mrmrs@mrmrs", + "link": { + "url": "http://flickr.com/photos/heyitsadam/" + } + }, + "annotations": {}, + "plain_text": "@mrmrs@mrmrs" + } + ] + } + } +} diff --git a/tests/html_jsondoc_pairs/ex08_basic_ol.json b/tests/html_jsondoc_pairs/test_ol_basic.json similarity index 100% rename from tests/html_jsondoc_pairs/ex08_basic_ol.json rename to tests/html_jsondoc_pairs/test_ol_basic.json diff --git a/tests/html_jsondoc_pairs/ex01_basic_paragraph.json b/tests/html_jsondoc_pairs/test_paragraph_basic.json similarity index 100% rename from tests/html_jsondoc_pairs/ex01_basic_paragraph.json rename to tests/html_jsondoc_pairs/test_paragraph_basic.json diff --git a/tests/html_jsondoc_pairs/ex02_bold_paragraph.json b/tests/html_jsondoc_pairs/test_paragraph_bold.json similarity index 100% rename from tests/html_jsondoc_pairs/ex02_bold_paragraph.json rename to tests/html_jsondoc_pairs/test_paragraph_bold.json diff --git a/tests/html_jsondoc_pairs/ex04_basic_table.json b/tests/html_jsondoc_pairs/test_table_basic.json similarity index 100% rename from tests/html_jsondoc_pairs/ex04_basic_table.json rename to tests/html_jsondoc_pairs/test_table_basic.json diff --git a/tests/html_jsondoc_pairs/ex06_caption_table.json b/tests/html_jsondoc_pairs/test_table_with_caption.json similarity index 100% rename from tests/html_jsondoc_pairs/ex06_caption_table.json rename to tests/html_jsondoc_pairs/test_table_with_caption.json diff --git a/tests/html_jsondoc_pairs/ex07_basic_ul.json b/tests/html_jsondoc_pairs/test_ul_basic.json similarity index 100% rename from tests/html_jsondoc_pairs/ex07_basic_ul.json rename to tests/html_jsondoc_pairs/test_ul_basic.json diff --git a/tests/test_html_to_jsondoc.py b/tests/test_html_to_jsondoc.py index a30f24f..2c63fcf 100644 --- a/tests/test_html_to_jsondoc.py +++ b/tests/test_html_to_jsondoc.py @@ -17,7 +17,8 @@ def test_convert_html_all_elements(): # content = "

This is a bold word and this is an emphasized word.

" ret = html_to_jsondoc(content) - print(ret) + # print(ret) + print(jsondoc_dump_json(ret, indent=2)) # print("\n\nConverted to markdown:\n\n") # print(jsondoc_to_markdown(ret[0])) From 8e6d922ec8fac78199c30783cb3786fb18e8c234 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Wed, 11 Sep 2024 22:36:03 +0200 Subject: [PATCH 33/45] Minor --- docs/html-conversion.md | 3 ++- jsondoc/convert/utils.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/html-conversion.md b/docs/html-conversion.md index c62548a..5d8a7df 100644 --- a/docs/html-conversion.md +++ b/docs/html-conversion.md @@ -73,8 +73,9 @@ To conditionally handle these elements, we create a corresponding placeholder bl - [x] Convert lists `