Skip to content

will add a preprocessor here (ready to merge) #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ If successful, a message will be printed with the URL to your post. Additionall

Currently, this package only works for the "classic" Jupyter Notebook and is not available in Jupyter Lab. If you have experience making Jupyter Lab extensions, please let me know.

## Removing cells
The code cells are removed by default. A cell tag ```show``` can supress this behaviour for a specific cell where the code should be included in the medium post.

### Removing pieces of cells using cell tags
You can remove pieces of cells using [cell tags](https://nbconvert.readthedocs.io/en/latest/removing_cells.html#removing-pieces-of-cells-using-cell-tags). You can use the tags: ```remove_cell```, ```remove_output```, ```remove_input```.


## Troubleshooting

If your post is unsuccessful, a message with the error will be printed to the screen with information that might help you solve the issue.
Expand Down
11 changes: 11 additions & 0 deletions jupyter_to_medium/_preprocesors.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,14 @@ def preprocess_cell(self, cell, resources, index):
# TODO: Necessary when images from IPython.display module used
pass
return cell, resources

# Remove code cells if a cell tag 'show' has not been defined.
class RemoveCodeCellPreprocessor(Preprocessor):

def preprocess_cell(self, cell, resources, cell_index):
if cell['cell_type'] == 'code':
tags=cell['metadata'].get('tags',[])
if not 'show' in tags:
cell['source'] = '' # remove the code it cell tag is not == 'show'

return cell, resources
34 changes: 29 additions & 5 deletions jupyter_to_medium/_publish_to_medium.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import requests
import nbformat
from nbconvert.exporters import MarkdownExporter
from nbconvert.preprocessors import TagRemovePreprocessor

from ._preprocesors import MarkdownPreprocessor, NoExecuteDataFramePreprocessor
from ._preprocesors import MarkdownPreprocessor, NoExecuteDataFramePreprocessor, RemoveCodeCellPreprocessor
from ._screenshot import Screenshot
from traitlets.config import Config


class Publish:
Expand All @@ -20,9 +22,10 @@ class Publish:
IMAGE_TYPES = {'png', 'gif', 'jpeg', 'jpg', 'tiff'}


def __init__(self, filename, integration_token, pub_name, title, tags,
publish_status, notify_followers, license, canonical_url,
chrome_path, save_markdown, table_conversion):
def __init__(self, filename:str, integration_token=None, pub_name=None, title=None,
tags=None, publish_status='draft', notify_followers=False,
license='all-rights-reserved', canonical_url=None, chrome_path=None,
save_markdown=False, table_conversion='chrome'):
self.filename = Path(filename)
self.img_data_json = self.filename.stem + '_image_data.json'
self.integration_token = self.get_integration_token(integration_token)
Expand Down Expand Up @@ -114,17 +117,38 @@ def get_pub_id(self):
f'Here is the publication data returned from Medium\n\n{data}')

def create_markdown(self):
"""[summary]

Returns:
md : str
markdown
image_data_dict : dict
"""

# Adding a preprocessor here...

mp = MarkdownPreprocessor()
mp.preprocess(self.nb, self.resources)

no_ex_pp = NoExecuteDataFramePreprocessor()
no_ex_pp.preprocess(self.nb, self.resources)

remove_code_cells_preprocess = RemoveCodeCellPreprocessor()
remove_code_cells_preprocess.preprocess(self.nb, self.resources)

# MarkdownExporter converts images to base64 bytes automatically
# MarkdownExporter deep copies resources and fails when matplotlib
# must remove converter key to not error
self.resources.pop('converter')
me = MarkdownExporter()

# Configure our tag removal
c = Config()
c.TagRemovePreprocessor.remove_cell_tags.add("remove_cell")
c.TagRemovePreprocessor.remove_all_outputs_tags.add('remove_output')
c.TagRemovePreprocessor.remove_input_tags.add('remove_input',)

me = MarkdownExporter(config=c)

md, self.resources = me.from_notebook_node(self.nb, self.resources)

