<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Michael Berry</title>
    <description>The latest articles on Forem by Michael Berry (@kenticomichaelb).</description>
    <link>https://forem.com/kenticomichaelb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F276841%2F055cd644-9b6a-4c4e-91a7-559eb3158424.jpeg</url>
      <title>Forem: Michael Berry</title>
      <link>https://forem.com/kenticomichaelb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kenticomichaelb"/>
    <language>en</language>
    <item>
      <title>Python and Headless: How to make interactive, adaptable, affordable reports</title>
      <dc:creator>Michael Berry</dc:creator>
      <pubDate>Wed, 16 Jun 2021 19:31:01 +0000</pubDate>
      <link>https://forem.com/kontent_ai/python-and-headless-how-to-make-interactive-adaptable-affordable-reports-1gb3</link>
      <guid>https://forem.com/kontent_ai/python-and-headless-how-to-make-interactive-adaptable-affordable-reports-1gb3</guid>
      <description>&lt;p&gt;In this article, I'll show you how to pull data from a headless CMS and create a content type report using the Python framework: &lt;a href="https://dash.plotly.com/introduction" rel="noopener noreferrer"&gt;Dash&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeing is believing
&lt;/h2&gt;

&lt;p&gt;I am a visual learner. I really appreciate when a graph or diagram accompanies data, or when a visual aid gets to "the point" in a presentation. Not everyone is as much of a fan of charts as I am, but representing data visually is universally helpful. For example, which is easier to understand in a presentation:&lt;/p&gt;

&lt;p&gt;these side-by-side numbers: &lt;em&gt;101 | 443 | 102 | 320 | 82&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;or &lt;/p&gt;

&lt;p&gt;this graph:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22txws4ogu1gyfdmbtok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22txws4ogu1gyfdmbtok.png" alt="Sample visual learning graphic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With just the delimited list of numbers, you don't get a good idea of how the values compare to one another. The graph makes differences and similarities more obvious. &lt;/p&gt;

&lt;p&gt;Let's say that you are a new manager or member of a marketing team that uses a headless CMS to produce content.  Knowing what types of content the team has been producing will be useful for better understanding their current content strategy. However, when you are looking at content models in a headless CMS, you're normally met with a long running list of names and numbers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frafvk11x4rny7ab5h9a0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frafvk11x4rny7ab5h9a0.png" alt="Kontent content type listing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To assess the kinds of content in the project, you're forced to scan the entire content type listing and mentally compare the values. Without a visual aid, it's hard to tell what topics are prevalent in your project. Furthermore, once you've finished your analysis, you can't easily share your findings with other members of your team. Making them believe your analysis is correct is much easier with a visual report.&lt;/p&gt;

&lt;p&gt;But what if you don't have the time or resources to integrate a commercial reporting tool into your project? Luckily, there are open source frameworks and libraries for building web analytic applications, like Dash.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Dash?
&lt;/h2&gt;

&lt;p&gt;Dash is framework built upon the popular &lt;a href="https://plotly.com/python/getting-started/#:~:text=Basic%20Charts%20tutorials.-,Overview,3%2Ddimensional%20use%2Dcases." rel="noopener noreferrer"&gt;Plotly&lt;/a&gt; graphing library, that allows you to build interactive visual applications and render them in a browser. Although it uses JavaScript libraries behind the scenes, Dash apps are written purely in Python and don't require familiarity with JavaScript. &lt;/p&gt;

&lt;p&gt;If you know some Python, Dash is well suited to create reports from headless CMS data because it supports a wide variety of &lt;a href="https://plotly.com/python/basic-charts/" rel="noopener noreferrer"&gt;well-documented&lt;/a&gt; graph types, and the open-source edition is completely free.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to create a report from a headless CMS
&lt;/h2&gt;

&lt;p&gt;Let's create a content report using the &lt;a href="https://kontent.ai/" rel="noopener noreferrer"&gt;Kentico Kontent headless CMS&lt;/a&gt;, the &lt;a href="https://github.com/kentico-michaelb/kontent-delivery-python-sdk" rel="noopener noreferrer"&gt;open source Kontent Python SDK&lt;/a&gt;, and &lt;a href="https://dash.plotly.com/introduction" rel="noopener noreferrer"&gt;Dash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In our sample scenario, we'll create three reports about a coffee shop website, and aggregate them in a tabbed report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk5v8gwkzzggx2f5xwcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk5v8gwkzzggx2f5xwcw.png" alt="Sample tabbed report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our report we want to show: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what type of content exists in the project&lt;/li&gt;
&lt;li&gt;the audiences our articles are targeting&lt;/li&gt;
&lt;li&gt;the timeline showing when they were published. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These reports will give us more insight into the "who, what, when" strategy surrounding our previously published articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application setup
&lt;/h2&gt;

&lt;p&gt;First we need to install Dash, our SDK, and dependencies. I recommend that you do this in a &lt;a href="https://docs.python.org/3/tutorial/venv.html" rel="noopener noreferrer"&gt;virtual environment&lt;/a&gt;. In the terminal run:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;pip install dash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip install kontent_delivery&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip install pandas&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Alternatively, you can download and then install this &lt;a href="https://github.com/kentico-michaelb/kontent-dash-blog/blob/master/requirements.txt" rel="noopener noreferrer"&gt;requirements.txt&lt;/a&gt; using &lt;code&gt;pip install -r /path/to/requirements.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By the end of the exercise, we will have a file structure that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kontent-dash-blog (root)/
    reports/
        article_timeline.py
        content_types.py
        personas.py
    report_dashboard.py
    requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dash and Kontent Initialization
&lt;/h2&gt;

&lt;p&gt;Once we've installed the requirements, we need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setup our Flask application for running Dash &lt;/li&gt;
&lt;li&gt;initialize our Kontent SDK delivery client using our &lt;a href="https://docs.kontent.ai/tutorials/develop-apps/get-content/get-content-items#a-getting-content-items" rel="noopener noreferrer"&gt;Kontent project ID&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;setup some methods to build our three reports &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll do this in a file called &lt;strong&gt;report_dashboard.py&lt;/strong&gt; and create it in the root of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import dash
import flask
from kontent_delivery.client import DeliveryClient
# report methods
from reports.content_types import build_types_chart
from reports.personas import build_personas_pie
from reports.article_timeline import build_post_timeline

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

# dash
server = flask.Flask(__name__)
app = dash.Dash(server=server, external_stylesheets=external_stylesheets)

# kontent
client = DeliveryClient("your_Kontent_Project_ID")

# custom methods to create reports
bar = build_types_chart(client)
pie = build_personas_pie(client)
timeline = build_post_timeline(client)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Report 1: bar chart showing content types
&lt;/h2&gt;

&lt;p&gt;In this chart, we'll call the Kontent API and count how many content types exist of a given type. We'll create the file &lt;strong&gt;content_types.py&lt;/strong&gt; in a &lt;strong&gt;reports&lt;/strong&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import plotly.express as px

def build_types_chart(client):
    type_response = client.get_content_types()
    item_response = client.get_content_items()

    types = {}

    # add Kontent type counts to a dictionary
    for content_type in type_response.types:
        types[content_type.codename] = 0

    # increment content type count per item of respective type
    for item in item_response.items:
        types[item.content_type] += 1 

    data = {"content types":types.keys(), "count": types.values()}

    fig = px.bar(data, x="content types", y="count")

    return fig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;em&gt;build_types_chart&lt;/em&gt; method, we create key value pairs from the codename of our content types, and a count of how many items are published of that type. With that data, we use Plotly Express to create and return a bar graph to our Dash application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxsrqi2b0pv8ks8fpj4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxsrqi2b0pv8ks8fpj4n.png" alt="Content types bar chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Report 2: pie chart showing article personas
&lt;/h2&gt;

&lt;p&gt;In our second chart, we want to show what percentage of personas our article content targets. Let's assume we know that the article content type uses a &lt;a href="https://docs.kontent.ai/tutorials/manage-kontent/content-modeling/create-taxonomies" rel="noopener noreferrer"&gt;taxonomy group&lt;/a&gt; called &lt;em&gt;personas&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6faxv7a8rw0puz3w8dr6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6faxv7a8rw0puz3w8dr6.png" alt="Kontent taxonomy group"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll create the file &lt;strong&gt;personas.py&lt;/strong&gt; in the &lt;strong&gt;reports&lt;/strong&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from kontent_delivery.builders.filter_builder import Filter
import plotly.express as px

