Click here to Skip to main content
15,907,395 members
Articles / Web Development / HTML

OneNote to Markdown Using Python and the Microsoft Graph API

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
2 Dec 2021CPOL4 min read 10.4K   7   1
The Python web app this article creates will be broadly useful to enterprise developers, who can use it as a starting point and swap out the Markdown conversion code for code that converts to whatever output format the business requires.

This article is a sponsored article. Articles such as these are intended to provide you with information on products and services that we consider useful and of value to developers

In this article, we will construct a Flask web app that lets users view a list of their OneNote pages, select a page, view as HTML, and download the page as Markdown.

OneNote provides a great note-taking and content creation experience. Users can export pages to Word documents and PDFs, which is certainly useful. However, many enterprises require employees to submit content for publishing in other formats, such as Markdown. Rather than downloading a Word document and manually converting it, we will have our app do this for us.

In our first article, we created a Python web app with Flask, which made a simple Graph API call. We will build on this app throughout this code-along article and make more advanced Graph calls to access some of our files.

To participate, you will need:

  • The project prerequisites, listed in our first article, along with the code we created
  • Some OneNote files of your own to experiment with

You can examine the complete code for this project on Github.

Adapt the Config

To begin, we need to adapt our config to permit our app to access our OneNote Files.

In, change the value of SCOPE to match the following:

SCOPE = ["User.Read.All", "Notes.ReadWrite.All"]

Now, when the user authenticates, the Microsoft identity platform will ask them for consent to grant these permissions, on their behalf, to the app in Graph.

We are making some more advanced Graph API calls, so to ease the testing of all these calls and permissions, let’s head over to Graph Explorer - Microsoft Graph. Graph Explorer will confirm the expected return for our app and provide a great experience for experimentation. Because it defaults to a sample authenticated user, sign in to work with your own account.

Create the Entry Point

Now, let’s give our app access to our OneNote files. To start, we provide a link on our home page for our users to access their OneNote pages.

Add the following code to templates/index.html:

<a class="btn btn-primary btn-lg" href="/onenote-demo" role="button">OneNote Demo</a>

Next, open, and add the following function to handle calls for the /onenote-demo route, which fetches all the user’s pages.

def onenote_demo():
    if not session.get("user"):
        return redirect(url_for("login"))
    token = get_token(app_config.SCOPE)
    one_notes = requests.get(  # Use token to call downstream service
        headers={'Authorization': 'Bearer ' + token['access_token']},
    return render_template('onenote-demo/index.html', result=one_notes)

Before we make our Graph API call, we need to get our token from the cache we built in the first article. We use the session to store the token, though we could configure it to use a Redis cache or a database if we needed to. This token is sent with the Graph call.

We need HTML to render once this onenote_demo function has run. In templates/onenote-demo, create index.html with the following code:

{% extends "base.html" %}
{% block mainheader %}OneNote Pages{% endblock %}
{% block content %}
  <a class="btn btn-primary btn-md" href="/" role="button">Back Home</a>
  <a class="btn btn-danger btn-md" href="/logout" role="button">Logout</a>
    <ul class="list-group list-group-flush">
      {% for x in result.value %}
      <li class="list-group-item">
        <div class="row justify-content-between">
          <div class="col-4">
            {{ x.title }}
          <div class="col-4">
            <a class="btn btn-primary btn-sm" href="/onenotepage?page_url={{ x.contentUrl }}" role="button">View as
            <a class="btn btn-primary btn-sm" href="/onenotepagemd?page_url={{ x.contentUrl }}"
              download="" role="button">Download as Markdown</a>
{% endfor %}
{% endblock %}

This HTML displays a list of all OneNote pages, returned from the Graph call. It also provides buttons to view them as HTML or download as Markdown.

For user convenience, we have also provided buttons to return back to the homepage and to log out.

Let’s get our function ready for this new page. Back in, add the following code:

def fetch_onenote_page():
    token = get_token(app_config.SCOPE)
    page = requests.get(  # Use token to call downstream service
        headers={'Authorization': 'Bearer ' + token['access_token']},
    return page.text # returns HTML

As before, we reuse our get_token function to fetch the token from the cache and redirect to the login page if no token is present. This renders our OneNote page as HTML.

Converting to Markdown

We can make use of Python’s rich ecosystem of libraries. Among these is Markdownify, which will convert our OneNote into markdown for us. We could easily swap this out if we required other formats.

We start by installing this library in our environment:

pip install markdownify

At the top of, add the following line to your list of already imported libraries:

from markdownify import markdownify as md

Also, add this code:

def fetch_onenote_page_md():
    token = get_token(app_config.SCOPE)
    page = requests.get(  # Use token to call downstream service
        headers={'Authorization': 'Bearer ' + token['access_token']},
    markdown = md(page.text, heading_style='ATX')
    return markdown

Here we get our page, convert it to Markdown, and return the converted page.

App in Action

Upon logging in, we see a simple menu:

Image 1

When we select OneNote Demo, the app presents options to view our pages as HTML or download them as Markdown:

Image 2

Selecting the View as HTML button will display our selected OneNote page:

Image 3

Selecting the Download as Markdown option and opening the downloaded file in VS Code should output something like this:

Image 4

Previewing this page as rendered Markdown presents something like this:

Image 5

What an achievement! This is so much easier than manually trying to convert our files to Markdown, and we have created this powerful app in a few minutes with very little code.

You may want to experiment and switch out the Markdown code for another format, or perhaps change the Graph call to view sections, or create a new page. There is a lot of potential for scope extension with this project. The basis of the authentication we created in article one — a secure and flexible foundation — is a great springboard to make applications like this.

See the OneNote REST API documentation for notes on additional use cases.

In the final article of this series, we will again be building on the authentication we set up in the first article. We will be creating an app that can create a new Teams Channel and invite stakeholders to provide updates to assist with incident management.

This article is part of the series 'Building on the Microsoft Graph with Python View All


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA2-Dec-21 18:49
professionalȘtefan-Mihai MOGA2-Dec-21 18:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Building on the Microsoft Graph with Python