From 4b8607b12a801e980106025145f21208ab9c29ac Mon Sep 17 00:00:00 2001 From: Sphericalkat Date: Sat, 27 May 2023 22:57:56 +0530 Subject: [PATCH] feat: add algorithm to parse ranges with markup Signed-off-by: Sphericalkat --- .gitignore | 3 +- api/routes/show.go | 26 +- frontend/dist/assets/index-4d1e72db.css | 1 - frontend/dist/index.html | 2 +- frontend/dist/show.html | 35 ++ frontend/index.html | 2 +- frontend/package.json | 1 + frontend/pnpm-lock.yaml | 35 ++ frontend/show.html | 34 ++ frontend/src/show.css | 37 ++ frontend/src/{index.css => styles.css} | 4 - frontend/tailwind.config.cjs | 50 ++- pkg/converters/markup_converter.go | 86 +++++ pkg/converters/markup_converter_test.go | 58 +++ response.json | 453 +++++++++++++++++++++++- static/index.css | 0 16 files changed, 814 insertions(+), 13 deletions(-) delete mode 100644 frontend/dist/assets/index-4d1e72db.css create mode 100644 frontend/src/show.css rename frontend/src/{index.css => styles.css} (71%) create mode 100644 pkg/converters/markup_converter.go create mode 100644 pkg/converters/markup_converter_test.go delete mode 100644 static/index.css diff --git a/.gitignore b/.gitignore index 97aca2e..0e2c6af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env -node_modules \ No newline at end of file +node_modules +frontend/dist \ No newline at end of file diff --git a/api/routes/show.go b/api/routes/show.go index 400e71f..f0c1e70 100644 --- a/api/routes/show.go +++ b/api/routes/show.go @@ -1,6 +1,11 @@ package routes import ( + "fmt" + "log" + "strings" + "time" + "github.com/gofiber/fiber/v2" "github.com/medium.rip/pkg/client" ) @@ -16,7 +21,26 @@ func show(c *fiber.Ctx) error { return err } - return c.JSON(e) + post := e.Data.Post + publishDate := time.UnixMilli(e.Data.Post.CreatedAt) + log.Println(publishDate) + + var sb strings.Builder + + for _, node := range post.Content.BodyModel.Paragraphs { + switch node.Type { + case "H3": + sb.WriteString(fmt.Sprintf("

%s

", node.Text)) + } + } + + return c.Render("show", fiber.Map { + "Title": post.Title, + "UserId": post.Creator.ID, + "Author": post.Creator.Name, + "PublishDate": publishDate.Format(time.DateOnly), + "Nodes": post.Content.BodyModel.Paragraphs, + }) } func index(c *fiber.Ctx) error { diff --git a/frontend/dist/assets/index-4d1e72db.css b/frontend/dist/assets/index-4d1e72db.css deleted file mode 100644 index 4971b1f..0000000 --- a/frontend/dist/assets/index-4d1e72db.css +++ /dev/null @@ -1 +0,0 @@ -*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.text-2xl{font-size:1.5rem;line-height:2rem}:root{--accent: #23b0ff;--background: #1f222a}html,body{width:100%;height:100%}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}@media (min-width: 1024px){html{font-size:1.125rem;line-height:1.7777778}}body{background-color:#1f222a;color:#fff;line-height:1.54;letter-spacing:-.02em;font-variant-ligatures:contextual;font-size:1rem}.text-accent{color:var(--accent)}.bg-accent{background:var(--accent)} diff --git a/frontend/dist/index.html b/frontend/dist/index.html index 563da44..6e19d7a 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -6,7 +6,7 @@ {{ .Title }} - +

Hello, World!

diff --git a/frontend/dist/show.html b/frontend/dist/show.html index e69de29..0560098 100644 --- a/frontend/dist/show.html +++ b/frontend/dist/show.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + {{ .Title }} + + + + +
+

{{.Title}}

+

{{.Author}} on {{.PublishDate}}

+ {{range .Nodes}} + {{if eq .type "H3"}} +

.text

+ {{end}} + {{if eq .type "IMG"}} +

.text

+ {{end}} + {{end}} +
+ + + \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index 08cb894..25a5499 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - + {{ .Title }} diff --git a/frontend/package.json b/frontend/package.json index 58848b5..ac747b9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "author": "", "license": "ISC", "devDependencies": { + "@tailwindcss/typography": "^0.5.9", "autoprefixer": "^10.4.14", "postcss": "^8.4.23", "tailwindcss": "^3.3.2", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 71bee84..1543ffc 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -1,6 +1,9 @@ lockfileVersion: '6.0' devDependencies: + '@tailwindcss/typography': + specifier: ^0.5.9 + version: 0.5.9(tailwindcss@3.3.2) autoprefixer: specifier: ^10.4.14 version: 10.4.14(postcss@8.4.23) @@ -274,6 +277,18 @@ packages: fastq: 1.15.0 dev: true + /@tailwindcss/typography@0.5.9(tailwindcss@3.3.2): + resolution: {integrity: sha512-t8Sg3DyynFysV9f4JDOVISGsjazNb48AeIYQwcL+Bsq5uf4RYL75C1giZ43KISjeDGBaTN3Kxh7Xj/vRSMJUUg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.3.2 + dev: true + /any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} dev: true @@ -557,6 +572,18 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + dev: true + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -697,6 +724,14 @@ packages: postcss-selector-parser: 6.0.13 dev: true + /postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + /postcss-selector-parser@6.0.13: resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} engines: {node: '>=4'} diff --git a/frontend/show.html b/frontend/show.html index e69de29..c417323 100644 --- a/frontend/show.html +++ b/frontend/show.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + {{ .Title }} + + + +
+

{{.Title}}

+

{{.Author}} on {{.PublishDate}}

+ {{range .Nodes}} + {{if eq .type "H3"}} +

.text

+ {{end}} + {{if eq .type "IMG"}} +

.text

+ {{end}} + {{end}} +
+ + + \ No newline at end of file diff --git a/frontend/src/show.css b/frontend/src/show.css new file mode 100644 index 0000000..4f3f446 --- /dev/null +++ b/frontend/src/show.css @@ -0,0 +1,37 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --accent: #23b0ff; + --background: #1f222a; +} + +html, body { + width: 100%; + height: 100%; +} + +@media (min-width: 1024px) { + html { + font-size: 1.125rem; + line-height: 1.7777778; + } +} + +body { + /* background-color: #1f222a; */ + /* color: #ffffff; */ + /* line-height: 1.54; + letter-spacing: -0.02em; + font-variant-ligatures: contextual; + font-size: 1rem; */ +} + +.text-accent { + color: var(--accent); +} + +.bg-accent { + background: var(--accent); +} \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/styles.css similarity index 71% rename from frontend/src/index.css rename to frontend/src/styles.css index 8a78494..f9e87be 100644 --- a/frontend/src/index.css +++ b/frontend/src/styles.css @@ -12,10 +12,6 @@ html, body { height: 100%; } -html { - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji; -} - @media (min-width: 1024px) { html { font-size: 1.125rem; diff --git a/frontend/tailwind.config.cjs b/frontend/tailwind.config.cjs index 87a92c7..6598ac0 100644 --- a/frontend/tailwind.config.cjs +++ b/frontend/tailwind.config.cjs @@ -1,8 +1,52 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: ["./src/**/*.ts", "./*.html"], + content: ["./src/**/*.ts", "./**/*.html"], theme: { - extend: {}, + fontFamily: { + 'sans': ['Inter'], + }, + extend: { + typography: ({ theme }) => ({ + pink: { + css: { + '--tw-prose-body': theme('colors.pink[800]'), + '--tw-prose-headings': theme('colors.pink[900]'), + '--tw-prose-lead': theme('colors.pink[700]'), + '--tw-prose-links': theme('colors.pink[900]'), + '--tw-prose-bold': theme('colors.pink[900]'), + '--tw-prose-counters': theme('colors.pink[600]'), + '--tw-prose-bullets': theme('colors.pink[400]'), + '--tw-prose-hr': theme('colors.pink[300]'), + '--tw-prose-quotes': theme('colors.pink[900]'), + '--tw-prose-quote-borders': theme('colors.pink[300]'), + '--tw-prose-captions': theme('colors.pink[700]'), + '--tw-prose-code': theme('colors.pink[900]'), + '--tw-prose-pre-code': theme('colors.pink[100]'), + '--tw-prose-pre-bg': theme('colors.pink[900]'), + '--tw-prose-th-borders': theme('colors.pink[300]'), + '--tw-prose-td-borders': theme('colors.pink[200]'), + '--tw-prose-invert-body': theme('colors.pink[200]'), + '--tw-prose-invert-headings': theme('colors.white'), + '--tw-prose-invert-lead': theme('colors.pink[300]'), + '--tw-prose-invert-links': theme('colors.white'), + '--tw-prose-invert-bold': theme('colors.white'), + '--tw-prose-invert-counters': theme('colors.pink[400]'), + '--tw-prose-invert-bullets': theme('colors.pink[600]'), + '--tw-prose-invert-hr': theme('colors.pink[700]'), + '--tw-prose-invert-quotes': theme('colors.pink[100]'), + '--tw-prose-invert-quote-borders': theme('colors.pink[700]'), + '--tw-prose-invert-captions': theme('colors.pink[400]'), + '--tw-prose-invert-code': theme('colors.white'), + '--tw-prose-invert-pre-code': theme('colors.pink[300]'), + '--tw-prose-invert-pre-bg': 'rgb(0 0 0 / 50%)', + '--tw-prose-invert-th-borders': theme('colors.pink[600]'), + '--tw-prose-invert-td-borders': theme('colors.pink[700]'), + }, + }, + }), + }, }, - plugins: [], + plugins: [ + require('@tailwindcss/typography'), + ], }; diff --git a/pkg/converters/markup_converter.go b/pkg/converters/markup_converter.go new file mode 100644 index 0000000..e1119d1 --- /dev/null +++ b/pkg/converters/markup_converter.go @@ -0,0 +1,86 @@ +package converters + +import ( + "sort" + + "github.com/medium.rip/pkg/entities" +) + +type RangeWithMarkup struct { + Range []int + Markups []entities.Markup +} + +func unique(intSlice []int) []int { + keys := make(map[int]bool) + list := []int{} + for _, entry := range intSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +func ranges(text string, markups []entities.Markup) []RangeWithMarkup { + ranges := make([]RangeWithMarkup, 0) + + // first, get all the borders of the markups + markupBoundaries := make([]int, 0) + for _, m := range markups { + markupBoundaries = append(markupBoundaries, []int{int(m.Start), int(m.End)}...) + } + + // include the start and end indexes of the text + markupBoundaries = append([]int{0}, markupBoundaries...) + markupBoundaries = append(markupBoundaries, len(text)) + + // remove duplicates + markupBoundaries = unique(markupBoundaries) + + // sort slice + sort.Slice(markupBoundaries, func(i, j int) bool { + return markupBoundaries[i] < markupBoundaries[j] + }) + + // attach markup to every range + for i := 0; i < len(markupBoundaries)-1; i++ { + start := markupBoundaries[i] + end := markupBoundaries[i+1] + + // check if this markup is covered by the range + coveredMarkups := make([]entities.Markup, 0) + for _, m := range markups { + if (int(m.Start) >= start && int(m.Start) < end) || (int(m.End - 1) >= start && int(m.End - 1) < end) { + coveredMarkups = append(coveredMarkups, m) + } + } + + // append the range + ranges = append(ranges, RangeWithMarkup{ + Range: []int{start, end}, + Markups: coveredMarkups, + }) + } + + return ranges +} + +func Convert(text string, markups []entities.Markup) { + // for _, m := range markups { + // switch m.Type { + // case "A": + // if m.Href != nil { + + // } else if { + // m.UserID != nil { + + // } + // } + // case "CODE": + // case "EM": + // case "STRONG": + // } + // } +} \ No newline at end of file diff --git a/pkg/converters/markup_converter_test.go b/pkg/converters/markup_converter_test.go new file mode 100644 index 0000000..372ce32 --- /dev/null +++ b/pkg/converters/markup_converter_test.go @@ -0,0 +1,58 @@ +package converters + +import ( + "testing" + + "github.com/medium.rip/pkg/entities" +) + +func TestRanges(t *testing.T) { + ranges := ranges("strong and emphasized only", []entities.Markup{ + { + Type: "STRONG", + Start: 0, + End: 10, + }, + { + Type: "EM", + Start: 7, + End: 21, + }, + }) + + if len(ranges) != 4 { + t.Errorf("Expected 4 ranges, got %d", len(ranges)) + } + + if ranges[0].Range[0] != 0 || ranges[0].Range[1] != 7 { + t.Errorf("Expected range to be [0, 7], got %v", ranges[0].Range) + } + + if ranges[0].Markups[0].Type != "STRONG" { + t.Errorf("Expected markup to be STRONG, got %s", ranges[0].Markups[0].Type) + } + + if ranges[1].Range[0] != 7 || ranges[1].Range[1] != 10 { + t.Errorf("Expected range to be [7, 10], got %v", ranges[1].Range) + } + + if ranges[1].Markups[0].Type != "STRONG" { + t.Errorf("Expected markup to be STRONG, got %s", ranges[1].Markups[0].Type) + } + + if ranges[2].Range[0] != 10 || ranges[2].Range[1] != 21 { + t.Errorf("Expected range to be [10, 21], got %v", ranges[2].Range) + } + + if ranges[2].Markups[0].Type != "EM" { + t.Errorf("Expected markup to be EM, got %s", ranges[2].Markups[0].Type) + } + + if ranges[3].Range[0] != 21 || ranges[3].Range[1] != 26 { + t.Errorf("Expected range to be [21, 26], got %v", ranges[3].Range) + } + + if len(ranges[3].Markups) != 0 { + t.Errorf("Expected markup to be empty, got %v", ranges[3].Markups) + } +} \ No newline at end of file diff --git a/response.json b/response.json index 3f7f065..8ff8837 100644 --- a/response.json +++ b/response.json @@ -1 +1,452 @@ -{"data":{"post":{"title":"Pandas AI — The Future of Data Analysis","createdAt":1683185501743,"creator":{"id":"b856005e5ecd","name":"Fareed Khan"},"content":{"bodyModel":{"paragraphs":[{"name":"b238","text":"Pandas AI — The Future of Data Analysis","type":"H3","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"cbfd","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*awdsJFX9yyTGK7Ym9I2TtA.png","originalWidth":1481,"originalHeight":758}},{"name":"2c3a","text":"Imagine being able to talk to your data like it’s your best friend. That’s what Pandas AI does! This Python library has generative artificial intelligence capabilities that can turn your dataframes into conversationalists. No more endless hours of staring at rows and columns.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"a1ee","text":"But don’t worry, Pandas AI is not here to replace your beloved Pandas. It’s here to enhance it! With Pandas AI, you can take your data analysis and manipulation to the next level. Think of it like a superhero sidekick — it’s there to help you save the day and make your life easier.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"5514","text":"The possibilities with Pandas AI are endless. Imagine having a dataframe that can write its own reports, or one that can analyze complex data and provide you with easy-to-understand summaries.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"5171","text":"In this quick guide, you’ll get a step-by-step walkthrough of how to use this cutting-edge library, regardless of your level of experience in the field.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"ab65","text":"Whether you’re an experienced data analyst or a beginner, this guide will equip you with all the tools you need to dive into the world of Pandas AI with confidence. So sit back, relax, and let’s explore the exciting possibilities that Pandas AI has to offer!","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"42f1","text":"Official GitHub Repository — https://github.com/gventuri/pandas-ai","type":"P","href":null,"layout":null,"markups":[{"title":"","type":"A","href":"https://github.com/gventuri/pandas-ai","userId":null,"start":29,"end":66,"anchorType":"LINK"},{"title":null,"type":"STRONG","href":null,"userId":null,"start":0,"end":28,"anchorType":null}],"iframe":null,"metadata":null},{"name":"7b4c","text":"Code— https://colab.research.google.com/drive/1rKz7TudOeCeKGHekw7JFNL4sagN9hon-?usp=sharing","type":"P","href":null,"layout":null,"markups":[{"title":"","type":"A","href":"https://colab.research.google.com/drive/1rKz7TudOeCeKGHekw7JFNL4sagN9hon-?usp=sharing","userId":null,"start":6,"end":91,"anchorType":"LINK"},{"title":null,"type":"STRONG","href":null,"userId":null,"start":0,"end":4,"anchorType":null}],"iframe":null,"metadata":null},{"name":"ae7c","text":"Installing Pandas AI using pip","type":"H4","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"51fe","text":"pip install pandasai","type":"PRE","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"4f69","text":"Our DataFrame contains information about various countries, including their GDP (in millions of USD) and happiness index scores. It consists of 10 rows and 3 columns:","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"4b06","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*FvevlIS-mnRSvqg2Nq5JnQ.png","originalWidth":925,"originalHeight":809}},{"name":"29ef","text":"Importing PandasAI with OpenAI","type":"H4","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"2656","text":"In the next step, we’ll import the pandasai library that we installed earlier and then import the LLM (Large Language Model) feature. As of May 2023, pandasai only supports the OpenAI model, which we’ll be utilizing understand the data.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"a57a","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*RsVA6lJngSkpSs_BB9yZAg.png","originalWidth":939,"originalHeight":409}},{"name":"e33e","text":"To use the OpenAI API, you must generate your own unique API key. If you haven’t done so already, you can easily create an account on the platform’s official website at platform.openai.com. Once you’ve created your account, you’ll receive an instant $5 credit that can be used to explore and experiment with the API.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"a3ab","text":"Initializing PandasAI and asking Question","type":"H4","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"52ae","text":"Afterwards, we’ll provide our OpenAI model to Pandas AI and ask various questions.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"37d9","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*UtA6DMSuzKtKOX5mNyfhCA.png","originalWidth":940,"originalHeight":226}},{"name":"7578","text":"When using pandas_ai.run, two parameters are necessary: the dataframe you’re working with and the question you’re seeking an answer to, it returns the top 5 happiest countries based on the supplied dataframe.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"8f1b","text":"Asking Complex Questions","type":"H4","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"6388","text":"Let’s check whether it can draw the plots for us?","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"1495","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*0mLGIH_2j4K0OC9SZzNrZQ.png","originalWidth":940,"originalHeight":153}},{"name":"140d","text":"Yes it does plot the graph, based on the question I asked.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"a242","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*zxjcTNyFUFSn2g9aeLrImw.png","originalWidth":900,"originalHeight":863}},{"name":"d6ec","text":"Let’s perform a complex task, removing NAN values from the below dataset:","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"48b0","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*5wd-jewNp91MNc69DPDe8Q.png","originalWidth":503,"originalHeight":481}},{"name":"c589","text":"This is the output we get:","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"a830","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*VAEUvjecOWIz5JLtMKLCDg.png","originalWidth":940,"originalHeight":137}},{"name":"b01d","text":"But when I print the df variable again, it does remove those NAN values from the dataset, removing that row entirely","type":"P","href":null,"layout":null,"markups":[{"title":null,"type":"STRONG","href":null,"userId":null,"start":21,"end":23,"anchorType":null}],"iframe":null,"metadata":null},{"name":"9b78","text":"","type":"IMG","href":null,"layout":"INSET_CENTER","markups":[],"iframe":null,"metadata":{"id":"1*vomhvRkNY_B4v3M7bhoXJA.png","originalWidth":496,"originalHeight":400}},{"name":"f25e","text":"The pandasai library offers an extensive range of possibilities, and you can explore them all by visiting their official repository page, which I’ve shared earlier.","type":"P","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"6d46","text":"It’s important to note that working with pandasai involves OpenAI pricing, and you can find the most up-to-date pricing information on their website. As of May 2023, the pricing is approximately 1000 tokens per $0.0200 (for the GPT-3.5-Turbo Model). When posing a question, it’s crucial to remember that the entire dataframe is passed along with the question every time, so it may not be an ideal solution for handling large datasets.","type":"H4","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null},{"name":"67b7","text":"If you have any query feel free to ask me!","type":"H3","href":null,"layout":null,"markups":[],"iframe":null,"metadata":null}]}}}}} +{ + "data": { + "post": { + "title": "Pandas AI — The Future of Data Analysis", + "createdAt": 1683185501743, + "creator": { + "id": "b856005e5ecd", + "name": "Fareed Khan" + }, + "content": { + "bodyModel": { + "paragraphs": [ + { + "name": "b238", + "text": "Pandas AI — The Future of Data Analysis", + "type": "H3", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "cbfd", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*awdsJFX9yyTGK7Ym9I2TtA.png", + "originalWidth": 1481, + "originalHeight": 758 + } + }, + { + "name": "2c3a", + "text": "Imagine being able to talk to your data like it’s your best friend. That’s what Pandas AI does! This Python library has generative artificial intelligence capabilities that can turn your dataframes into conversationalists. No more endless hours of staring at rows and columns.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "a1ee", + "text": "But don’t worry, Pandas AI is not here to replace your beloved Pandas. It’s here to enhance it! With Pandas AI, you can take your data analysis and manipulation to the next level. Think of it like a superhero sidekick — it’s there to help you save the day and make your life easier.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "5514", + "text": "The possibilities with Pandas AI are endless. Imagine having a dataframe that can write its own reports, or one that can analyze complex data and provide you with easy-to-understand summaries.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "5171", + "text": "In this quick guide, you’ll get a step-by-step walkthrough of how to use this cutting-edge library, regardless of your level of experience in the field.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "ab65", + "text": "Whether you’re an experienced data analyst or a beginner, this guide will equip you with all the tools you need to dive into the world of Pandas AI with confidence. So sit back, relax, and let’s explore the exciting possibilities that Pandas AI has to offer!", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "42f1", + "text": "Official GitHub Repository — https://github.com/gventuri/pandas-ai", + "type": "P", + "href": null, + "layout": null, + "markups": [ + { + "title": "", + "type": "A", + "href": "https://github.com/gventuri/pandas-ai", + "userId": null, + "start": 29, + "end": 66, + "anchorType": "LINK" + }, + { + "title": null, + "type": "STRONG", + "href": null, + "userId": null, + "start": 0, + "end": 28, + "anchorType": null + } + ], + "iframe": null, + "metadata": null + }, + { + "name": "7b4c", + "text": "Code— https://colab.research.google.com/drive/1rKz7TudOeCeKGHekw7JFNL4sagN9hon-?usp=sharing", + "type": "P", + "href": null, + "layout": null, + "markups": [ + { + "title": "", + "type": "A", + "href": "https://colab.research.google.com/drive/1rKz7TudOeCeKGHekw7JFNL4sagN9hon-?usp=sharing", + "userId": null, + "start": 6, + "end": 91, + "anchorType": "LINK" + }, + { + "title": null, + "type": "STRONG", + "href": null, + "userId": null, + "start": 0, + "end": 4, + "anchorType": null + } + ], + "iframe": null, + "metadata": null + }, + { + "name": "ae7c", + "text": "Installing Pandas AI using pip", + "type": "H4", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "51fe", + "text": "pip install pandasai", + "type": "PRE", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "4f69", + "text": "Our DataFrame contains information about various countries, including their GDP (in millions of USD) and happiness index scores. It consists of 10 rows and 3 columns:", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "4b06", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*FvevlIS-mnRSvqg2Nq5JnQ.png", + "originalWidth": 925, + "originalHeight": 809 + } + }, + { + "name": "29ef", + "text": "Importing PandasAI with OpenAI", + "type": "H4", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "2656", + "text": "In the next step, we’ll import the pandasai library that we installed earlier and then import the LLM (Large Language Model) feature. As of May 2023, pandasai only supports the OpenAI model, which we’ll be utilizing understand the data.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "a57a", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*RsVA6lJngSkpSs_BB9yZAg.png", + "originalWidth": 939, + "originalHeight": 409 + } + }, + { + "name": "e33e", + "text": "To use the OpenAI API, you must generate your own unique API key. If you haven’t done so already, you can easily create an account on the platform’s official website at platform.openai.com. Once you’ve created your account, you’ll receive an instant $5 credit that can be used to explore and experiment with the API.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "a3ab", + "text": "Initializing PandasAI and asking Question", + "type": "H4", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "52ae", + "text": "Afterwards, we’ll provide our OpenAI model to Pandas AI and ask various questions.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "37d9", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*UtA6DMSuzKtKOX5mNyfhCA.png", + "originalWidth": 940, + "originalHeight": 226 + } + }, + { + "name": "7578", + "text": "When using pandas_ai.run, two parameters are necessary: the dataframe you’re working with and the question you’re seeking an answer to, it returns the top 5 happiest countries based on the supplied dataframe.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "8f1b", + "text": "Asking Complex Questions", + "type": "H4", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "6388", + "text": "Let’s check whether it can draw the plots for us?", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "1495", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*0mLGIH_2j4K0OC9SZzNrZQ.png", + "originalWidth": 940, + "originalHeight": 153 + } + }, + { + "name": "140d", + "text": "Yes it does plot the graph, based on the question I asked.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "a242", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*zxjcTNyFUFSn2g9aeLrImw.png", + "originalWidth": 900, + "originalHeight": 863 + } + }, + { + "name": "d6ec", + "text": "Let’s perform a complex task, removing NAN values from the below dataset:", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "48b0", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*5wd-jewNp91MNc69DPDe8Q.png", + "originalWidth": 503, + "originalHeight": 481 + } + }, + { + "name": "c589", + "text": "This is the output we get:", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "a830", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*VAEUvjecOWIz5JLtMKLCDg.png", + "originalWidth": 940, + "originalHeight": 137 + } + }, + { + "name": "b01d", + "text": "But when I print the df variable again, it does remove those NAN values from the dataset, removing that row entirely", + "type": "P", + "href": null, + "layout": null, + "markups": [ + { + "title": null, + "type": "STRONG", + "href": null, + "userId": null, + "start": 21, + "end": 23, + "anchorType": null + } + ], + "iframe": null, + "metadata": null + }, + { + "name": "9b78", + "text": "", + "type": "IMG", + "href": null, + "layout": "INSET_CENTER", + "markups": [], + "iframe": null, + "metadata": { + "id": "1*vomhvRkNY_B4v3M7bhoXJA.png", + "originalWidth": 496, + "originalHeight": 400 + } + }, + { + "name": "f25e", + "text": "The pandasai library offers an extensive range of possibilities, and you can explore them all by visiting their official repository page, which I’ve shared earlier.", + "type": "P", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "6d46", + "text": "It’s important to note that working with pandasai involves OpenAI pricing, and you can find the most up-to-date pricing information on their website. As of May 2023, the pricing is approximately 1000 tokens per $0.0200 (for the GPT-3.5-Turbo Model). When posing a question, it’s crucial to remember that the entire dataframe is passed along with the question every time, so it may not be an ideal solution for handling large datasets.", + "type": "H4", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + }, + { + "name": "67b7", + "text": "If you have any query feel free to ask me!", + "type": "H3", + "href": null, + "layout": null, + "markups": [], + "iframe": null, + "metadata": null + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/static/index.css b/static/index.css deleted file mode 100644 index e69de29..0000000