def build_personas_pie(client):
    # only return "article" content items
    item_response = client.get_content_items(
        Filter("system.type", "[eq]", "article")
    )

    # get the taxonomy group that articles use
    personas_response = client.get_taxonomy("personas")
    chart_personas = {}

    # identify personas to be counted
    def get_taxonomy_terms(terms):
        for persona in terms:
            chart_personas[persona.name] = 0
            if persona.terms:
                get_taxonomy_terms(persona.terms)


    get_taxonomy_terms(personas_response.terms)

    # increment persona count per item
    for item in item_response.items:
        for persona in item.elements.personas.value:
            chart_personas[persona.name] += 1 

    data = {"personas":chart_personas.keys(), "count": chart_personas.values()}

    fig = px.pie(data, names="personas", values="count",
                title='Assigned article personas')
    return fig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;em&gt;build_personas_pie&lt;/em&gt; method, we pull all of our article content items.  We then create key value pairs from the name of taxonomy terms that exist in our Personas taxonomy group, and a count of how many items are tagged with a particular term. With that data, we use Plotly Express to create and return a pie graph to our Dash application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5or1j7sz6dwu1iaq8laq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5or1j7sz6dwu1iaq8laq.png" alt="Personas pie chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Report 3: timeline showing date posted
&lt;/h2&gt;

&lt;p&gt;Information about when these articles were published could provide more context about what our content strategy was previously, or how it changed over time. Let's assume we have a "posted date" element on our articles that we can use to create our timeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frovnhw9v15tv3s4a5r0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frovnhw9v15tv3s4a5r0l.png" alt="Kontent content item posted date"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll create the file &lt;strong&gt;article_timeline.py&lt;/strong&gt; in the &lt;strong&gt;reports&lt;/strong&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from kontent_delivery.builders.filter_builder import Filter
import pandas as pd
import plotly.express as px

def build_post_timeline(client):
    # only return "article" content items
    item_response = client.get_content_items(
        Filter("system.type", "[eq]", "article")
    )

    # setup two arrays for Plotly scatter
    chart_items = []
    chart_post_date = []

    # populate arrays with respective item name and post date
    for item in item_response.items:
        chart_items.append(item.name)
        chart_post_date.append(item.elements.post_date.value)

    df = pd.DataFrame(dict(item=chart_items, post_date=chart_post_date))

    # use column names of df for the different parameters x, y, ...
    fig = px.scatter(df, x="post_date", y="item",
                    title="Article post timeline",
                    labels={"Post Date":"Date item posted"} # customize axis label
                    )

    return fig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the other methods, we retrieve the article items from Kontent, but in this example we take an extra step to convert two arrays into a dictionary and pass them into a &lt;a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html" rel="noopener noreferrer"&gt;pandas DataFrame&lt;/a&gt;. This is only for the sake of matching the &lt;a href="https://plotly.com/python/dot-plots/" rel="noopener noreferrer"&gt;basic dot plot example&lt;/a&gt; in the Plotly documentation. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlqtepnjpdkglrfzxgtp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlqtepnjpdkglrfzxgtp.png" alt="Dash timeline report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tying it all together in a tabbed report with Dash
&lt;/h2&gt;

&lt;p&gt;It's time to pull all the visual aids together in a single report and add some interactivity with Dash. We created the &lt;strong&gt;report_dashboard.py&lt;/strong&gt; file earlier to add the Kontent client and our chart building methods, and now we'll add a layout with some interactive components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ... other imports from before
# add these for components:
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

# dash
server = flask.Flask(__name__)
app = dash.Dash(server=server, external_stylesheets=external_stylesheets)

# kontent
client = DeliveryClient(config.project_id, options=config.delivery_options)

bar = build_types_chart(client)
pie = build_personas_pie(client)
timeline = build_post_timeline(client)

# -- end of "Dash and Kontent Initialization" code --

# -- start of "Tying it all together..." code --

# adding the dash layout
app.layout = html.Div([
    # dash tabs component that wraps individual tabs
    dcc.Tabs(id='tabs-example', value='tab-1', children=[
        dcc.Tab(label='Content Types', value='tab-1'),
        dcc.Tab(label='Article Personas', value='tab-2'),
        dcc.Tab(label='Article Post Timeline', value='tab-3'),
    ]),
    # div used as a canvas to show reports
    html.Div(id='tabs-example-content')
])

# onclick events that call our chart building methods
@app.callback(Output('tabs-example-content', 'children'),
              Input('tabs-example', 'value'))
def render_content(tab):
    if tab == 'tab-1':
        return html.Div([
            dcc.Graph(figure=bar)
        ])
    elif tab == 'tab-2':
        return html.Div([
             dcc.Graph(figure=pie)
        ])
    elif tab == 'tab-3':
        return html.Div([
             dcc.Graph(figure=timeline)
        ])