image_data_dict = {**self.resources['image_data_dict'], **self.resources['outputs']}
Expand Down
2 changes: 1 addition & 1 deletion tests/notebooks/Test Medium Blog Post.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -3613,7 +3613,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
"version": "3.6.5"
}
},
"nbformat": 4,
Expand Down
151 changes: 151 additions & 0 deletions tests/notebooks/latex_to_medium.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# LaTeX to medium\n",
"As being a dedicated user of jupyter notebooks in my research I was very pleased to hear about the \n",
"[jupyter_to_medium](https://github.com/dexplo/jupyter_to_medium) package, also introduced in this [post](https://medium.com/dunder-data/jupyter-to-medium-initial-post-ecd140d339f0).\n",
"I tried this package and was very impressed by how easy my work in jupyter could be converted into a medium post. One pretty essential part missing however, was the LaTeX equations from my notebooks...\n",
"\n",
"This was a known issue, but someone proposed that matplotlib could be used to render the equations into images stored in the notebooks, which would be converted to medium. Here is my attempt to do so..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I wrote a simple method that uses matplotlib to render the equations:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"tags": [
"show"
]
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import sympy as sp\n",
"\n",
"def display(eq:sp.Eq, size=20):\n",
" \"\"\"\n",
" Use matplotlib to render LaTeX equations to figure within notebook.\n",
" \n",
" ----------\n",
" eq : str or sympy.Eq\n",
" LaTeX equation to render\n",
" \n",
" size : int\n",
" size of the equation\n",
" \"\"\"\n",
" \n",
" if isinstance(eq, str):\n",
" eq_latex = eq\n",
" else:\n",
" eq_latex = sp.latex(eq)\n",
"\n",
" fig,ax=plt.subplots()\n",
" fig.set_size_inches(0.01, 0.01)\n",
" ax.axis(\"off\")\n",
" plt.title(r'$' + eq_latex + r'$' , size=size);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As I'm also a big fan of the SymPy package, this method accepts equations both as pure LaTeX string or a SymPy expression. So it is either possible to write the LaTeX:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"tags": [
"show"
]
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAG4AAAAmCAYAAAAlUK76AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAEC0lEQVR4nO2ZW4iVVRTHf/+p1MopS4qCsTERQRHNGekhCrxAFyIYfAi6v/QcQk8G2XmpwIlsHgqihyYioQdpyC5oUj0IBpadphtJ2dWsvJTjFMk4rR72PnCavsv+xnPmzIb9h813OP+19177+++11158MjMS4kNXpx1ImB6ScJEiCRcpknCRIgkXKZJwkSIJ10FI2iLpgKQxScck7ZK0MqRvEq6zWAc8B9wAbADOAnslXV7WUakAnz2QNB84BQyY2a4i20oRJ2mPJCtpj56L87MNkmqShmdoum6cJr+XGZ5fceB+XDg/XmCzs+KYsw6Ses3s+xxuEXDEzP5pw9RDQB3YX2ppZkENWAIYcDC0T4wNuBY4DTwDXArUgGFgHrAVGANubMO8TwM/A0tC7KsclWv980CFPm2FpPX+eH5KUp+kEUknJZ2S9Jqkq7zdCkk7JP3muTckXZM1ppl9CyzHHVtfABuBVcBnwHXA9Wa2L8C3dZJelfSTpDOSjkraLWkgw3Y7cBewwcwOBy2+wo7Yhou4BzsdFU0+Pex9eh34C3dMDwKf+v/fAu4Axr3NIG7jGfB+wPibcKnBgIcCfRIuWg04hovWJ4EdwHFg2xT7IeAXYHmVtQffKiW9C6wHXgR+yDEbNLM/c/pvBhYETeZQN7OREp9eAe4GfgVuNrNR/3+397Eb9/I2mdl+z80BvgZ6gIvM7O+McXtwR+RtwGHgYtyxWQceMbOvCnx6AtiC20QPNL8Pf2tcaD5/SnoWuA8YwEV3A+NmNl609tCdLeAP3C7KaydKxviupP/UNhzg15fe9tYM7mPP3ZvBveO5hRlcI8cN8f8c9xgFOQ7oAyZxUT0nwP+8tddK+wYKt8wPuK/Vx910Gy4KJoFvcviTwAngggzuEDBWMHZv0+9a8yYCFgFdOf1e9u9pY7vXH3o5aVxM6oH2M4HVuJpn71RC0mLgMlwem5jCzQeWUrAWyykFPPej5ZcCt+BqsPdKfD9nhNZxDeE+me5Ebchxff75UQbXX8CtwR39B0OcMLNaiJ2kecAVOL/bUeP9BzMmHLAZ6K1g/xIwUsA3hPswg+sv4Nb4Z5BwFSD/vLLF42YjIJd04ZL1WeDCTue2Jr/qwBkyLgHAbvIvH8OeW9kGnxplyJ0Z3DLgvFbNVVoOSFoBfI67TW0vMH3BzI5U3jnTgKS5uM00amZrM/jjuCv14gxuFJfjus1sssV+3Y6rFwW8jXtvC3BR3mNmV7dssoBddD/lV/dJ4JIZjLZ+P+/zGVyv53ZmcHOBCeCDNvp2E/Am7lY7ARwF9gD3tHKe9FknUqQPqZEiCRcpknCRIgkXKZJwkSIJFymScJEiCRcpknCRIgkXKZJwkSIJFymScJHiX90O/2mX01elAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 0.72x0.72 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"eq = r'E = m*c^2'\n",
"display(eq)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"tags": [
"show"
]
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAF0AAAAmCAYAAACmlJfBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAADe0lEQVR4nO2Z24sURxTGf5+Jl5B18UKCAVEJ6oMIshdEREUNMYII+xQQb3+CvuZBEMGnBI0PCiKKJhBQkCyaENQFfRAironrihcWFRFF0KzGjYGIWY8PVYPD2jNdszvbpUx9UDQ93+lzvv66OFXdIzMjoViMiS2gEZFMj4BkegQk0yMgmR4ByfQIaBjTJX0jqVvSgKTHkk5Kmh9DS8OYDiwH9gGLgZXA/0CXpClFC1GjvhxJagKeAR1mdrLI2jXNdEmnJVnO2DZaYuuMibj7f1p04ZpmuqR+oBnYWSXsmJldH6mw0YakY8AcoN3MBous/WFooKTPgSnAZTPbPmqKCoCkXcASYEnRhkNt7aXdH7tHQ8hIIGm5pKOS7kt6IemhpFOSOjJidwPrgJVmdieDX+Hb5HeSWiV1Snoi6ZmknyVN83HzJP0k6ZHnfpE0I0Tve226HL4HzuJ2JF3ALn/ehtuplMfv4Y3hNyukbfXHucB5YBA4CNwDOoBDktYCF4Em4AjQB6wBfgjRHdxeeGP6YknTK8R8a2b/ZhGStgKTaqjXY2adOTE7gS3AcWBzeW2/O5ladr4X2Igz7mlpxgLPzex5Wc6S6QuBRWbW66/fgTN+FdACfGlmv3tuHHALWCZpgpn9V1W1meUOQMDfgFUZ/Tk57uZcP3QczsnXipuF3cC4gHuoVGf7kLgb/vfVGTkue25DBnfGc1NztQSaPtcnPB8SX8QAfvSavqhjzo/9g7xdgX8C9ANjM7g+YCCkTmhPL7WWnsD4IvAVbo99to45F+DWua6hhKRZwGTgnJm9HMI1AbMJ9Ce0p5dMvxIY/xbq2dMlTQA+8TGvhqspA6V+/kcG11aFa8G14D9DihRmOrAVmFlD/BGgswInf/x0BHqyUDL9UgbXVoVr8ccg00P63BjgH9wHoo9i9/IyXVdxPf3rCmvQB8PI2QO8IGNhBk5RYaEEDntufkid3M8AkuYB14ABYHeV0ANm9iDoSdcBktYAJ3Cz/jecxkm4WTfdzD6rMd943OTqNbP2DP4v3PZyVgbXi+vpEy3kDTfg6W8if3s3CDRHmO1LgV9xu4qXwEPgNLB+GLna/L3sz+Bmeu54Bjfe174QWqthP+3GRCP9ifHOIJkeAcn0CEimR0AyPQKS6RGQTI+AZHoEJNMjIJkeAcn0CEimR0AyPQJeA9tcMksNMo7bAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 0.72x0.72 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"E,m,c = sp.symbols('E m c')\n",
"eq2 = sp.Eq(E,m*c**2)\n",
"display(eq2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conclusions\n",
"Eventhough this requires that you rewrite your notebooks using the display method, I think that this is still much more convenient than having to create these LaTeX equations as images in some other (more manual way). I will now definatelly used the [jupyter_to_medium](https://github.com/dexplo/jupyter_to_medium) package for my next post. "
]
}
],
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
"display_name": "Python 3",
"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.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
14 changes: 14 additions & 0 deletions tests/test_create_markdown.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import pytest
import os.path
from jupyter_to_medium._publish_to_medium import Publish

@pytest.fixture
def file_name():
path = os.path.dirname(__file__)
yield os.path.join(path,'notebooks','latex_to_medium.ipynb')


def test_create_markdown(file_name):
publish = Publish(filename=file_name)
md, image_data_dict = publish.create_markdown()
a = 1