{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Making an Aesop's Fables book"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We're going to take https://aesopfables.com/aesopsel.html, which has a list of 86 Aosop fables, linking to a version of each story and create a children's book containing rhyming versions of the story, along with illustrations in the style of famous artists and art movements."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Set the folder you want to store stuff in below (you may need to make some extra subfolders later as well)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# enter the path to the root of the project here:\n",
    "rootdir = '/Users/thorwhalen/Dropbox/_od/generated/aesop_fables/'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import oa.examples.aesop_fables as aesop"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extracting the fables table"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>moral</th>\n",
       "      <th>title</th>\n",
       "      <th>rel_url</th>\n",
       "      <th>url</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Appearances are deceptive</td>\n",
       "      <td>The Ant and the Chrysalis</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheAntandtheChrysalis</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>One good turn deserves another</td>\n",
       "      <td>The Ant and the Dove</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheAntandtheDove&amp;&amp;antdove.ram</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>It is best to prepare for the days of necessity</td>\n",
       "      <td>The Ant and the Grasshopper</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheAntandtheGrasshopper&amp;&amp;a...</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>He that finds discontentment in one place is n...</td>\n",
       "      <td>The Ass and His Masters</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheAssandHisMasters</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>A man is known by the company he keeps</td>\n",
       "      <td>The Ass and his Purchaser</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheAssandhisPurchaser2</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>81</th>\n",
       "      <td>Every man for himself</td>\n",
       "      <td>The Three Tradesmen</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheThreeTradesmen</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>82</th>\n",
       "      <td>Quality is better than quantity</td>\n",
       "      <td>The Vixen and the Lioness</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheVixenandtheLioness</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>83</th>\n",
       "      <td>It is easy to be brave from a safe distance</td>\n",
       "      <td>The Wolf and the Kid</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheWolfandtheKid</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>84</th>\n",
       "      <td>Appearances are deceptive</td>\n",
       "      <td>The Wolf in Sheep's Clothing</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheWolfinSheepsClothing2</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>85</th>\n",
       "      <td>No gratitude from the wicked</td>\n",
       "      <td>The Woodman and the Serpent</td>\n",
       "      <td>/cgi/aesop1.cgi?sel&amp;TheWoodmanandtheSerpent</td>\n",
       "      <td>https://aesopfables.com//cgi/aesop1.cgi?sel&amp;Th...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>86 rows × 4 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                moral  \\\n",
       "0                           Appearances are deceptive   \n",
       "1                      One good turn deserves another   \n",
       "2     It is best to prepare for the days of necessity   \n",
       "3   He that finds discontentment in one place is n...   \n",
       "4              A man is known by the company he keeps   \n",
       "..                                                ...   \n",
       "81                              Every man for himself   \n",
       "82                    Quality is better than quantity   \n",
       "83        It is easy to be brave from a safe distance   \n",
       "84                          Appearances are deceptive   \n",
       "85                       No gratitude from the wicked   \n",
       "\n",
       "                           title  \\\n",
       "0      The Ant and the Chrysalis   \n",
       "1           The Ant and the Dove   \n",
       "2    The Ant and the Grasshopper   \n",
       "3        The Ass and His Masters   \n",
       "4      The Ass and his Purchaser   \n",
       "..                           ...   \n",
       "81           The Three Tradesmen   \n",
       "82     The Vixen and the Lioness   \n",
       "83          The Wolf and the Kid   \n",
       "84  The Wolf in Sheep's Clothing   \n",
       "85   The Woodman and the Serpent   \n",
       "\n",
       "                                              rel_url  \\\n",
       "0           /cgi/aesop1.cgi?sel&TheAntandtheChrysalis   \n",
       "1   /cgi/aesop1.cgi?sel&TheAntandtheDove&&antdove.ram   \n",
       "2   /cgi/aesop1.cgi?sel&TheAntandtheGrasshopper&&a...   \n",
       "3             /cgi/aesop1.cgi?sel&TheAssandHisMasters   \n",
       "4          /cgi/aesop1.cgi?sel&TheAssandhisPurchaser2   \n",
       "..                                                ...   \n",
       "81              /cgi/aesop1.cgi?sel&TheThreeTradesmen   \n",
       "82          /cgi/aesop1.cgi?sel&TheVixenandtheLioness   \n",
       "83               /cgi/aesop1.cgi?sel&TheWolfandtheKid   \n",
       "84       /cgi/aesop1.cgi?sel&TheWolfinSheepsClothing2   \n",
       "85        /cgi/aesop1.cgi?sel&TheWoodmanandtheSerpent   \n",
       "\n",
       "                                                  url  \n",
       "0   https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "1   https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "2   https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "3   https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "4   https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "..                                                ...  \n",
       "81  https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "82  https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "83  https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "84  https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "85  https://aesopfables.com//cgi/aesop1.cgi?sel&Th...  \n",
       "\n",
       "[86 rows x 4 columns]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import dol\n",
    "\n",
    "files = dol.Files(rootdir)\n",
    "\n",
    "fable_table = aesop.get_fable_table(files)\n",
    "fable_table"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Making some stores"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here, we'll use `dol` to make some \"stores\" -- that is, a `MutableMapping` facade to where we'll store stuff (our fables text, rhyming stories, illustration urls, images...). \n",
    "\n",
    "We'll store things in local files here, but we can change this to use S3, DBs, etc. simply by changing the backend of the facade."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from functools import partial\n",
    "\n",
    "fullpath = partial(os.path.join, rootdir, 'elements')\n",
    "original_stories = aesop.Texts(fullpath('original_stories'))\n",
    "rhyming_stories = aesop.Texts(fullpath('rhyming_stories'))\n",
    "image_descriptions = aesop.Texts(fullpath('image_descriptions'))\n",
    "image_urls = aesop.Texts(fullpath('image_urls'))\n",
    "images = aesop.Images(fullpath('images'))\n",
    "htmls = aesop.Htmls(fullpath('htmls'))\n",
    "\n",
    "# make the dirs automatically, if they don't exist\n",
    "for s in [\n",
    "        original_stories, rhyming_stories, \n",
    "        image_descriptions, image_urls, images\n",
    "    ]:\n",
    "    if not os.path.exists(s.rootdir):\n",
    "        os.makedirs(s.rootdir)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extract and store the text of each fable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "aesop.get_original_stories(fable_table, original_stories)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Make rhyming stories"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use chatGPT to create (and store) rhyming versions of the fables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/85: The Heifer and the Ox\n",
      "2/85: Hercules and the Waggoner\n",
      "3/85: The Kings Son and the Painted Lion\n",
      "4/85: The Oxen and the Axle Trees\n",
      "5/85: The Ass the Fox and the Lion\n",
      "6/85: The Bald Man and the Fly\n",
      "7/85: The Bee and Jupiter\n",
      "8/85: The Crow and the Pitcher\n",
      "9/85: The Mule\n",
      "10/85: The Shipwrecked Impostor\n",
      "11/85: The Eagle and the Fox\n",
      "12/85: The Lion and the Eagle\n",
      "13/85: The Thief and the Innkeeper\n",
      "14/85: The Boy and the Filberts\n",
      "15/85: The Rose and the Amaranth\n",
      "16/85: The Fox and the Hedgehog\n",
      "17/85: The Shepherds Boy and the Wolf\n",
      "18/85: The Ass and His Masters\n",
      "19/85: The Three Tradesmen\n",
      "20/85: The Hunter and the Woodman\n",
      "21/85: The Vixen and the Lioness\n",
      "22/85: The Ant and the Dove\n",
      "23/85: The Wolf and the Kid\n",
      "24/85: The Ass and his Purchaser\n",
      "25/85: The Goose With the Golden Eggs\n",
      "26/85: The Eagle and the Arrow\n",
      "27/85: The Ass in the Lion's Skin\n",
      "28/85: The Fox and the Goat\n",
      "29/85: The Hare With Many Friends\n",
      "30/85: The Eagle the Cat and the Wild Sow\n",
      "31/85: The Horse Hunter and Stag\n",
      "32/85: The Hart in the Ox-Stall\n",
      "33/85: The Old Woman and the Physician\n",
      "34/85: The Miser\n",
      "35/85: The Frogs Desiring a King\n",
      "36/85: The Frogs and the Well\n",
      "37/85: The Lion's Share\n",
      "38/85: The Miller His Son and Their Ass\n",
      "39/85: The Man and the Satyr\n",
      "40/85: The Lion the Bear and the Fox\n",
      "41/85: The Old Woman and the Wine Jar\n",
      "42/85: The Boy and the Nettles\n",
      "43/85: The Dove and the Ant\n",
      "44/85: The Ant and the Grasshopper\n",
      "45/85: The Four Oxen and the Lion\n",
      "46/85: The Man Bitten by a Dog\n",
      "47/85: The Dog and the Hare\n",
      "48/85: The Nurse and the Wolf\n",
      "49/85: The Man the Boy and the Donkey\n",
      "50/85: The Kid and the Wolf\n",
      "51/85: The Bat the Birds and the Beasts\n",
      "52/85: The Monkey and the Dolphin\n",
      "53/85: The Jay and the Peacock\n",
      "54/85: The Hare and the Tortoise\n",
      "55/85: Mercury and the Woodman\n",
      "56/85: The Father and His Two Daughters\n",
      "57/85: The Sick Stag\n",
      "58/85: The Lion in Love\n",
      "59/85: The Cat and Venus\n",
      "60/85: The Woodman and the Serpent\n",
      "61/85: The Fox and the Grapes\n",
      "62/85: The Milkmaid and Her Pail\n",
      "63/85: The Dog and the Wolf\n",
      "64/85: The Seagull and the Kite\n",
      "65/85: The Ant and the Chrysalis\n",
      "66/85: The Blind Man and the Whelp\n",
      "67/85: The Hart and the Hunter\n",
      "68/85: The Swallow and the Crow\n",
      "69/85: The Serpent and the Eagle\n",
      "70/85: The Boys and the Frogs\n",
      "71/85: The Frogs Asking for King\n",
      "72/85: The Wolf in Sheep's Clothing\n",
      "73/85: The Dog in the Manger\n",
      "74/85: The Bear and the Two Travelers\n",
      "75/85: The Fox Who Had Lost His Tail\n",
      "76/85: The Hares and the Frogs\n",
      "77/85: The Peacock and the Crane\n",
      "78/85: The Serpent and the File\n",
      "79/85: The Lion and the Mouse\n",
      "80/85: The One Eyed Doe\n",
      "81/85: The Dancing Monkeys\n",
      "82/85: The Farmer and the Stork\n",
      "83/85: The Dogs and the Fox\n",
      "84/85: The Horse and Groom\n",
      "85/85: The Monkeys and Their Mother\n"
     ]
    }
   ],
   "source": [
    "aesop.get_rhyming_stories(original_stories, rhyming_stories=rhyming_stories)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Make illustrations"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use chatGPT to create (and store) a description of an illustration, and ask dall-E to make such an image (and store the url)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "aesop.get_image_descriptions(\n",
    "    rhyming_stories, \n",
    "    image_descriptions=image_descriptions,\n",
    "    image_urls=image_urls,\n",
    "    images=images,\n",
    "    image_styles=aesop.styles_of_art,\n",
    "    launch_iteration=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/60: The Heifer and the Ox\n",
      "2/60: Hercules and the Waggoner\n",
      "3/60: The Kings Son and the Painted Lion\n",
      "4/60: The Oxen and the Axle Trees\n",
      "5/60: The Bald Man and the Fly\n",
      "6/60: The Bee and Jupiter\n",
      "7/60: The Crow and the Pitcher\n",
      "8/60: The Mule\n",
      "9/60: The Shipwrecked Impostor\n",
      "10/60: The Lion and the Eagle\n",
      "11/60: The Thief and the Innkeeper\n",
      "12/60: The Rose and the Amaranth\n",
      "13/60: The Fox and the Hedgehog\n",
      "14/60: The Shepherds Boy and the Wolf\n",
      "15/60: The Ass and His Masters\n",
      "16/60: The Three Tradesmen\n",
      "17/60: The Vixen and the Lioness\n",
      "18/60: The Ant and the Dove\n",
      "19/60: The Wolf and the Kid\n",
      "20/60: The Goose With the Golden Eggs\n",
      "21/60: The Eagle and the Arrow\n",
      "22/60: The Horse Hunter and Stag\n",
      "23/60: The Hart in the Ox-Stall\n",
      "24/60: The Old Woman and the Physician\n",
      "25/60: The Lion's Share\n",
      "26/60: The Lion the Bear and the Fox\n",
      "27/60: The Old Woman and the Wine Jar\n",
      "28/60: The Boy and the Nettles\n",
      "29/60: The Dove and the Ant\n",
      "30/60: The Ant and the Grasshopper\n",
      "31/60: The Four Oxen and the Lion\n",
      "32/60: The Man Bitten by a Dog\n",
      "33/60: The Nurse and the Wolf\n",
      "34/60: The Man the Boy and the Donkey\n",
      "35/60: The Kid and the Wolf\n",
      "36/60: The Bat the Birds and the Beasts\n",
      "37/60: The Jay and the Peacock\n",
      "38/60: The Hare and the Tortoise\n",
      "39/60: The Father and His Two Daughters\n",
      "40/60: The Sick Stag\n",
      "41/60: The Lion in Love\n",
      "42/60: The Cat and Venus\n",
      "43/60: The Woodman and the Serpent\n",
      "44/60: The Fox and the Grapes\n",
      "45/60: The Dog and the Wolf\n",
      "46/60: The Seagull and the Kite\n",
      "47/60: The Ant and the Chrysalis\n",
      "48/60: The Hart and the Hunter\n",
      "49/60: The Swallow and the Crow\n",
      "50/60: The Boys and the Frogs\n",
      "51/60: The Frogs Asking for King\n",
      "52/60: The Bear and the Two Travelers\n",
      "The description or url for The Bear and the Two Travelers lead to the error ('Connection aborted.', ConnectionResetError(54, 'Connection reset by peer')). Description:\n",
      "\n",
      "The illustration would show the two friends hiking together in the woods, with trees and bushes surrounding them. In the background, the figure of a large bear is visible, slowly approaching the two friends. One of them is frozen in fear, while the other quickly climbs up a nearby tree to escape the bear's reach. The bear sniffs around the frozen friend, who is lying on the ground, pretending to be dead. The bear eventually leaves and the friend in the tree comes down, looking relieved but also a bit shaken. The two friends then join together, with one of them holding the other's hand, symbolizing their unbreakable bond and commitment to being there for each other, no matter what challenges they may face in their journey of life. The illustration would be done in Gustave Caillebotte's signature style, with bold brushstrokes, vivid colors, and an almost photographic attention to detail.\n",
      "\n",
      "\n",
      "\n",
      "53/60: The Fox Who Had Lost His Tail\n",
      "54/60: The Hares and the Frogs\n",
      "55/60: The Peacock and the Crane\n",
      "56/60: The Serpent and the File\n",
      "57/60: The Lion and the Mouse\n",
      "58/60: The Dancing Monkeys\n",
      "59/60: The Farmer and the Stork\n",
      "60/60: The Monkeys and Their Mother\n"
     ]
    }
   ],
   "source": [
    "aesop.get_images(\n",
    "    image_descriptions=image_descriptions,\n",
    "    images=images,\n",
    "    image_styles=aesop.styles_of_art,\n",
    "    overwrite=False,\n",
    "    print_progress=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1/60: The Heifer and the Ox\n",
      "1/60: The Heifer and the Ox\n"
     ]
    }
   ],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "len(original_stories)=85\n",
      "len(rhyming_stories)=85\n",
      "len(image_descriptions)=60\n",
      "len(image_urls)=60\n",
      "\n",
      "len(missing_rhyming_stories)=0\n",
      "len(missing_descriptions)=25\n",
      "len(missing_urls)=0\n"
     ]
    }
   ],
   "source": [
    "aesop.store_stats(\n",
    "    original_stories=original_stories, \n",
    "    rhyming_stories=rhyming_stories, \n",
    "    image_descriptions=image_descriptions, \n",
    "    image_urls=image_urls,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "len(missing_descriptions)=25 and len(missing_urls)=0\n"
     ]
    }
   ],
   "source": [
    "# missing_descriptions = set(rhyming_stories) - set(image_descriptions)\n",
    "# missing_urls = set(image_descriptions) - set(image_urls)\n",
    "# print(f\"{len(missing_descriptions)=} and {len(missing_urls)=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# # Some urls were missing -- probably due to description too big, or sensored.\n",
    "# # Delete the descriptions when urls are missing.\n",
    "# for k in missing_urls:\n",
    "#     del image_descriptions[k]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "def format_html(template, **kwargs):\n",
    "    for key, value in kwargs.items():\n",
    "        template = template.replace(f'[[{key}]]', str(value))\n",
    "        # template = template.replace('”', '\"')  # TODO: Why are the quotes appearing anyway?\n",
    "    return template\n",
    "\n",
    "template = \"\"\"<!DOCTYPE html>\n",
    "<html>\n",
    "<head>\n",
    "    <style>\n",
    "        .container {\n",
    "            display: grid;\n",
    "            grid-template-columns: 1fr 1fr;\n",
    "            height: 100vh;\n",
    "        }\n",
    "        .text-area, .image-area {\n",
    "            border: 1px solid black;\n",
    "            padding: 20px;\n",
    "            box-sizing: border-box;\n",
    "            height: 100%;\n",
    "            width: 100%;\n",
    "        }\n",
    "        .text-area {\n",
    "            column-count: 2;\n",
    "            column-fill: auto;\n",
    "            column-gap: 20px;\n",
    "            text-align: justify;\n",
    "        }\n",
    "        .image-area {\n",
    "            background: url('your-image-url') no-repeat center center;\n",
    "            background-size: cover;\n",
    "        }\n",
    "    </style>\n",
    "</head>\n",
    "<body>\n",
    "    <div class=\"container\">\n",
    "        <div class=\"text-area\" id=\"text-area\">\n",
    "        [[story]]\n",
    "        </div>\n",
    "        <div class=\"image-area\">\n",
    "         <img src=\"[[image]]\">\n",
    "        </div>\n",
    "    </div>\n",
    "<script>\n",
    "    var textArea = document.getElementById('text-area');\n",
    "    function fitText() {\n",
    "        var fontSize = 100;\n",
    "        do {\n",
    "            fontSize--;\n",
    "            textArea.style.fontSize = fontSize + '%';\n",
    "        } while(textArea.scrollHeight > textArea.offsetHeight);\n",
    "    }\n",
    "    window.onresize = fitText;\n",
    "    fitText();\n",
    "</script>\n",
    "</body>\n",
    "</html>\"\"\"\n",
    "\n",
    "\n",
    "# <style>\n",
    "#     .poem {\n",
    "#         white-space: pre-line;\n",
    "#     }\n",
    "# </style>\n",
    "# <div class=\"poem\">\n",
    "#     <!-- Insert poem here -->\n",
    "# </div>\n",
    "\n",
    "from urllib.parse import quote\n",
    "\n",
    "for k in rhyming_stories:\n",
    "    story = rhyming_stories[k]\n",
    "    story = story.replace('\\n', '<br>')  # alt: f'<pre>{story}</pre>' or white-space css\n",
    "    image = quote(f\"../images/{k}.jpg\")\n",
    "    htmls[k] = format_html(template, story=story, image=image)\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'/Users/thorwhalen/Dropbox/_od/generated/aesop_fables/elements/htmls/'"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "import pdfkit\n",
    "from jinja2 import Template\n",
    "\n",
    "# HTML template\n",
    "html_template = \"\"\"\n",
    "<!DOCTYPE html>\n",
    "<html>\n",
    "<head>\n",
    "    <style>\n",
    "        .container {\n",
    "            display: flex;\n",
    "            justify-content: space-between;\n",
    "        }\n",
    "        .text {\n",
    "            width: 50%;\n",
    "        }\n",
    "        .image {\n",
    "            width: 50%;\n",
    "            text-align: center;\n",
    "        }\n",
    "    </style>\n",
    "</head>\n",
    "<body>\n",
    "    <div class=\"container\">\n",
    "        <div class=\"text\">\n",
    "            <pre>{{ text }}</pre>\n",
    "        </div>\n",
    "        <div class=\"image\">\n",
    "            <img src=\"data:image/jpeg;base64,{{ image }}\">\n",
    "        </div>\n",
    "    </div>\n",
    "</body>\n",
    "</html>\n",
    "\"\"\"\n",
    "\n",
    "# Jinja2 template\n",
    "template = Template(html_template)\n",
    "\n",
    "# List of file names (without extension)\n",
    "file_names = [...]  # Fill in with your file names\n",
    "\n",
    "# Directory paths\n",
    "text_directory = 'texts'\n",
    "image_directory = 'images'\n",
    "\n",
    "# Loop through each file\n",
    "for file_name in rhyming_stories:\n",
    "    # Read text\n",
    "    # with open(os.path.join(text_directory, f'{file_name}.txt'), 'r') as f:\n",
    "    #     text = f.read()\n",
    "\n",
    "    # # Read image\n",
    "    # with open(os.path.join(image_directory, f'{file_name}.jpg'), 'rb') as f:\n",
    "    #     image = f.read()\n",
    "\n",
    "    # Encode image in base64\n",
    "    image_encoded = base64.b64encode(image).decode('utf-8')\n",
    "\n",
    "    # Render HTML\n",
    "    html = template.render(text=text, image=image_encoded)\n",
    "\n",
    "    # Convert to PDF\n",
    "    pdfkit.from_string(html, f'{file_name}.pdf')\n",
    "    break\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "ename": "OSError",
     "evalue": "No wkhtmltopdf executable found: \"b''\"\nIf this file exists please check that this process can read it or you can pass path to it manually in method call, check README. Otherwise please install wkhtmltopdf - https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "File \u001b[0;32m~/.pyenv/versions/3.10.8/lib/python3.10/site-packages/pdfkit/configuration.py:35\u001b[0m, in \u001b[0;36mConfiguration.__init__\u001b[0;34m(self, wkhtmltopdf, meta_tag_prefix, environ)\u001b[0m\n\u001b[1;32m     33\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwkhtmltopdf \u001b[39m=\u001b[39m lines[\u001b[39m0\u001b[39m]\u001b[39m.\u001b[39mstrip()\n\u001b[0;32m---> 35\u001b[0m \u001b[39mwith\u001b[39;00m \u001b[39mopen\u001b[39;49m(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mwkhtmltopdf) \u001b[39mas\u001b[39;00m f:\n\u001b[1;32m     36\u001b[0m     \u001b[39mpass\u001b[39;00m\n",
      "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: b''",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31mOSError\u001b[0m                                   Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[56], line 18\u001b[0m\n\u001b[1;32m      6\u001b[0m html_files \u001b[39m=\u001b[39m [os\u001b[39m.\u001b[39mpath\u001b[39m.\u001b[39mjoin(htmls\u001b[39m.\u001b[39mrootdir, \u001b[39mf\u001b[39m\u001b[39m'\u001b[39m\u001b[39m{\u001b[39;00mk\u001b[39m}\u001b[39;00m\u001b[39m.html\u001b[39m\u001b[39m'\u001b[39m) \u001b[39mfor\u001b[39;00m k \u001b[39min\u001b[39;00m htmls]\n\u001b[1;32m      8\u001b[0m options \u001b[39m=\u001b[39m {\n\u001b[1;32m      9\u001b[0m     \u001b[39m'\u001b[39m\u001b[39mpage-size\u001b[39m\u001b[39m'\u001b[39m: \u001b[39m'\u001b[39m\u001b[39mLetter\u001b[39m\u001b[39m'\u001b[39m,\n\u001b[1;32m     10\u001b[0m     \u001b[39m'\u001b[39m\u001b[39mmargin-top\u001b[39m\u001b[39m'\u001b[39m: \u001b[39m'\u001b[39m\u001b[39m0mm\u001b[39m\u001b[39m'\u001b[39m,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m     15\u001b[0m     \u001b[39m'\u001b[39m\u001b[39mno-outline\u001b[39m\u001b[39m'\u001b[39m: \u001b[39mNone\u001b[39;00m\n\u001b[1;32m     16\u001b[0m }\n\u001b[0;32m---> 18\u001b[0m pdfkit\u001b[39m.\u001b[39;49mfrom_file(html_files, \u001b[39m'\u001b[39;49m\u001b[39maesop_fables.pdf\u001b[39;49m\u001b[39m'\u001b[39;49m, options\u001b[39m=\u001b[39;49moptions)\n",
      "File \u001b[0;32m~/.pyenv/versions/3.10.8/lib/python3.10/site-packages/pdfkit/api.py:48\u001b[0m, in \u001b[0;36mfrom_file\u001b[0;34m(input, output_path, options, toc, cover, css, configuration, cover_first, verbose)\u001b[0m\n\u001b[1;32m     30\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mfrom_file\u001b[39m(\u001b[39minput\u001b[39m, output_path\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, options\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, toc\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, cover\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, css\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m,\n\u001b[1;32m     31\u001b[0m               configuration\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, cover_first\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m, verbose\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m):\n\u001b[1;32m     32\u001b[0m     \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m     33\u001b[0m \u001b[39m    Convert HTML file or files to PDF document\u001b[39;00m\n\u001b[1;32m     34\u001b[0m \n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m     45\u001b[0m \u001b[39m    Returns: True on success\u001b[39;00m\n\u001b[1;32m     46\u001b[0m \u001b[39m    \"\"\"\u001b[39;00m\n\u001b[0;32m---> 48\u001b[0m     r \u001b[39m=\u001b[39m PDFKit(\u001b[39minput\u001b[39;49m, \u001b[39m'\u001b[39;49m\u001b[39mfile\u001b[39;49m\u001b[39m'\u001b[39;49m, options\u001b[39m=\u001b[39;49moptions, toc\u001b[39m=\u001b[39;49mtoc, cover\u001b[39m=\u001b[39;49mcover, css\u001b[39m=\u001b[39;49mcss,\n\u001b[1;32m     49\u001b[0m                configuration\u001b[39m=\u001b[39;49mconfiguration, cover_first\u001b[39m=\u001b[39;49mcover_first, verbose\u001b[39m=\u001b[39;49mverbose)\n\u001b[1;32m     51\u001b[0m     \u001b[39mreturn\u001b[39;00m r\u001b[39m.\u001b[39mto_pdf(output_path)\n",
      "File \u001b[0;32m~/.pyenv/versions/3.10.8/lib/python3.10/site-packages/pdfkit/pdfkit.py:45\u001b[0m, in \u001b[0;36mPDFKit.__init__\u001b[0;34m(self, url_or_file, type_, options, toc, cover, css, configuration, cover_first, verbose)\u001b[0m\n\u001b[1;32m     41\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m__init__\u001b[39m(\u001b[39mself\u001b[39m, url_or_file, type_, options\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, toc\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, cover\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m,\n\u001b[1;32m     42\u001b[0m              css\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, configuration\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m, cover_first\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m, verbose\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m):\n\u001b[1;32m     44\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39msource \u001b[39m=\u001b[39m Source(url_or_file, type_)\n\u001b[0;32m---> 45\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mconfiguration \u001b[39m=\u001b[39m (Configuration() \u001b[39mif\u001b[39;00m configuration \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m\n\u001b[1;32m     46\u001b[0m                           \u001b[39melse\u001b[39;00m configuration)\n\u001b[1;32m     47\u001b[0m     \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m     48\u001b[0m         \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwkhtmltopdf \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mconfiguration\u001b[39m.\u001b[39mwkhtmltopdf\u001b[39m.\u001b[39mdecode(\u001b[39m'\u001b[39m\u001b[39mutf-8\u001b[39m\u001b[39m'\u001b[39m)\n",
      "File \u001b[0;32m~/.pyenv/versions/3.10.8/lib/python3.10/site-packages/pdfkit/configuration.py:38\u001b[0m, in \u001b[0;36mConfiguration.__init__\u001b[0;34m(self, wkhtmltopdf, meta_tag_prefix, environ)\u001b[0m\n\u001b[1;32m     36\u001b[0m         \u001b[39mpass\u001b[39;00m\n\u001b[1;32m     37\u001b[0m \u001b[39mexcept\u001b[39;00m (\u001b[39mIOError\u001b[39;00m, \u001b[39mFileNotFoundError\u001b[39;00m) \u001b[39mas\u001b[39;00m e:\n\u001b[0;32m---> 38\u001b[0m     \u001b[39mraise\u001b[39;00m \u001b[39mIOError\u001b[39;00m(\u001b[39m'\u001b[39m\u001b[39mNo wkhtmltopdf executable found: \u001b[39m\u001b[39m\"\u001b[39m\u001b[39m%s\u001b[39;00m\u001b[39m\"\u001b[39m\u001b[39m\\n\u001b[39;00m\u001b[39m'\u001b[39m\n\u001b[1;32m     39\u001b[0m                   \u001b[39m'\u001b[39m\u001b[39mIf this file exists please check that this process can \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m     40\u001b[0m                   \u001b[39m'\u001b[39m\u001b[39mread it or you can pass path to it manually in method call, \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m     41\u001b[0m                   \u001b[39m'\u001b[39m\u001b[39mcheck README. Otherwise please install wkhtmltopdf - \u001b[39m\u001b[39m'\u001b[39m\n\u001b[1;32m     42\u001b[0m                   \u001b[39m'\u001b[39m\u001b[39mhttps://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf\u001b[39m\u001b[39m'\u001b[39m \u001b[39m%\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwkhtmltopdf)\n\u001b[1;32m     45\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39menviron \u001b[39m=\u001b[39m environ\n\u001b[1;32m     47\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39menviron:\n",
      "\u001b[0;31mOSError\u001b[0m: No wkhtmltopdf executable found: \"b''\"\nIf this file exists please check that this process can read it or you can pass path to it manually in method call, check README. Otherwise please install wkhtmltopdf - https://github.com/JazzCore/python-pdfkit/wiki/Installing-wkhtmltopdf"
     ]
    }
   ],
   "source": [
    "import pdfkit\n",
    "\n",
    "# List of your HTML files\n",
    "# html_files = ['file1.html', 'file2.html', 'file3.html', ...]\n",
    "\n",
    "html_files = [os.path.join(htmls.rootdir, f'{k}.html') for k in htmls]\n",
    "\n",
    "options = {\n",
    "    'page-size': 'Letter',\n",
    "    'margin-top': '0mm',\n",
    "    'margin-right': '0mm',\n",
    "    'margin-bottom': '0mm',\n",
    "    'margin-left': '0mm',\n",
    "    'encoding': \"UTF-8\",\n",
    "    'no-outline': None\n",
    "}\n",
    "\n",
    "pdfkit.from_file(html_files, 'aesop_fables.pdf', options=options)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "from gtts import gTTS\n",
    "import os\n",
    "\n",
    "text = \"\"\"\n",
    "C'était l'heure du goûter dans la famille de Cora, \n",
    "(Maintenant écrit les mots: heure, goûter, famille) 1, 2, 3\n",
    "La joyeuse petite fille aux cheveux bleus. \n",
    "(Maintenant écrit les mots: joyeuse, cheveux, bleus) 1, 2, 3\n",
    "\n",
    "Sa sœur, qui est nageuse, est rentrée de l'école \n",
    "(Maintenant écrit les mots: sœur, nageuse, école) 1, 2, 3\n",
    "\n",
    "Avec une faim de loup. \n",
    "(Maintenant écrit les mots: faim, loup) 1, 2, 3\n",
    "\n",
    "Elles ont décidé de faire un gâteau, un de leurs jeux préférés. \n",
    "(Maintenant écrit les mots: gâteau, jeux) 1, 2, 3\n",
    "\n",
    "Avec la pomme du bol sur la table et l'oeuf du frigo, elles se sont mises au travail.\n",
    "(Maintenant écrit les mots: pomme, oeuf) 1, 2, 3\n",
    "\n",
    "Un peu de beurre, un peu de farine, et la pâte a commencé à grouiller sous leurs mains. \n",
    "(Maintenant écrit les mots: beurre, farine, pâte) 1, 2, 3\n",
    "\n",
    "La plus petite s'est même amusée à y mettre son nez! \n",
    "(Maintenant écrit les mots: petite, nez) 1, 2, 3\n",
    "\n",
    "Elles ont versé la pâte dans un moule en forme de dauphin. \n",
    "(Maintenant écrit les mots: pâte, moule, dauphin) 1, 2, 3\n",
    "\n",
    "Cora, habillée de son tablier à rayures et sa botte en main, \n",
    "(Maintenant écrit les mots: tablier, rayures, botte) 1, 2, 3\n",
    "\n",
    "A décidé de le peindre de couleur citron \n",
    "(Maintenant écrit les mots: peindre, couleur, citron) 1, 2, 3\n",
    "\n",
    "Pour y ajouter un peu de soleil à cet hiver froid. Ensuite, elles l'ont mis au four. \n",
    "(Maintenant écrit les mots: soleil, hiver, froid, four) 1, 2, 3\n",
    "\n",
    "Pendant ce temps, la télévision diffusait un match de sport, \n",
    "(Maintenant écrit les mots: télévision, match, sport) 1, 2, 3\n",
    "\n",
    "Et leur chien, un grand berger\n",
    "allemand nommé Lundi, s'était assis devant l'écran. \n",
    "(Maintenant écrit les mots: chien, Lundi, écran) 1, 2, 3\n",
    "\n",
    "La mère a alors sorti du bois pour le feu. \n",
    "(Maintenant écrit les mots: mère, bois, feu) 1, 2, 3\n",
    "\n",
    "Le père, un fermier, rentré de son travail aux champs, \n",
    "(Maintenant écrit les mots: père, fermier, travail, champs) 1, 2, 3\n",
    "\n",
    "S'est assis dans son fauteuil préféré pour se reposer. \n",
    "(Maintenant écrit les mots: fauteuil, reposer) 1, 2, 3\n",
    "\n",
    "Après un moment, une délicieuse odeur de gâteau a rempli la maison. \n",
    "(Maintenant écrit les mots: odeur, gâteau, maison) 1, 2, 3\n",
    "\n",
    "Elles ont ouvert le four et là, un magnifique gâteau doré les attendait.\n",
    "(Maintenant écrit les mots: four, gâteau, doré) 1, 2, 3\n",
    "\n",
    "Avec une cuillère, elles ont ajouté une cerise sur le dessus pour le plaisir gourmand. \n",
    "(Maintenant écrit les mots: cuillère, cerise, gourmand) 1, 2, 3\n",
    "\n",
    "\"Maintenant, c'est l'heure de goûter!\", a crié Cora. \n",
    "(Maintenant écrit les mots: heure, goûter, Cora) 1, 2, 3\n",
    "\n",
    "Tout le monde s'est rassemblé autour de la table. Le goûter a commencé, et quel régal! \n",
    "(Maintenant écrit les mots: monde, table, goûter, régal) 1, 2, 3\n",
    "\n",
    "C'était le meilleur gâteau qu'elles aient jamais fait. Le bonheur était palpable, comme un douillet coussin. \n",
    "(Maintenant écrit les mots: gâteau, bonheur, douillet, coussin) 1, 2, 3\n",
    "\n",
    "Elles ont terminé la journée par une promenade, marchant ensemble,\n",
    "(Maintenant écrit les mots: journée, promenade, marchant) 1, 2, 3\n",
    "\n",
    "Sous le ciel bleu d'hiver. \n",
    "(Maintenant écrit les mots: ciel, bleu, hiver) 1, 2, 3\n",
    "\n",
    "La neige recouvrait le sol, \n",
    "(Maintenant écrit les mots: neige, sol) 1, 2, 3\n",
    "\n",
    "Et on pouvait voir leurs empreintes de pied en rentrant à la maison. \n",
    "(Maintenant écrit les mots: empreintes, pied, maison) 1, 2, 3\n",
    "\n",
    "Enfin, après cette longue journée, Cora est allée se coucher, avec un sourire heureux sur son visage. \n",
    "(Maintenant écrit les mots: journée, Cora, coucher, heureux, visage) 1, 2, 3\n",
    "\n",
    "\"\"\"\n",
    "\n",
    "tts = gTTS(text=text, lang='fr')\n",
    "tts.save(os.path.expanduser(\"~/histoire_avec_mots_a_apprendre_avec_pauses.mp3\"))"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Poking around"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "title='The Heifer and the Ox'\n"
     ]
    }
   ],
   "source": [
    "title = next(iter(original_stories), None)\n",
    "print(f\"{title=}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The Heifer and the Ox \n",
      "\n",
      "\n",
      "  A HEIFER saw an Ox hard at work harnessed to a plow, and\n",
      "tormented him with reflections on his unhappy fate in being\n",
      "compelled to labor.  Shortly afterwards, at the harvest festival,\n",
      "the owner released the Ox from his yoke, but bound the Heifer\n",
      "with cords and led him away to the altar to be slain in honor of\n",
      "the occasion.  The Ox saw what was being done, and said with a\n",
      "smile to the Heifer:  \"For this you were allowed to live in\n",
      "idleness, because you were presently to be sacrificed.\"\n"
     ]
    }
   ],
   "source": [
    "print(original_stories[title])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Once there was a heifer,\n",
      "Whose life was quite nice,\n",
      "She grazed in the pasture,\n",
      "And didn't think twice.\n",
      "\n",
      "She saw an ox plowing,\n",
      "Hard at work in the fields,\n",
      "And thought to herself,\n",
      "\"My fate's much more ideal.\"\n",
      "\n",
      "But oh, how she erred,\n",
      "For soon came the harvest fair,\n",
      "The ox was freed from his labor,\n",
      "And the heifer, they did ensnare.\n",
      "\n",
      "They tied her up with cords,\n",
      "And led her to the altar,\n",
      "Where she was to be sacrificed,\n",
      "In honor of the harvest's flair.\n",
      "\n",
      "The ox chuckled and said,\n",
      "\"As you lay here in idleness,\n",
      "You didn't think of the future,\n",
      "Which now seems quite senseless.\"\n",
      "\n",
      "So, dear children, take note,\n",
      "Don't compare with those around,\n",
      "For sometimes hard work,\n",
      "Is really a blessing that's found.\n"
     ]
    }
   ],
   "source": [
    "print(rhyming_stories[title])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "60"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = aesop.mk_pages_store(\n",
    "    rhyming_stories=rhyming_stories, image_urls=image_urls, ipython_display=False\n",
    ")\n",
    "len(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<html>\n",
      "    <body>\n",
      "    <img src=\"https://oaidalleapiprodscus.blob.core.windows.net/private/org-AY3lr3H3xB9yPQ0HGR498f9M/user-7ZNCDYLWzP0GT48V6DCiTFWt/img-PEoJsYRgYjPcpp2aYRqDvMc3.png?st=2023-05-09T09%3A50%3A53Z&se=2023-05-09T11%3A50%3A53Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2023-05-08T20%3A05%3A01Z&ske=2023-05-09T20%3A05%3A01Z&sks=b&skv=2021-08-06&sig=UGnOmHBRS6w4Y4CEN1ebziLej5KlCkeJNYJ6SHHGJPc%3D\" />\n",
      "    <p><p>Once there was a clever fox,<br>He loved to stroll and he loved to talk.<br>One day he wandered and came across<br>A vine of grapes, as sweet as a summer&#x27;s toss.<br><br>The grapes were plump and juicy,<br>The fox couldn&#x27;t resist their beauty.<br>He tried to jump and grab each one,<br>But alas, his attempts came undone.<br><br>One, two, three, he tried so hard,<br>But the grapes remained far apart.<br>In frustration, he finally gave up,<br>And walked away feeling bitter and rough.<br><br>&quot;This must be it,&quot; he thought, &quot;they&#x27;re sour,<br>I&#x27;ll find juicier grapes with much more power.&quot;<br>But deep down, he knew it wasn&#x27;t true,<br>For the grapes he couldn&#x27;t have were the only ones that would do.<br><br>So, next time you covet what you can&#x27;t get,<br>Remember the fox and his grapes, don&#x27;t fret.<br>Work hard and try your very best,<br>And sometimes what you want will be yours to possess.</p></p>\n",
      "    </body>\n",
      "    </html>\n"
     ]
    }
   ],
   "source": [
    "print(s['The Fox and the Grapes'])"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Appendix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "36 styles\n",
      "Abstract Expressionism, Art Deco, Art Nouveau, Avant-garde, Baroque, Bauhaus, Classicism, CoBrA, Color Field Painting, Conceptual Art, Constructivism, Cubism, Dada / Dadaism, Digital Art, Expressionism, Fauvism, Futurism, Harlem Renaissance, Impressionism, Installation Art, Land Art, Minimalism, Neo-Impressionism, Neoclassicism, Neon Art, Op Art, Performance Art, Pop Art, Post-Impressionism, Precisionism, Rococo, Street Art, Surrealism, Suprematism, Symbolism, Zero Group\n"
     ]
    }
   ],
   "source": [
    "# Taken from https://magazine.artland.com/art-movements-and-styles/\n",
    "\n",
    "styles = \"\"\"Abstract Expressionism\n",
    "Art Deco\n",
    "Art Nouveau\n",
    "Avant-garde\n",
    "Baroque\n",
    "Bauhaus\n",
    "Classicism\n",
    "CoBrA\n",
    "Color Field Painting\n",
    "Conceptual Art\n",
    "Constructivism\n",
    "Cubism\n",
    "Dada / Dadaism\n",
    "Digital Art\n",
    "Expressionism\n",
    "Fauvism\n",
    "Futurism\n",
    "Harlem Renaissance\n",
    "Impressionism\n",
    "Installation Art\n",
    "Land Art\n",
    "Minimalism\n",
    "Neo-Impressionism\n",
    "Neoclassicism\n",
    "Neon Art\n",
    "Op Art\n",
    "Performance Art\n",
    "Pop Art\n",
    "Post-Impressionism\n",
    "Precisionism\n",
    "Rococo\n",
    "Street Art\n",
    "Surrealism\n",
    "Suprematism\n",
    "Symbolism\n",
    "Zero Group\"\"\".splitlines()\n",
    "\n",
    "print(f\"{len(styles)} styles\")\n",
    "print(*styles, sep=', ')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "from operator import attrgetter, itemgetter, methodcaller\n",
    "from bs4 import BeautifulSoup\n",
    "from dol import Pipe\n",
    "\n",
    "get_soup = Pipe(requests.get, attrgetter('content'), BeautifulSoup)\n",
    "\n",
    "soup = get_soup('https://www.art-prints-on-demand.com/a/artists-painters.html')\n",
    "soup2 = get_soup('https://www.art-prints-on-demand.com/a/artists-painters.html&mpos=999&ALL_ABC=1')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "100"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# extract artists\n",
    "get_title = Pipe(methodcaller('find', 'a'), itemgetter('title'))\n",
    "t1 = list(map(get_title, soup.find_all('div', {'class': 'kk_category_pic'})))  # 30 top\n",
    "t2 = list(map(get_title, soup2.find_all('div', {'class': 'kk_category_pic'})))  # 100 top\n",
    "# merge both lists leaving the top 30 at the top, to favor them\n",
    "t = t1 + t2\n",
    "top_artists = [x for i, x in enumerate(t) if x not in (t)[:i]]\n",
    "\n",
    "len(top_artists)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Claude Monet',\n",
       " 'Gustav Klimt',\n",
       " 'Vincent van Gogh',\n",
       " 'Paul Klee',\n",
       " 'Wassily Kandinsky',\n",
       " 'Franz Marc',\n",
       " 'Caspar David Friedrich',\n",
       " 'August Macke',\n",
       " 'Egon Schiele',\n",
       " 'Pierre-Auguste Renoir',\n",
       " 'William Turner',\n",
       " 'Leonardo da Vinci',\n",
       " 'Johannes Vermeer',\n",
       " 'Albrecht Dürer',\n",
       " 'Carl Spitzweg',\n",
       " 'Alphonse Mucha',\n",
       " 'Catrin Welz-Stein',\n",
       " 'Max Liebermann',\n",
       " 'Paul Cézanne',\n",
       " 'Rembrandt van Rijn',\n",
       " 'Paul Gauguin',\n",
       " '(Raphael) Raffaello Sanzio',\n",
       " 'Amadeo Modigliani',\n",
       " 'Sandro Botticelli',\n",
       " 'Edvard Munch',\n",
       " 'Pierre Joseph Redouté',\n",
       " 'Michelangelo Caravaggio',\n",
       " 'Ernst Ludwig Kirchner',\n",
       " 'Piet Mondrian',\n",
       " 'Pablo Picasso',\n",
       " 'Katsushika Hokusai',\n",
       " 'Hieronymus Bosch',\n",
       " 'Timothy  Easton',\n",
       " 'Paula Modersohn-Becker',\n",
       " 'Edgar Degas',\n",
       " 'Michelangelo (Buonarroti)',\n",
       " 'Salvador Dali',\n",
       " 'Gustave Caillebotte',\n",
       " 'Pieter Brueghel the Elder',\n",
       " 'Ferdinand Hodler',\n",
       " 'Joan Miró',\n",
       " 'John William Waterhouse',\n",
       " 'Peter Severin Kroyer',\n",
       " 'Peter Paul Rubens',\n",
       " 'Peter  Graham',\n",
       " 'Henri de Toulouse-Lautrec',\n",
       " 'Camille Pissarro',\n",
       " 'Edouard Manet',\n",
       " 'Joaquin Sorolla',\n",
       " 'Sara Catena',\n",
       " 'Henri Julien-Félix Rousseau',\n",
       " 'Gustave Courbet',\n",
       " 'Jack Vettriano',\n",
       " 'Felix Vallotton',\n",
       " 'All catalogs',\n",
       " 'Arnold Böcklin',\n",
       " 'Alexej von Jawlensky',\n",
       " 'Kazimir Severinovich Malewitsch',\n",
       " 'Odilon Redon',\n",
       " 'Jean-Étienne Liotard',\n",
       " 'Giovanni Segantini',\n",
       " 'Azure',\n",
       " 'Oskar Schlemmer',\n",
       " 'Carl Larsson',\n",
       " 'Francisco José de Goya',\n",
       " 'Artist Artist',\n",
       " 'François Boucher',\n",
       " 'Mark Rothko',\n",
       " 'Susett Heise',\n",
       " 'Alfred Sisley',\n",
       " 'Giovanni Antonio Canal (Canaletto)',\n",
       " 'Jean-François Millet',\n",
       " 'Giuseppe Arcimboldo',\n",
       " 'Iwan Konstantinowitsch Aiwasowski',\n",
       " 'Catherine  Abel',\n",
       " 'Edward Hopper',\n",
       " 'Mark  Adlington',\n",
       " 'Jean Honoré Fragonard',\n",
       " 'Lucy Willis',\n",
       " 'Jacques Louis David',\n",
       " 'Pavel van Golod',\n",
       " 'M.c. Escher',\n",
       " 'Pierre Bonnard',\n",
       " 'Ferdinand Victor Eugène Delacroix',\n",
       " 'Carel Fabritius',\n",
       " 'Franz von Stuck',\n",
       " 'John Constable',\n",
       " 'László Moholy-Nagy',\n",
       " 'Lincoln  Seligman',\n",
       " 'William Adolphe Bouguereau',\n",
       " 'Adolph Friedrich Erdmann von Menzel',\n",
       " 'Petra Schüßler',\n",
       " 'Pompei, wall painting',\n",
       " 'Unbekannter Künstler',\n",
       " 'Ando oder Utagawa Hiroshige',\n",
       " 'Marc Chagall',\n",
       " 'Zita Rauschgold',\n",
       " 'William  Ireland',\n",
       " 'Bernardo Bellotto',\n",
       " 'Hermann Angeli']"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "top_artists"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": false,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {
    "height": "2515.56px",
    "left": "223px",
    "top": "111.45px",
    "width": "173px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "vscode": {
   "interpreter": {
    "hash": "5a05cf141187f787fb6e83e7d454331f1c13ec1ebc2819d212853f70d1e7de77"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