# run the application when report_dashboard.py is called
if __name__ == '__main__':
    app.run_server(debug=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the final code file of our report, we use &lt;a href="https://dash.plotly.com/dash-html-components" rel="noopener noreferrer"&gt;Dash's core HTML components&lt;/a&gt; to create a simple two div layout. The first div contains Dash "Tab" components that we use to toggle between graphs, and the second div acts as a canvas to show the graphs we generated.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsg43f8p5r30ain4cpse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsg43f8p5r30ain4cpse.png" alt="Dash report with tabs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following the layout, we use &lt;a href="https://dash.plotly.com/basic-callbacks" rel="noopener noreferrer"&gt;Dash callbacks&lt;/a&gt; to generate a different graph depending upon which tab we click. More information about Dash's core components, including "Graph" and "Tabs" can be seen in the &lt;a href="https://dash.plotly.com/dash-core-components" rel="noopener noreferrer"&gt;Dash documentation here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point, you can run &lt;code&gt;report_dashboard.py&lt;/code&gt; in your terminal and watch Dash spin up a localhost server for your newly created reporting application.  Follow the link in the terminal, and VOILA! You've created your own interactive, custom report for free.  &lt;/p&gt;

&lt;p&gt;You can check out a &lt;a href="https://kontent-dash-report.herokuapp.com" rel="noopener noreferrer"&gt;live hosted example&lt;/a&gt; or the &lt;a href="https://github.com/kentico-michaelb/kontent-dash-blog" rel="noopener noreferrer"&gt;full source code on GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article we discussed what Dash is, and how you can use it to create interactive reports from your headless CMS data. Now you know with a little Python knowledge, and an open-source framework like Dash, you don't have to miss out on having headless CMS reports.&lt;/p&gt;

&lt;p&gt;Want more Dash and headless samples? Check out this &lt;a href="https://github.com/kentico-michaelb/kontent-linked-report" rel="noopener noreferrer"&gt;Dash + Kontent app&lt;/a&gt; for making modular content graphs.&lt;/p&gt;

&lt;p&gt;Are you having issues with the samples from the article? &lt;a href="//bit.ly/kontent-discord"&gt;Get in touch on our Discord&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>headlesscms</category>
      <category>reports</category>
      <category>free</category>
    </item>
    <item>
      <title>Can you evolve your documentation from fossil to fantastic?</title>
      <dc:creator>Michael Berry</dc:creator>
      <pubDate>Tue, 22 Dec 2020 13:17:57 +0000</pubDate>
      <link>https://forem.com/kontent_ai/can-you-evolve-your-documentation-from-fossil-to-fantastic-2fa5</link>
      <guid>https://forem.com/kontent_ai/can-you-evolve-your-documentation-from-fossil-to-fantastic-2fa5</guid>
      <description>&lt;p&gt;&lt;strong&gt;We can all agree that repeating the same information over and over wastes time and that writing comprehensive and accessible documentation is essential. But have you thought about &lt;em&gt;how&lt;/em&gt; you’re creating that documentation? Are you still dusting off printed instructions or digging up ancient word documents? Is there a more modern approach?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my opinion: "Boilerplates" are beautiful. They make using a new framework, language, or platform possible without starting from scratch, and can vastly simplify starting a new project.  To simplify my documentation creation process, I've made a &lt;em&gt;Kontent + Docusaurus&lt;/em&gt; boilerplate that will handle styling, navigation, and structure, while also allowing me to maintain my content using a headless CMS.&lt;/p&gt;

&lt;p&gt;In this article, I’ll talk about the benefits of using Kentico Kontent, Facebook’s Docusaurus, and my Kontent + Docusaurus boilerplate to create scalable, attractive, easy-to-manage documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why my documentation went “the way of the dinosaurs”
&lt;/h2&gt;

&lt;p&gt;I wear a lot of hats in my organization: I’m a manager, support engineer, content creator, internal trainer, to name a few. Shifting attention between varying tasks and functions is a common occurrence and can be pretty overwhelming. To save time (and my sanity), documenting my processes and responsibilities helps me stay organized and allows me to delegate tasks to my team more effectively. But until recently, I didn’t have a great way to create this resource. I’ve used our crowded company wiki, Word documents, PowerPoint presentations, and a lot of information is given verbally from memory. Something had to change as this approach locked me into a perpetual loop of updating static presentations and documents with no hope of delegation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it help you?
&lt;/h2&gt;

&lt;p&gt;Maybe you’re not managing a team or on-boarding anyone, but I’m confident at some point you’ve had to walk a teammate or multiple colleagues through a process a few times. &lt;strong&gt;Let’s be fair&lt;/strong&gt;: your colleagues don’t want to take valuable time away from you. They just need answers, and those answers happen to live in your head, are hiding in your notes, or are just not documented in an accessible location.&lt;/p&gt;

&lt;p&gt;Furthermore, updating and maintaining documentation or guides doesn’t &lt;em&gt;have&lt;/em&gt; to (and normally shouldn’t) rely upon developers, specialized, or senior team members. You should be able to write the original copy, hand-off the maintenance to a colleague or team, and move on to other tasks that are burning for your attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  The boilerplate: Our cloning DNA
&lt;/h3&gt;

&lt;p&gt;Creating a new piece of documentation shouldn’t be an ordeal; you don’t need another project vying for your time. Using a template as a starting point is a good way to make sure starting the documentation isn’t a major line item blocking out your calendar. So clone this repository and save time—no prehistoric mosquitoes required (you knew a &lt;em&gt;Jurassic Park&lt;/em&gt; reference was coming).&lt;/p&gt;

&lt;p&gt;This boilerplate comes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A navigation sidebar with collapsible sub-sections&lt;/li&gt;
&lt;li&gt;  Attractive pre-made styles, including light &amp;amp; dark mode&lt;/li&gt;
&lt;li&gt;  A minimal amount of content to act as a “starting point”&lt;/li&gt;
&lt;li&gt;  Quick and easy setup instructions in the &lt;a href="https://5fb2a7f3aec91f000740cba3--sad-clarke-a5925e.netlify.app/" rel="noopener noreferrer"&gt;Boilerplate’s documentation&lt;/a&gt; or &lt;a href="https://github.com/kentico-michaelb/kontent-docusaurus-boilerplate#readme" rel="noopener noreferrer"&gt;GitHub README&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F6cd0f682-c90c-45e9-9353-eb5d3028641a%2FdocusaurusBoilerplateExample.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F6cd0f682-c90c-45e9-9353-eb5d3028641a%2FdocusaurusBoilerplateExample.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Kontent + Docusaurus boilerplate pictured above.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The headless CMS: Advancing our reptile brains
&lt;/h3&gt;

&lt;p&gt;When documentation is required, jumping into a simple word processor is second-nature to a lot of us. It’s comfortable, it’s quick, it’s familiar. This doesn’t mean it is the best approach for the job, though, and I argue that breaking this habit is key for making scalable and accessible documentation. Using a headless CMS addresses scalability head-on and future-proofs your documentation. &lt;/p&gt;

&lt;p&gt;I work in Kentico Kontent all the time, so it is a go-to comfort pick for me when choosing a CMS, but those who aren’t fluent in Kontent will still reap major benefits from using it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  It’s a Software-as-a-Service platform, meaning no managing upgrades, hosting, or security patches.&lt;/li&gt;
&lt;li&gt;  Kontent’s search bar, filters, and “Home” allows you to quickly locate content.&lt;/li&gt;
&lt;li&gt;  Inserting code snippets into the Rich Text Editor using a WYSIWYG editor is easy.&lt;/li&gt;
&lt;li&gt;  Reusable content and content links mean no more copy-pasting swaths of documentation between sections.&lt;/li&gt;
&lt;li&gt;  Its features, API, and SDKs are &lt;a href="https://docs.kontent.ai/" rel="noopener noreferrer"&gt;well documented&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F4a97bade-9433-42d9-a2c0-a7ae305af58e%2FdocusaurusCalloutExample.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F4a97bade-9433-42d9-a2c0-a7ae305af58e%2FdocusaurusCalloutExample.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Sample content item in Kentico Kontent pictured above.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These are just a few of the benefits when using Kentico Kontent, but if you need more proof, the official Kontent documentation uses Kontent, and there is a &lt;a href="https://kontent.ai/blog/content-as-a-service-for-documentation" rel="noopener noreferrer"&gt;two-part article series&lt;/a&gt; that talks about why.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docusaurus: More Barney than an apex predator
&lt;/h3&gt;

&lt;p&gt;In addition to the &lt;a href="https://v2.docusaurus.io/docs/#features" rel="noopener noreferrer"&gt;list of benefits from Docusaurus’ website&lt;/a&gt;, the plugins and presets provided by the Docusaurus team make getting up and running fast. Its creators’ focus on documentation sites, along with 20,000 GitHub stars and being backed by Facebook, is what really drew me to using Docusaurus as the front-end application for my documentation. It’s also a nice fit because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  It’s a static site generator making it fast and secure.&lt;/li&gt;
&lt;li&gt;  It can be deployed and hosted virtually anywhere for free.&lt;/li&gt;
&lt;li&gt;  The Docusaurus team wrote easy-to-read documentation about everything from &lt;em&gt;Installation *to *Lifecycle APIs.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This React and MDX markdown powered project has T-Rex-level power in its extensibility and out-of-the-box functionality, but the features and configuration are intuitive, so its learning curve isn’t intimidating. For example, in just three terminal commands you can have their &lt;em&gt;classic&lt;/em&gt; preset site running. Docusaurus is definitely a friendly dinosaur.&lt;/p&gt;

&lt;h2&gt;
  
  
  So...is it &lt;em&gt;actually&lt;/em&gt; fantastic?
&lt;/h2&gt;

&lt;p&gt;For me personally, I’m a lot happier to point a colleague to up-to-date stylized on-boarding resources than having to muddle together PowerPoint presentations, but this depends on your use case. If you just need a few quick, static paragraphs to document something, Kontent + Docusaurus might be overkill. But, if you have living documentation that needs frequent updates, reusable content, and structured navigation, it could be a better choice than passing around Word documents or jamming information into overflowing wikis. &lt;/p&gt;

&lt;p&gt;Additionally, since the content is available over an API in a headless CMS, this documentation can &lt;em&gt;evolve&lt;/em&gt; with the times; Create a voice-app that pulls the documentation from Kontent, integrate personalization to better serve the visitor, add analytics or commenting with machine learning to gauge the helpfulness of a page. The possibilities and complexity can scale with the project needs. I’d say that’s pretty fantastic!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F95aa0422-8451-4148-93cf-8aca59551b3e%2FKentico_Kontent.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F95aa0422-8451-4148-93cf-8aca59551b3e%2FKentico_Kontent.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Kentico Kontent architecture possibilities pictured above.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Can I see this evolution in action?
&lt;/h2&gt;

&lt;p&gt;Absolutely! &lt;a href="https://5fb2a7f3aec91f000740cba3--sad-clarke-a5925e.netlify.app/" rel="noopener noreferrer"&gt;The documentation&lt;/a&gt; explaining how to set up the Kontent + Docusaurus Boilerplate was written using Kontent and Docusaurus. You can also set up your own project using the instructions in the &lt;a href="https://github.com/kentico-michaelb/kontent-docusaurus-boilerplate" rel="noopener noreferrer"&gt;Kontent + Docusaurus Boilerplate GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%3A443%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F3f8dcb1b-d655-409c-8d74-a9f96e89c082%2FKontent_Docusaurus_FireFox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets-us-01.kc-usercontent.com%3A443%2Ff1c5bba1-dddc-00d2-e9ec-9d20ce57ea18%2F3f8dcb1b-d655-409c-8d74-a9f96e89c082%2FKontent_Docusaurus_FireFox.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t wait for a meteorite
&lt;/h2&gt;

&lt;p&gt;In this article, we highlighted the benefits of replacing old documentation with Kontent and Docusaurus and how a boilerplate can streamline your documentation creation. So don’t wait for a cataclysmic event, blow up your ancient documentation habits, and give Kontent and Docusaurus a try!&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>headlesscms</category>
      <category>ssg</category>
      <category>docusaurus</category>
    </item>
    <item>
      <title>Machine learning: How to ensure labels are smart, not lucky</title>
      <dc:creator>Michael Berry</dc:creator>
      <pubDate>Fri, 12 Jun 2020 13:55:02 +0000</pubDate>
      <link>https://forem.com/kenticomichaelb/machine-learning-how-to-ensure-labels-are-smart-not-lucky-3gpp</link>
      <guid>https://forem.com/kenticomichaelb/machine-learning-how-to-ensure-labels-are-smart-not-lucky-3gpp</guid>
      <description>&lt;p&gt;Without labels and categories organizing our lives, the world would be chaos. However, what you consider to be a “sensible label” for something may not be the term others would use, and choosing the “correct” terms turns into a game of chance. So how can you guarantee that your project’s content is organized with appropriate and consistent categories?&lt;/p&gt;

&lt;p&gt;In this article, I'll demonstrate how to use &lt;a href="https://dotnet.microsoft.com/apps/machinelearning-ai/ml-dotnet"&gt;ML.NET&lt;/a&gt; to automate adding taxonomy terms (tags) to content items in the headless CMS &lt;a href="https://kontent.ai/"&gt;Kentico Kontent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s turn that game of “lucky labeling” into a smarter solution!&lt;/p&gt;

&lt;h3&gt;
  
  
  No data science or machine learning degree required
&lt;/h3&gt;

&lt;p&gt;I was initially intimidated by the algorithms, statistics, and other mathematical complexities surrounding creating machine learning models, but the fears were quickly dispelled when I discovered &lt;a href="https://dotnet.microsoft.com/apps/machinelearning-ai/ml-dotnet/model-builder"&gt;Microsoft’s ML.NET Model Builder Extension for Visual Studio&lt;/a&gt;. This extension uses automated machine learning to find the best machine learning algorithm for your scenario and data set. Once it chooses the best algorithm, you’re able to select that algorithm, train your model, and evaluate it all within a graphical user interface. &lt;strong&gt;The big takeaway:&lt;/strong&gt; It requires &lt;strong&gt;zero&lt;/strong&gt; machine learning/data science experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  When is ML.NET categorization helpful in a Kontent project?
&lt;/h3&gt;

&lt;p&gt;Applying machine learning for content categorization is helpful if:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have similar taxonomy terms in your project and editors are struggling to decide which term best fits their content.&lt;/li&gt;
&lt;li&gt;There are a lot of taxonomy terms in the project, and you need to identify which ones can be removed.&lt;/li&gt;
&lt;li&gt;You have a large data set and need to identify what terms you should add to your project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll focus on scenario #1: &lt;em&gt;You have similar taxonomy terms in your project, and editors are struggling to decide which term best fits their content.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Categorizing the Chaotic Netflix Catalog
&lt;/h3&gt;

&lt;p&gt;Let's take a look how ML.NET can automate content categorization. We'll aim to automatically suggest Netflix categories for movies based upon their titles, descriptions and ratings using a .NET Core 3.1 console application. Once the application is working, we'll extend the functionality to the headless CMS. For the sake of simplicity, I’ve focused on a non-hierarchical labeling structure where one taxonomy term is assigned to a movie, and have broken the steps down into the following stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Training the model&lt;/li&gt;
&lt;li&gt;Integrating the headless CMS&lt;/li&gt;
&lt;li&gt;Analyzing the content&lt;/li&gt;
&lt;li&gt;Importing the suggested term&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Training the model
&lt;/h3&gt;

&lt;p&gt;The first step for creating a ML.NET model is to find a suitable dataset in the supported SQL Database, CSV, or TSV formats. We'll download a &lt;a href="https://github.com/kentico-michaelb/MLNET-kontent-taxonomy-app/blob/master/MLNET-kontent-taxonomy-app/files/netflix_movies.csv"&gt;CSV file&lt;/a&gt;. We need to download and install &lt;a href="https://dotnet.microsoft.com/apps/machinelearning-ai/ml-dotnet/model-builder"&gt;Microsoft’s ML.NET Model Builder Extension for Visual Studio&lt;/a&gt;. Once downloaded, open the console application in Visual Studio, right-click the project, and use “Add” to add “Machine Learning” to the project.&lt;/p&gt;

&lt;p&gt;After machine learning has been added, the extension provides six machine learning templates to choose from. Per &lt;a href="https://docs.microsoft.com/en-us/dotnet/machine-learning/automate-training-with-model-builder#scenario"&gt;Microsoft’s scenario examples and descriptions&lt;/a&gt;, “Issue Classification” best fits since we are trying to predict labels in 3+ categories, similar to Microsoft's GitHub Issue Classification example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_hbe511X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/7dafdf19-d057-42a3-9500-a1e5ede86398/ML_NET-Image-classification-scenario.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_hbe511X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/7dafdf19-d057-42a3-9500-a1e5ede86398/ML_NET-Image-classification-scenario.png" alt="alt text" title="ML.NET Issue Classification option" width="880" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next steps are for training the model. Select the CSV mentioned earlier, select the "listed_in" column as the column to predict, and choose the "description", "rating", and "title" columns the model should base its suggestion upon. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mJ83tlcm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/9f711079-4ac6-4b8b-9bbe-4f5f95725fa7/ML_NET-train-model_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mJ83tlcm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/9f711079-4ac6-4b8b-9bbe-4f5f95725fa7/ML_NET-train-model_.png" alt="alt text" title="ML.NET Add Data option" width="880" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the “Train” button at the bottom of the screen. This opens a screen to specify how long to train the model. Microsoft has some training recommendations here: &lt;a href="https://docs.microsoft.com/en-us/dotnet/machine-learning/automate-training-with-model-builder#how-long-should-i-train-for"&gt;https://docs.microsoft.com/en-us/dotnet/machine-learning/automate-training-with-model-builder#how-long-should-i-train-for&lt;/a&gt;. In my project, I chose 120 seconds and it managed a 57.44% accuracy. Clicking “Evaluate” allowed me to test the model against some of the data in the CSV and broke down the top 5 prediction results. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4e2ONYDq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/d6d85ad5-4679-40f5-acab-74f360e4864e/ML_NET-evaluate-model.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4e2ONYDq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/d6d85ad5-4679-40f5-acab-74f360e4864e/ML_NET-evaluate-model.png" alt="alt text" title="ML.NET Evaluate data results" width="880" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few tests, I wasn’t happy with the 57% accuracy results, suggesting that the Microsoft training time recommendation was too short for the seventeen different classification categories in the CSV. That inspired me to re-run the training for 1 hour, which resulted in an improved ~66% accuracy, so don't be afraid to increase the training time.&lt;/p&gt;

&lt;p&gt;Click the “Code” button to &lt;a href="https://dotnet.microsoft.com/learn/ml-dotnet/get-started-tutorial/generate-code"&gt;generate and add two machine learning projects&lt;/a&gt; to the project's solution. Once the ML projects are there, &lt;a href="https://dotnet.microsoft.com/learn/ml-dotnet/get-started-tutorial/consume"&gt;consume the model&lt;/a&gt; using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add input data&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"The Nightmare Before Christmas"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Jack Skellington, king of Halloween Town, "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                    &lt;span class="s"&gt;"discovers Christmas Town, but his attempts to bring Christmas to his home causes confusion."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rating&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PG"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Load model and predict output of sample data&lt;/span&gt;
&lt;span class="n"&gt;ModelOutput&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsumeModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prediction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the console application, and it will correctly produce the output “Children &amp;amp; Family Movies,” demonstrating that the ML.NET model is ready to be linked to content in a headless CMS. &lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating the headless CMS
&lt;/h3&gt;

&lt;p&gt;In a real-world scenario, the project may exist before the need for “Smart” labeling arose, but in our case the demand for machine learning and a headless CMS was driven by some new, fancy Netflix data.&lt;/p&gt;

&lt;p&gt;To set up Kontent, create a new project that will contain a single content type named "Movie" and a single taxonomy group called "Listed in." The content type and taxonomy group consist of elements and terms from the CSV used to build the machine learning model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Title: a simple text element&lt;/li&gt;
&lt;li&gt;Rating: a multiple-choice element contains all 11 content ratings (rated TV-G to rated R) in the CSV&lt;/li&gt;
&lt;li&gt;Release Date: a date &amp;amp; time element&lt;/li&gt;
&lt;li&gt;Description: a rich text element&lt;/li&gt;
&lt;li&gt;Listed in: a taxonomy group containing all 17 of “Listed in” terms in the CSV&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xLr2DF3y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/2b7bd627-26a3-4344-96ed-c8968f92e2b0/MLT_NET-movie-content-type_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xLr2DF3y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/2b7bd627-26a3-4344-96ed-c8968f92e2b0/MLT_NET-movie-content-type_.png" alt="alt text" title="Kentico Kontent movie content type" width="880" height="767"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S25Rj4Jv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/7b4464e6-bd8c-4bff-b7b7-83d1dacdf8d5/MLT_NET_taxonomy-terms_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S25Rj4Jv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/7b4464e6-bd8c-4bff-b7b7-83d1dacdf8d5/MLT_NET_taxonomy-terms_.png" alt="alt text" title="Kentico Kontent listed in taxonomy group" width="880" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the content type and taxonomy terms are present, start making some sample content item variants using not-yet-released movie titles, ratings, and descriptions. I leave them in the “Draft” workflow step to ensure you are able to upsert data to the items from the console application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EKiJWDuV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/68871a13-6d67-4897-8a74-3700e93fe0a2/ML_NET-content-item-variant.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EKiJWDuV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/68871a13-6d67-4897-8a74-3700e93fe0a2/ML_NET-content-item-variant.png" alt="alt text" title="Kentico Kontent movie content item" width="880" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Analyzing the content
&lt;/h3&gt;

&lt;p&gt;Now it is time to pull that content from Kentico Kontent and feed it into the console application to make a prediction. For this, install the &lt;a href="https://www.nuget.org/packages/Kentico.Kontent.Delivery"&gt;Kentico Kontent .NET SDK NuGet package&lt;/a&gt;, create a class to store your &lt;a href="https://docs.kontent.ai/tutorials/develop-apps/build-strong-foundation/set-up-preview#a-get-the-latest-version-of-everything"&gt;API keys&lt;/a&gt;, and a &lt;em&gt;MovieListing&lt;/em&gt; Class that returns a strongly typed &lt;em&gt;Movie&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/Configuration/KontentKeys.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KontentKeys&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ProjectId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;PreviewApiKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ManagementApiKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/MovieListing.cs&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MovieListing&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;IDeliveryClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MovieListing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KontentKeys&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DeliveryClientBuilder&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithProjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProjectId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UsePreviewApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PreviewApiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;       

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DeliveryItemListingResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetMovies&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;DeliveryItemListingResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetItemsAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EqualsFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"system.type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"movie"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ElementsParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"listed_in"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;       
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/Models/Movie.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Movie&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Codename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"movie"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;DescriptionCodename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ListedInCodename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"listed_in"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;RatingCodename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ReleaseDateCodename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"release_date"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;TitleCodename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"listed_in"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TaxonomyTerm&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ListedIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MultipleChoiceOption&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Rating&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"release_date"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ReleaseDate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ContentItemSystemAttributes&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instantiate this class from the main Program.cs, set and pass the API keys to it, and return a DeliveryItemListingResponse to loop through. Set up the ML.NET consumption logic in a separate class for easier maintainability and a cleaner main program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/TaxonomyPredictor.cs&lt;/span&gt;
&lt;span class="c1"&gt;//generated using: https://github.com/Kentico/kontent-generators-net&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaxonomyPredictor&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetTaxonomy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Add input data&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ModelInput&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rating&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rating&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;First&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Load model and predict output of sample data&lt;/span&gt;
            &lt;span class="n"&gt;ModelOutput&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConsumeModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Listing best match: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prediction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;//formatting to meet Kontent codename requirements&lt;/span&gt;
            &lt;span class="c1"&gt;//ex: Children &amp;amp; Family Movies =&amp;gt; children___family_movies&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;formatted_prediction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prediction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;amp;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;formatted_prediction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instantiate a "predictor" from Program.cs. The TaxonomyPredictor.GetTaxonomy(Movie) method can now be used to suggest “listed in” terms when looping through the list of movies returned by MovieListing.GetMovies().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/Program.cs&lt;/span&gt;

&lt;span class="n"&gt;MovieListing&lt;/span&gt; &lt;span class="n"&gt;movieListing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MovieListing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;TaxonomyPredictor&lt;/span&gt; &lt;span class="n"&gt;predictor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TaxonomyPredictor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;movieListing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetMovies&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListedIn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;formatted_prediction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;predictor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTaxonomy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formatted_prediction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will produce the “best match” taxonomy term in the console when run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing the suggested term
&lt;/h3&gt;

&lt;p&gt;Finally, it is time to automate upserting the suggested taxonomy terms to the headless CMS. Use the &lt;a href="https://github.com/Kentico/kontent-management-sdk-net"&gt;Kentico Kontent .NET Management SDK&lt;/a&gt; in a separate class called TaxonomyImporter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/TaxonomyImporter.cs&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaxonomyImporter&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ManagementClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TaxonomyImporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KontentKeys&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ManagementOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ManagementOptions&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ProjectId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;ApiKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ManagementApiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="c1"&gt;// Initializes an instance of the ManagementClient client&lt;/span&gt;
            &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ManagementClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;UpsertTaxonomy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;listing_prediction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="n"&gt;MovieImport&lt;/span&gt; &lt;span class="n"&gt;stronglyTypedElements&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MovieImport&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;ListedIn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;TaxonomyTermIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ByCodename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listing_prediction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="c1"&gt;// Specifies the content item and the language variant&lt;/span&gt;
            &lt;span class="n"&gt;ContentItemIdentifier&lt;/span&gt; &lt;span class="n"&gt;itemIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ContentItemIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ByCodename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Codename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;LanguageIdentifier&lt;/span&gt; &lt;span class="n"&gt;languageIdentifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LanguageIdentifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ByCodename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Language&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;ContentItemVariantIdentifier&lt;/span&gt; &lt;span class="n"&gt;identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ContentItemVariantIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;languageIdentifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Upserts a language variant of your content item&lt;/span&gt;
            &lt;span class="n"&gt;ContentItemVariantModel&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MovieImport&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpsertContentItemVariantAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stronglyTypedElements&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" updated."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instantiate the importer in Program.cs to use the UpsertTaxonomy method when looping through the list of movies. Create a strongly typed &lt;strong&gt;MovieImport&lt;/strong&gt; model that inherits from &lt;strong&gt;Movie&lt;/strong&gt; in order to accommodate what the Kentico Kontent Management SDK expects when upserting content item variants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/Models/MovieImport.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MovieImport&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Movie&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"listed_in"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TaxonomyTermIdentifier&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ListedIn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JsonProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rating"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MultipleChoiceOptionIdentifier&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Rating&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is done, add logic to use environment variables, appsettings.json, and configuration binding using a combination of &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1"&gt;.NET Core configuration options&lt;/a&gt; make your API keys hidden and secure! Below is the completed Program.cs file, configuration files, and result of running the program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/Program.cs&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting program..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;environmentName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ASPNETCORE_ENVIRONMENT"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"appsettings.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;environmentName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/Configuration/KontentKeys.cs&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;KontentKeys&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;ConfigurationBinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"KontentKeys"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;MovieListing&lt;/span&gt; &lt;span class="n"&gt;movieListing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MovieListing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;TaxonomyPredictor&lt;/span&gt; &lt;span class="n"&gt;predictor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TaxonomyPredictor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;TaxonomyImporter&lt;/span&gt; &lt;span class="n"&gt;importer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TaxonomyImporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;movieListing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetMovies&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Movie&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListedIn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;formatted_prediction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;predictor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTaxonomy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;upsertResponse&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;importer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpsertTaxonomy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formatted_prediction&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upsertResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Program finished."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;       
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/Configuration/KontentKeys.cs&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KontentKeys&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ProjectId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;PreviewApiKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ManagementApiKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//file location: MLNET-kontent-taxonomy-app/appsettings.json&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s"&gt;"KontentKeys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"ProjectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;YOUR PROJECT ID&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"PreviewApiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;YOUR PREVIEW API KEY&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"ManagementApiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;YOUR MANAGEMENT API KEY&amp;gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P95VEDzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/1018405f-3326-4b30-8cd4-d29450842860/ML-final-result.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P95VEDzI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://assets-us-01.kc-usercontent.com/4e9bdd7a-2db8-4c33-a13a-0c368ec2f108/1018405f-3326-4b30-8cd4-d29450842860/ML-final-result.gif" alt="alt text" title="Completed demonstration gif" width="880" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Imagine the possibilities
&lt;/h3&gt;

&lt;p&gt;In this article, I solved inconsistent content tagging using the ML.NET Model Builder, a .NET Core console application, and a Kentico Kontent project. &lt;/p&gt;

&lt;p&gt;In my project, I focused on a flat taxonomy structure and straightforward classification scenario, but this is just one of almost endless machine learning possibilities. For example, combining machine learning with webhook notifications will allow automatic improvements to created content.  With this combination, it's possible to automatically write descriptions for uploaded assets or suggest the best SEO keywords for blog posts.&lt;/p&gt;

&lt;p&gt;You can find the full source code for my application, supporting data files, and instructions on how to test your own copy of the project here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kentico-michaelb/MLNET-kontent-taxonomy-app"&gt;Get the code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this inspired you to stop being lucky, and start being consistent using machine learning! 😃&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>machinelearning</category>
      <category>dotnet</category>
      <category>headless</category>
    </item>
    <item>
      <title>How to Keep It Stupid Simple: Use .NET Core Razor Pages</title>
      <dc:creator>Michael Berry</dc:creator>
      <pubDate>Thu, 26 Mar 2020 13:07:43 +0000</pubDate>
      <link>https://forem.com/kenticomichaelb/how-to-keep-it-stupid-simple-use-net-core-razor-pages-110n</link>
      <guid>https://forem.com/kenticomichaelb/how-to-keep-it-stupid-simple-use-net-core-razor-pages-110n</guid>
      <description>&lt;p&gt;With recent improvements to the .NET platform and the ever-evolving CMS market, guidance on creating feature-rich websites is available at every turn. But what if you don’t have time for complicated tutorials and just need a modern, simple, easy-to-manage site?&lt;/p&gt;

&lt;p&gt;With the release of .NET Core 3.x, advances in &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/overview?view=aspnetcore-3.1" title="ASP.NET Core MVC"&gt;ASP.NET Core MVC&lt;/a&gt; and the emergence of &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/get-started?view=aspnetcore-3.1&amp;amp;tabs=visual-studio" title="Blazor preview"&gt;Blazor preview&lt;/a&gt;, there is a lot of excitement in the .NET community. Although these improvements are wonderful, the robustness of MVC and the newness of Blazor may make some projects feel over-complicated. In addition to development complexity, accounting for CMS installations and upgrades can add extra overhead to a project. In the following article, I’ll discuss how &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-3.1&amp;amp;tabs=visual-studio" title="ASP.NET Core Razor Pages"&gt;ASP.NET Core Razor Pages&lt;/a&gt; and &lt;a href="https://kontent.ai/blog/the-importance-of-the-content-as-a-service-mindset" title="Content as a Service"&gt;Content as a Service (CaaS)&lt;/a&gt; can help prevent overhead and complexity. If you’re a strong believer in the &lt;a href="https://en.wikipedia.org/wiki/KISS_principle" title="KISS"&gt;KISS&lt;/a&gt; principle like I am, I hope this article shows that your .NET development can be stupid simple!&lt;/p&gt;

&lt;h2&gt;Why Razor Pages?&lt;/h2&gt;

&lt;h3&gt;Straightforward structure&lt;/h3&gt;

&lt;p&gt;ASP.NET Core Razor Pages takes a page-focused approach to the project structure. It co-locates a page’s view and its PageModel (logic pertaining to a view) in a “Pages” directory. If you’ve modeled your content and identified that the majority of it is structured around the concept of a “page,” then Razor Pages may be the perfect framework for your project.&lt;/p&gt;

&lt;p&gt;For example, in my sample coffee shop project, I have a lot of basic informational pages about coffee and cafes. If I wanted to make changes to the “Cafes” page, I’d navigate to ~/Pages/Cafes/Index.cshtml to edit the view or ~/Pages/Cafes/Index.cshtml/Index.cshtml.cs to edit the logic. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_jQ_oK_Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vnvoarfy7hn964fenwuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_jQ_oK_Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vnvoarfy7hn964fenwuj.png" alt="Alt Text" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This single-responsibility-style structure makes finding the code for specific pages less difficult since pages are found in the “Pages” folder.&lt;/p&gt;

&lt;h3&gt;Familiarity&lt;/h3&gt;

&lt;p&gt;If you’ve ever used Web Forms, the above description of layout and logic being colocated should sound very familiar.  Razor Pages is sometimes viewed as the successor to Web Forms due to its structure, but it comes with &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-3.1#why-choose-aspnet-core" title="features of .NET Core"&gt;modern features of .NET Core&lt;/a&gt;.  So if you’ve been dreading the transition from Web Forms to MVC in order to use .NET Core features, I’m sure Razor Pages will be a welcoming experience.&lt;/p&gt;

&lt;h3&gt;Options&lt;/h3&gt;

&lt;p&gt;Routing can be very confusing or prone to errors. This being said, instead of using MVC’s less straightforward controller -&amp;gt; action -&amp;gt; view approach, default routing in a Razor Pages project uses the location of a page within the project to determine its URL.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NOexlh0h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ic6xm2butqifj47n2dve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NOexlh0h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ic6xm2butqifj47n2dve.png" alt="Alt Text" width="880" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, a request to “http://domain/Articles” will be handled by ~/Pages/Articles/Index.cshtml and its respective .cs file, while a request to “http://domain/articles/detail” will be handled by ~/Pages/Articles/Detail.cshtml and its .cs file.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MT6jjGmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0tq38o6m5y90xsiog38y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MT6jjGmh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0tq38o6m5y90xsiog38y.png" alt="Alt Text" width="880" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although this behavior helps simplify routing, it doesn’t prevent you from having custom routes.  For example, in my sample project, I opted to use an &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-3.1&amp;amp;tabs=visual-studio#custom-routes" title="page directive"&gt;@page directive&lt;/a&gt; to append a segment to my article URLs in order to return a specific article and present a practical address to visitors. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uB1DL2wG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tsk2k8pyca2wclvo6nja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uB1DL2wG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tsk2k8pyca2wclvo6nja.png" alt="Alt Text" width="880" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Articles/Detail.cshtml view, the directive @page “{UrlPattern}” is used. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--07xZdD_O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1ga4dzl9eezyb5zdjwng.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--07xZdD_O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1ga4dzl9eezyb5zdjwng.png" alt="Alt Text" width="880" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This UrlPattern value is used in the page’s logic to return content from my headless CMS’s API. We’ll discuss my choice of Content-as-a-Service/headless CMS choice later in this article, but for now, rest assured that the @page directive allows me to add a segment to a URL such as “http://domain/Articles/Detail/&lt;b&gt;coffee-processing-techniques&lt;/b&gt;.”&lt;/p&gt;

&lt;p&gt;In addition to page directives, Razor Pages projects can use &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.mvcservicecollectionextensions.addrazorpages?view=aspnetcore-3.1" title="AddRazorPages"&gt;AddRazorPages&lt;/a&gt; and &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.mvcrazorpagesmvcbuilderextensions.addrazorpagesoptions?view=aspnetcore-3.1" title="AddRazorPagesOptions"&gt;AddRazorPagesOptions&lt;/a&gt; on the service collection in the Startup class. This opens up a lot of routing possibilities described in the official &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/razor-pages/razor-pages-conventions?view=aspnetcore-3.1" title="Microsoft documentation for Razor Pages routing"&gt;Microsoft documentation for Razor Pages routing&lt;/a&gt;. To showcase this, I used custom routing for my homepage. I wanted to organize the homepage in a sub-folder similar to the other pages in my site, and I didn’t want “/Home” in the URL. This isn’t a complicated routing scenario, but you can see how this approach allows for more flexible routing.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XSyevf1V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lgjknwqwvt7tlsr53yr3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XSyevf1V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lgjknwqwvt7tlsr53yr3.png" alt="Alt Text" width="880" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although I’ve focused on the page-centric structure thus far, that doesn’t mean that your project is limited to pages. Functionality that is distinct from pages or that can be reused across multiple pages can be implemented using &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1" title="ViewComponents"&gt;ViewComponents&lt;/a&gt;. For example, in my project, I created a Components folder within Pages that contains the logic and view for displaying contact information. To use this component in my site’s footer, I just call&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;C# @await Component.InvokeAsync("Contact")&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 in my shared layout. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qKw67X6R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/d4yq3krjsmozl3u120oh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qKw67X6R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/d4yq3krjsmozl3u120oh.png" alt="Alt Text" width="880" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although this adds a folder within Pages that isn’t directly related to any given page, this component is responsible for rendering content to pages, so it still seems sensible to have it in this location.&lt;/p&gt;

&lt;p&gt;These are a few examples of how Razor Pages can allow developers to reap the numerous benefits of modern features provided in .NET Core while also keeping their project as simple as possible. This being said, writing code is only a fraction of the effort it takes to create a great website. How can the theme of “simplicity” extend to the content management of the project? &lt;/p&gt;

&lt;h2&gt;Why CaaS?&lt;/h2&gt;

&lt;p&gt;For starters, what is “CaaS?” Content as a Service is an approach to creating content in a Software as a Service content management system and delivering that content over multiple channels using an API. You might be thinking, “That sounds a lot like a headless CMS...” Exactly! The CaaS approach to content creation leverages a headless CMS but also includes various services, bundles, or offerings in addition to giving editors a CMS and developers an API. For the sake of this article, you can substitute “CaaS" for “headless CMS” if it better fits your experience.&lt;/p&gt;

&lt;h3&gt;No CMS maintenance&lt;/h3&gt; 

&lt;p&gt;If you’ve ever managed an on-premise CMS or have been involved in CMS deployment and maintenance, you know that it comes with a fair amount of overhead. Installations, managing maintenance windows, doing upgrades, applying bug fixes, upgrading infrastructure... The list goes on and on. These tasks take time away from development and content creation. With a Content-as-a-Service offering, the vendor takes care of the CMS and infrastructural maintenance, which allows you to focus on development.&lt;/p&gt;

&lt;h3&gt;Omnichannel&lt;/h3&gt; 

&lt;p&gt;Even though we’re trying to keep our projects simple, the world is not a simple place. Customers have complex needs. In 2020 and beyond, customers are going to expect their content to be available on websites, mobile applications, voice assistants, and other channels. By creating your content in a system or service that is platform agnostic and can be delivered to any channel over an API, you’re ensuring the future expansion of your project to these other mediums is possible.&lt;/p&gt;

&lt;h3&gt;Structured content&lt;/h3&gt;

&lt;p&gt;In addition to the omnichannel benefits, working with CaaS makes development easier by ensuring content is structured. It’s a common practice for CaaS products to send API responses in either JSON or XML format. This streamlines the development process by making content returned from the API predictable and consistent across the project. If you’ve done proper content modeling in the early stages of development, there shouldn’t be any guessing involved when using a response from your CaaS API. Minimizing guesswork with uniform, structured content delivery will definitely make the development process smoother.&lt;/p&gt;

&lt;h2&gt;Why Kentico Kontent?&lt;/h2&gt;

&lt;p&gt;For my sample project, I chose to use &lt;a href="https://kontent.ai" title="Kentico Kontent"&gt;Kentico Kontent&lt;/a&gt; as my CaaS offering. In addition to meeting all of the requirements listed in the Why CaaS? section above, &lt;a href="https://github.com/Kentico/kontent-delivery-sdk-net" title="Kontent .NET SDK"&gt;Kentico Kontent’s open-source .NET SDK&lt;/a&gt; and supporting tools further improve the development experience.&lt;/p&gt;

&lt;h3&gt;.NET SDK&lt;/h3&gt; 

&lt;p&gt;In 2020, having a clean API is not the pinnacle of a pleasant developer experience. I’d argue that many developers leveraging an API service jump into SDK documentation almost as quickly as the base API documentation itself. With this in mind, Kentico Kontent’s .NET SDK has sensible naming conventions, &lt;a href="https://github.com/Kentico/kontent-delivery-sdk-net#kentico-kontent-delivery-net-sdk" title="Kontent .NET SDK documentation"&gt;extensive documentation&lt;/a&gt;, and &lt;a href="https://github.com/Kentico/kontent-delivery-sdk-net/wiki/Instantiating-DeliveryClient-with-Configuration-API-and-DI-in-ASP.NET-Core-MVC-apps" title="Kontent .NET SDK documentation"&gt;dependency injection support&lt;/a&gt; that makes setting up the SDK and querying content a breeze. For example, other than the code related to dependency injection and strong typing, my call for retrieving an “About Us” item from Kentico Kontent is a single line of code:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tRkw04bl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ik9wt7dwr60fp5q371ge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tRkw04bl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ik9wt7dwr60fp5q371ge.png" alt="Alt Text" width="880" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code returns a structured JSON response containing all the details about my “About Us” content item that I created in the Kentico Kontent user interface, then casts it to my AboutUs model.&lt;/p&gt;

&lt;p&gt;Setting up the application to use dependency injection with the SDK can be accomplished in a single service registration in the Services.cs file that uses a standard configuration in an appsettings.json file:&lt;/p&gt;

&lt;h4&gt;Startup.cs&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IConfiguration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IConfigurationRoot&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDeliveryClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;appsetings.json&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DeliveryOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ProjectId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;Kontent_ProjectId_Value&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although I’ve focused on a minimal setup in the above examples, don’t worry! If your project needs more advanced functionality, the Kentico Kontent .NET SDK has lots of features and options, making it scale comfortably with increasing demands or complexity.&lt;/p&gt;

&lt;h3&gt;Model generator&lt;/h3&gt; 

&lt;p&gt;When using a Model-View-ViewModel design pattern like Razor Pages, creating and updating models can be a considerable amount of upfront and ongoing work, especially if your content creators or project managers change the content structure or requirements. To account for this, Kentico Kontent has an open-source tool that automatically generates strongly-typed models for your project. If you rely on strongly-typed models for IntelliSense, hate manually editing models, and don’t mind using a CLI tool to take on this otherwise tedious task, you’re going to love that Kentico Kontent provided the &lt;a href="https://github.com/Kentico/kontent-generators-net#kentico-kontent-model-generator-utility-for-net" title="Kontent model generator for .NET"&gt;Kontent model generator for .NET projects&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Not just for developers&lt;/h3&gt;

&lt;p&gt;In addition to the development perks, a headless CMS will benefit editors and their managers too. If you’re interested in content creation features, check out &lt;a href="https://kontent.ai/marketers" title="Kontent for marketers"&gt;this page&lt;/a&gt;, or you can &lt;a href="https://app.kontent.ai/sign-up" title="try Kontent out"&gt;try it out&lt;/a&gt; for yourself. No manuals required!&lt;/p&gt;

&lt;h2&gt;Summary&lt;/h2&gt;

&lt;p&gt;In this article, we discussed what aspects of ASP.NET Core Razor Pages make it a great framework for creating simple, but modern websites in both structure and feature sets. We also described how pairing Razor Pages with a CaaS offering like Kentico Kontent can further reduce development overhead. I hope that demonstrating my &lt;a href="https://github.com/Kentico/kontent-sample-app-razorpages" title="sample project"&gt;sample project&lt;/a&gt; showed you how we can keep it simple in 2020 and beyond!&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>headless</category>
      <category>dotnet</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Lessons Learned: Using a Headless CMS without an SDK</title>
      <dc:creator>Michael Berry</dc:creator>
      <pubDate>Thu, 21 Nov 2019 14:12:11 +0000</pubDate>
      <link>https://forem.com/kenticomichaelb/lessons-learned-using-a-headless-cms-without-an-sdk-51lm</link>
      <guid>https://forem.com/kenticomichaelb/lessons-learned-using-a-headless-cms-without-an-sdk-51lm</guid>
      <description>&lt;p&gt;How complicated is it for a beginner to implement a headless CMS powered site without an SDK? I chose Python and Flask to build a section of a sample site. Did I succeed? And what did I learn in the process?&lt;/p&gt;

&lt;h2&gt;Why Python and Flask?&lt;/h2&gt;

&lt;p&gt;Earlier this year, I found myself spending a lot of time manually grinding through administrative tasks that felt repetitive, time consuming, and tedious. I began to search for the best way for a non-developer to start automating tasks. After a few days of browsing, I noticed that I always seemed to end up on a blog, forum, or community post talking about Python. I picked up a few books, took an online introductory course, and started writing scripts to help me with my work.&lt;/p&gt;

&lt;p&gt;After completing some basic tutorials, I wondered if I could somehow connect these newly learned skills to the Content-as-a-Service product that my team supports, &lt;a href="https://kontent.ai/"&gt;Kentico Kontent&lt;/a&gt;. Furthermore, could I do it without an SDK since Kontent does not currently have one for Python? To test this, I decided to recreate the articles section of the &lt;a href="https://docs.kontent.ai/tutorials/develop-apps/get-started/running-a-sample-application#a-accessing-the-sample-project"&gt;Dancing Goat sample project&lt;/a&gt; that comes with new Kontent subscriptions and catalog some of the lessons I learned from the project.&lt;/p&gt;

&lt;h2&gt;Learn the Terminology&lt;/h2&gt;

&lt;p&gt;If you've decided to use Kentico Kontent for your project, reviewing and bookmarking the &lt;a href="https://docs.kontent.ai/tutorials/assorted-items/dive-deeper-into-kentico-kontent/terminology"&gt;terminology&lt;/a&gt; we use throughout the documentation and product UI is an important step.  If you're diving into a project and haven't established a firm grasp on the differences between content types, content items, and components, then the Kentico Kontent documentation won’t deliver its true value.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Lesson learned: taking the time to “learn the lingo” makes working in the Kentico Kontent ecosystem easier.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Establish Your Project Scope&lt;/h2&gt;

&lt;p&gt;Scope creep has always been an issue of mine, and I don't think I'm alone.  When I began this project, I naively wanted to recreate the Dancing Goat site in its entirety while leveraging all of Kentico Kontent’s features. This wasn't an effective plan.&lt;br&gt;
Although no Kentico Kontent features are too difficult to implement, starting small and focusing just on the home page and articles in the sample site gave me the best opportunity to learn.  I could see the API in action, learn about the Flask microframework, and understand how content modeling was done in the sample project.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Lesson learned: starting small and then scaling up prevents the project from becoming overwhelming and provides the opportunity to learn the Kentico Kontent basics.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Structure Your Content&lt;/h2&gt;

&lt;p&gt;Knowing how the content types and content items connected to each other in my sample project saved me a significant amount of time. Since mapping out the home page and article relationships was so useful in a pre-generated project, I know it will be even more beneficial for a new project.&lt;br&gt;
I've seen a fair share of real customer projects that have redundant entries because they didn't consider the relationships between their content prior to development.  This section on &lt;a href="https://docs.kontent.ai/tutorials/set-up-projects/define-content-models/content-modeling-101"&gt;content modeling&lt;/a&gt; from the Kentico Kontent documentation will likely be helpful in avoiding this pitfall.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Lesson learned: prevent duplicative content by understanding what is reusable and which content is related.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Create Sample/Starter Content&lt;/h2&gt;

&lt;p&gt;I remember feeling a bit overwhelmed by the size of the Delivery API responses from my project. This is because I was using the sample site which has a lot of content items and linked items.  The sample site is a great reference tool, but may be more complex than most projects need to be.&lt;br&gt;
Building out a minimal set of content types and items related to the core functionality in your project can save time when developing your front-end application. Creating only a few content items in the project will make reading and using the JSON responses provided by the Delivery API more manageable.  You'll also have an easier time remembering the codenames of your content items, which you will be using a lot during testing.  Keep it simple, then scale up.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Lesson learned: only build a few content items of each type during the early phases of the project to make analyzing JSON responses more straightforward.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Identify the Necessary API Requests&lt;/h2&gt;

&lt;p&gt;I started using the &lt;a href="https://docs.kontent.ai/tutorials/develop-apps/get-started/using-the-apis-with-our-postman-collection"&gt;POSTman&lt;/a&gt; REST client to test and document the GET requests I needed to make in my project.  This was immensely helpful since I needed to construct requests in my front-end application using a Python library. It can also be helpful in other scenarios since:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You'll familiarize yourself with the &lt;a href="https://developer.kenticocloud.com/reference#delivery-api"&gt;Delivery API’s response structure&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You'll be more aware of the API's &lt;a href="https://developer.kenticocloud.com/reference#content-filtering"&gt;filtering capabilities&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can more easily determine if there is an issue with your code, the content, or our API endpoint during debugging.  By running tests in a REST client, you separate your code from our product.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;b&gt;Lesson learned: understanding the API calls in advance provides a helpful roadmap for building a front-end application and helps during debugging.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Decide If You Are Going to Use an SDK&lt;/h2&gt;

&lt;p&gt;I knew there wasn't a Python SDK for Kentico Kontent. I needed to find documentation on how to call API endpoints using Python and Flask. This may not be your situation, but it would be a shame if halfway through a JavaScript, .Net, PHP, Ruby, or Java project, you or your developers stumbled upon an SDK that would have made your lives a lot easier.&lt;/p&gt;

&lt;p&gt;For projects that will use an SDK, it is important to review the &lt;a href="https://docs.kontent.ai/tutorials/develop-apps/get-started/getting-development-resources"&gt;SDK documentation&lt;/a&gt;, stay up to date on current GitHub issue submissions, and check release notes for the chosen SDK. The SDK GitHub repositories and their accompanying documentation are updated frequently, including breaking change documentation when a new version of an SDK is released.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Lesson learned: collect and review relevant documentation before starting your project, and watch the GitHub repositories that you are using for your project.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Understand the Conventions in Your Selected Framework/Programming Language&lt;/h2&gt;

&lt;p&gt;Let's be honest, I started hacking away trying to get my front-end application to work. The approach should have been to explore Flask and Python conventions first.  Understanding what options I had for structuring my application, determining routes, error handling capabilities, and configuration file conventions should have come sooner rather than later in the project.  &lt;/p&gt;

&lt;p&gt;I'm sure Flask and Python purists could have a field day critiquing my sample project, but when I started applying some conventions, my application started to become more manageable.  If I had known this, I could have saved a lot of time!&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Lesson learned: learning about framework conventions prior to development makes the project easier to navigate and manage.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Formulate a Resolver Strategy (for Types and Inline Items)&lt;/h2&gt;

&lt;p&gt;Early on, it was clear that not using &lt;a href="https://docs.kontent.ai/tutorials/develop-apps/get-content/using-strongly-typed-models"&gt;strongly typed models&lt;/a&gt; made my project very messy and hard to read. Once I implemented my own &lt;a href="https://github.com/kentico-michaelb/cloud-sample-app-flask/blob/master/DancingGoat/helpers/type.py"&gt;type helper&lt;/a&gt;, similar to the &lt;a href="https://github.com/Kentico/cloud-sample-app-net/blob/master/DancingGoat/Models/ContentTypes/CustomTypeProvider.cs"&gt;CustomTypeProvider&lt;/a&gt; in the Kentico Kontent .Net sample site, I was able to improve the readability of my code.  To help with strongly typing models, Kentico created model generators for the &lt;a href="https://github.com/Kentico/cloud-generators-net"&gt;.Net&lt;/a&gt;, &lt;a href="https://github.com/Kentico/kontent-model-generator-js"&gt;JavaScript&lt;/a&gt;, and &lt;a href="https://github.com/Kentico/cloud-generators-java"&gt;Java&lt;/a&gt; SDKs.  This didn't help me with my Python project, but it is good to know that these tools are available for projects that use the SDKs.&lt;/p&gt;

&lt;p&gt;In addition to strongly typing models,  you'll want to resolve linked items, components, and text links that appear in Rich Text elements. In my project, I had to create a &lt;a href="https://github.com/kentico-michaelb/cloud-sample-app-flask/blob/master/DancingGoat/resolvers/inline_resolver.py"&gt;custom resolver&lt;/a&gt;. This resolver scrapes the content in my Rich Text elements for object and anchor tags and replaces the references with actual links or content. This seems confusing at first, but after spending some time looking at the JSON returned by the API for individual content items, you'll see that Rich Text elements come with accompanying “links” and “modular_content” values that can be used for inline link and item resolution.&lt;/p&gt;

&lt;p&gt;Luckily, the Kentico Kontent documentation has a section on &lt;a href="https://docs.kontent.ai/tutorials/develop-apps/get-content/dealing-with-structure-in-rich-text"&gt;dealing with structure in rich text&lt;/a&gt;, and there are built-in methods in the Kentico Kontent SDKs that handle this resolution for the most part. If you decide to use an SDK, a resolver strategy will be more straightforward.  Otherwise, spend some time looking at the structure of the API's JSON responses for linked items and rich text.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Lessons learned: plan on implementing strongly typed models from the beginning of the project, and review how inline content items appear in the Delivery API responses.&lt;/b&gt;&lt;/p&gt;

&lt;h2&gt;Start Building&lt;/h2&gt;

&lt;p&gt;I hope that after reading about the steps that helped me with my project, you feel more prepared to take on a new Kontent project of your own. As a Python and programming novice, I was able to get a Kentico Kontent blog up and running without an SDK, so I am sure you will create some killer apps that enable a flawless content strategy. You can check out my project &lt;a href="https://github.com/kentico-michaelb/cloud-sample-app-flask"&gt;here&lt;/a&gt; on GitHub, and feel free to leave comments and suggestions.&lt;br&gt;
If Vue.js is more interesting or relevant to you, my colleague Chris Jennings also wrote an excellent &lt;a href="https://kontent.ai/blog/lessons-learned-from-building-a-blog-on-kentico-cloud"&gt;"Lessons learned" blog post&lt;/a&gt; that may be helpful.&lt;/p&gt;

</description>
      <category>python</category>
      <category>kenticokontent</category>
      <category>headless</category>
      <category>caas</category>
    </item>
  </channel>
</rss>
