Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

OneNote to Markdown Using Python and the Microsoft Graph API

0.00/5 (No votes)
2 Dec 2021 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.

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 app_config.py, 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:

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

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

Python
@app.route("/onenote-demo")
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
        'https://graph.microsoft.com/v1.0/me/onenote/pages',
        headers={'Authorization': 'Bearer ' + token['access_token']},
    ).json()        
    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:

HTML
{% 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>
          <div class="col-4">
            <a class="btn btn-primary btn-sm" href="/onenotepage?page_url={{ x.contentUrl }}" role="button">View as
              HTML</a>
            <a class="btn btn-primary btn-sm" href="/onenotepagemd?page_url={{ x.contentUrl }}"
              download="page_download.md" role="button">Download as Markdown</a>
            </div>
          </div>
      </li>
{% endfor %}
</ul>
{% 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 app.py, add the following code:

HTML
@app.route("/onenotepage")
def fetch_onenote_page():
    token = get_token(app_config.SCOPE)
    page = requests.get(  # Use token to call downstream service
        request.args['page_url'],
        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 app.py, add the following line to your list of already imported libraries:

from markdownify import markdownify as md

Also, add this code:

@app.route("/onenotepagemd")
def fetch_onenote_page_md():
    token = get_token(app_config.SCOPE)
    page = requests.get(  # Use token to call downstream service
        request.args['page_url'],
        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:

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

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

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

Previewing this page as rendered Markdown presents something like this:

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here