# Sunday, August 18, 2019

HerzogPoor Moses Herzog.

His wife convinced him to buy a house in Chicago; then tried to have him committed; then, left him for his best friend. On top of that, he is denied contact with either of his children.

Moses sets out for Chicago with his later father's antique pistol, 2 bullets, and murder on his mind. But nothing goes right, and his plans fall apart, and he is in an automobile accident and the police discover his loaded pistols.

The action does of Saul Bellow's Herzog does not happen until the novel is at least half over. But the author keeps us engaged through Moses's angst, as his life falls apart. Moses deals with his depression by writing letters - most of which are never sent and many of which are addressed to famous people such as Friedrich Nietzsche and (then-President) Dwight Eisenhower.

Herzog is unstable; but he is not crazy as his shrewish wife asserts. And his letter-writing decreases as he comes to terms with the cards he is dealt.

Bellow's writing is poignant and sad, but sometimes humorous, with lines such as

"Lead me into Penn Station"
and
"Will never understand what women want. What do they want? They eat green salad and drink human blood."

This book will appeal to anyone who has experienced the helplessness of a mid-life crisis; or the pain of betrayal; or the paralysis of uncertainty.

Sunday, August 18, 2019 4:19:00 PM (GMT Daylight Time, UTC+01:00)
# Saturday, August 17, 2019

UbikDeath is very different in the universe of Philip K. Dick's Ubik. Rather than simply ending life or transitioning to an afterlife, the recently dead remain cognizant. If properly preserved, technology exists that allows the living to communicate with the dead - sometimes for years. The consciousness slowly decays over time, after which the deceased finally cease to exist or are reborn anew.

Psychics are common enough in this universe to keep Glen Runciter busy. His company specializes in protecting people from psychic intrusion.

A group of powerful anti-psychics, led by Runciter, travel to the moon to meet with a potential client, where they are attacked and seemingly hurled back in time to the year 1939. But not everything is like the 1939 of their own past and they wonder exactly where they are.

Runciter is a good man and so is his employee Joe Chip. Chip is the perfect hero. He is loyal to his employer and dedicated to finding the truth; but he is financially irresponsible and always lacking funds (a problem when every appliance, device, and door requires a cash fee before it will operate).

Ubik is an imaginative tale of action and drama and speculative science fiction. Dick does an excellent job of taking far-fetched ideas and making them seem plausible. In under 250 pages he builds an entire world; then, destroys that world and creates another.

The story is both dark and humorous.

This novel influenced science fiction for many years after its 1969 publication. Douglas Adams must have read about the annoying talking doors when he wrote them into his Hitchhiker series. At times, we see nested realities, in a way that echoed in the 2010 film "Inception".

The narrative keeps changing our expectations. He keeps us guessing about reality versus illusion and life versus death and past versus present. And that's just the way Philip K. Dick wants it.

Saturday, August 17, 2019 8:21:00 AM (GMT Daylight Time, UTC+01:00)
# Friday, August 16, 2019

In the last article, I walked through the syntax of calling the Bing Spell Check service.

In this article, I will walk through a simple JavaScript application that calls this service.

If you want to follow along this sample is part of my Cognitive Services demos, which you can find on GitHub at https://github.com/DavidGiard/CognitiveSvcsDemos 

This project is found in the "SpellCheckDemo" folder.

Here is the main web page:

Listing 1:

<html>
<head>
    <title>Spell Check Demo</title>
    <script src="scripts/jquery-1.10.2.min.js"></script>
    <script src="scripts/script.js"></script>
    <script src="scripts/getkey.js"></script>
    <link rel="stylesheet" href="css/site.css">
</head>
 <body>
     <h1>Spell Check Demo</h1>
     <div>
         <textarea id="TextToCheck">Life ig buuutiful all the tyme
         </textarea>
     </div>
    <button id="SpellCheckButton">Check Spelling!</button>
     <div id="NewTextDiv"></div>
     <div id="OutputDiv"></div>

</body>
</html>
  

As you can see, the page consists of a text area with some misspelled text; a button; and 2 empty divs.

The page looks like this when rendered in a browser:

scjs01-PageOnLoad
Fig. 1

When the user clicks the button, we want to call the Spell Check service, sending it the text in the text area.

We want to display the values in the web service response in the OutputDiv div; and we want to display some of the raw information in the response in the NewTextDiv div.

Below is the screen after clicking the [Check Spelling] button

scjs02-PageAfterClick

Fig. 2

We need a reference to the outputDiv, so we can easily write to it.

Listing 2:

var outputDiv = document.getElementById("OutputDiv");
  

Next, we bind code to the button's click event, as shown in Listing 3.

Listing 3:

var spellCheckButton = document.getElementById("SpellCheckButton"); 
spellCheckButton.onclick = function () { 
    // Replace this with your Spell Check API key from Aure 
    var subscriptionKey = "xxxxxxxxxxxxxxxxxxxxxxxx"; 

    outputDiv.innerHTML = "Thinking...";

    var textToCheck = document.getElementById("TextToCheck").textContent; 
    var webSvcUrl = "https://api.cognitive.microsoft.com/bing/v7.0/spellcheck/?text=" + textToCheck; 
    webSvcUrl = webSvcUrl + "&mode=proof&mkt=en-US";

    var httpReq = new XMLHttpRequest(); 
    httpReq.open("GET", webSvcUrl, true); 
    httpReq.setRequestHeader("Ocp-Apim-Subscription-Key", subscriptionKey) 
    httpReq.setRequestHeader("contentType", "application/json") 
    httpReq.onload = onSpellCheckSuccess; 
    httpReq.onerror = onSpellCheckError; 
    httpReq.send(null); 
};
  

This code gets the text from the text area and makes an asynchronous HTTP GET request to the Spell Check API, passing the API key in the header. When the API sends a response, this will call the onSpellCheckSuccess or onSpellCheckError function, depending on the success of the call.

Listing 4 shows the onSpellCheckSuccess function:

Listing 4:

function onSpellCheckSuccess(evt) { 
    var req = evt.srcElement; 
    var resp = req.response; 
    var data = JSON.parse(resp);

    var flaggedTokens = data.flaggedTokens; 
    if (data.flaggedTokens.length > 0) { 
        var newText = document.getElementById("TextToCheck").textContent; 
        ; 
        var outputHtml = ""; 
         flaggedTokens.forEach(flaggedToken => { 
            var token = flaggedToken.token; 
            var tokenType = flaggedToken.type; 
            var offset = flaggedToken.offset; 
            var suggestions = flaggedToken.suggestions; 
            outputHtml += "<div>" 
            outputHtml += "<h3>Token: " + token + "</h3>"; 
            outputHtml += "Type: " + tokenType + "<br/>"; 
            outputHtml += "Offset: " + offset + "<br/>"; 
             outputHtml += "<div>Suggestions</div>"; 
            outputHtml += "<ul>";

            if (suggestions.length > 0) { 
                 suggestions.forEach(suggestion => { 
                     outputHtml += "<li>" + suggestion.suggestion; 
                     outputHtml += " (" + (suggestion.score * 100).toFixed(2) + "%)" 
                }); 
                outputHtml += "</ul>"; 
                outputHtml += "</div>";

                newText = replaceTokenWithSuggestion(newText, token, offset, suggestions[0].suggestion) 
            } 
            else { 
                 outputHtml += "<ul><li>No suggestions for this token</ul>"; 
            } 
        });

        newText = "<h2>New Text:</h2>" + newText; 
        var newTextDiv = document.getElementById("NewTextDiv"); 
        newTextDiv.innerHTML = newText;

        outputHtml = "<h2>Details</h2>" + outputHtml; 
        outputDiv.innerHTML = outputHtml;

    } 
    else { 
        outputDiv.innerHTML = "No errors found."; 
    } 
};
  

As you can see, we parse out the JSON object from the response and retrieve each flaggedToken from that object. For each flaggedToken, we output information, such as the original text (or token), the tokenType, and suggested replacements, along with the score of each replacement.

If an error occurs when calling the API service, the onSpellCheckError function is called, as shown in Listing 5.

Listing 5:

function onSpellCheckError(evt) { 
    outputDiv.innerHTML = "An error has occurred!!!"; 
};
  

Finally, we replace each token with the first suggestion, using the code in Listing 6.

Listing 6*:

function replaceTokenWithSuggestion(originalString, oldToken, offset, newWord) { 
    var textBeforeToken = originalString.substring(0, offset);

    var textAfterToken = ""; 
    if (originalString.length > textBeforeToken.length + oldToken.length) { 
        textAfterToken = originalString.substring(offset + oldToken.length, originalString.length); 
    }

    var newString = textBeforeToken + newWord + textAfterToken;

    return newString; 
 }
  

Here is the full JavaScript:

Listing 7:

window.onload = function () {

    var outputDiv = document.getElementById("OutputDiv");
    // var subscriptionKey = getKey();

    var spellCheckButton = document.getElementById("SpellCheckButton");
    spellCheckButton.onclick = function () {
        var subscriptionKey = getKey();
        var textToCheck = document.getElementById("TextToCheck").textContent;

        var webSvcUrl = "https://api.cognitive.microsoft.com/bing/v7.0/spellcheck/?text=" + textToCheck;
        webSvcUrl = webSvcUrl + "&mode=proof&mkt=en-US";

        outputDiv.innerHTML = "Thinking...";

        var httpReq = new XMLHttpRequest();
        httpReq.open("GET", webSvcUrl, true);
        httpReq.setRequestHeader("Ocp-Apim-Subscription-Key", subscriptionKey)
        httpReq.setRequestHeader("contentType", "application/json")
        httpReq.onload = onSpellCheckSuccess;
        httpReq.onerror = onSpellCheckError;
        httpReq.send(null);
    };

    function onSpellCheckSuccess(evt) {
        var req = evt.srcElement;
        var resp = req.response;
        var data = JSON.parse(resp);

        var flaggedTokens = data.flaggedTokens;
        if (data.flaggedTokens.length > 0) {
            var newText = document.getElementById("TextToCheck").textContent;
            ;
            var outputHtml = "";
            flaggedTokens.forEach(flaggedToken => {
                var token = flaggedToken.token;
                var tokenType = flaggedToken.type;
                var offset = flaggedToken.offset;
                var suggestions = flaggedToken.suggestions;
                outputHtml += "<div>"
                outputHtml += "<h3>Token: " + token + "</h3>";
                outputHtml += "Type: " + tokenType + "<br/>";
                outputHtml += "Offset: " + offset + "<br/>";
                outputHtml += "<div>Suggestions</div>";
                outputHtml += "<ul>";

                if (suggestions.length > 0) {
                    suggestions.forEach(suggestion => {
                        outputHtml += "<li>" + suggestion.suggestion;
                        outputHtml += " (" + (suggestion.score * 100).toFixed(2) + "%)" 
                    });
                    outputHtml += "</ul>";
                    outputHtml += "</div>";

                    newText = replaceTokenWithSuggestion(newText, token, offset, suggestions[0].suggestion)
                }
                else {
                    outputHtml += "<ul><li>No suggestions for this token</ul>";
                }
            });

            newText = "<h2>New Text:</h2>" + newText;
            var newTextDiv = document.getElementById("NewTextDiv");
            newTextDiv.innerHTML = newText;

            outputHtml = "<h2>Details</h2>" + outputHtml;
            outputDiv.innerHTML = outputHtml;

        }
        else {
            outputDiv.innerHTML = "No errors found.";
        }
    };

    function onSpellCheckError(evt) {
        outputDiv.innerHTML = "An error has occurred!!!";
    };

    function replaceTokenWithSuggestion(originalString, oldToken, offset, newWord) {
        var textBeforeToken = originalString.substring(0, offset);

        var textAfterToken = "";
        if (originalString.length > textBeforeToken.length + oldToken.length) {
            textAfterToken = originalString.substring(offset + oldToken.length, originalString.length);
        }

        var newString = textBeforeToken + newWord + textAfterToken;

        return newString;
    }

};
  

Hopefully, this sample gives you an idea how to get started building your first app that uses the Bing Spell Check API.



* This code currently has a bug in it: It only works if each suggestion is the same length as the token it replaces. I plan to fix this bug, but I'm publishing now because:

  1. It is not a fatal bug and
  2. It is not relevant to the call to the API, which is the primary point I'm showing in this article.
Friday, August 16, 2019 9:00:00 AM (GMT Daylight Time, UTC+01:00)
# Thursday, August 15, 2019

GCast 61:

Text Recognition C# Demo

In this video, I walk you through a C# application that calls the Text Recognition service, passing in an image of text and retrieving that text.

Thursday, August 15, 2019 8:47:00 AM (GMT Daylight Time, UTC+01:00)
# Wednesday, August 14, 2019

In the last article, I showed how to create a Bing Spell Check service in Azure. Once you have created this service, you can now pass text to a web service to perform spell checking.

Given a text sample, the service checks the spelling of each token in the sample. A token is a word or two word that should be a single word, such as "arti cle", which is a misspelling of the word "article".

It returns an array of unrecognized tokens, along with suggested replacements for these misspelled tokens.

URL and querystring arguments

The URL for the web service is
https://api.cognitive.microsoft.com/bing/v7.0/spellcheck

You can add some optional querystring parameters to this URL:

mode
Set this to "proof" if you want to check for spelling, grammar, and punctuation errors
Set it to "spell" if you only want to check for spelling errors.

If you omit the "mode" querystring argument, it defaults to "proof".

mkt
Set this to the Market Code of the country/language/culture you want to test. This is in the format [Language Code]-[Country Code], such as "en-US" for United States English. A full list of Market Codes can be fond here.

The "Proof" mode supports only en-US,  es-ES, and pt-BR Market Codes.

If you omit the mkt argument, the service will guess the market based on the text. Therefore, it is a good idea to include this value, even though it is optional.

Below is an example of a URL with some querystring values set.

https://api.cognitive.microsoft.com/bing/v7.0/spellcheck?mode=proof&mkt=en-us

POST vs GET

You have the option to submit either an HTTP POST or an HTTP GET request to the URL. We will discuss the differences below.

If you use the GET verb, you pass the text to check in the querystring, as in the following example:

https://api.cognitive.microsoft.com/bing/v7.0/spellcheck?mode=proof&mkt=en-us&text=Life+ig+buuutifull+all+the+tyme

With the GET method, the text is limited to 1,500 characters

If you use the POST verb, the text is passed in the body of the request, as in the following example:

text=Life+ig+buuutifull+all+the+tyme

With the POST method, you can send text up to 10,000 characters long.

Results

If successful, the web service will return an HTTP 200 ("OK") response, along with the following data in JSON format in the body of the response:

_type: "SpellCheck"

An array of "flaggedTokens", representing spelling errors found

Each flaggedToken consists of the following information:

  • offset: The position of the offending token within the text
  • token: The token text
  • type: The reason this token is in this list (usually "UnknownToken")
  • suggestion: An array of suggested replacements for the offending token. Each suggestion consists of the following:
  • score: a value (0-1) indicating the likelihood that this suggestion is the appropriate replacement

Below is an example of a response:

{
   "_type": "SpellCheck",
   "flaggedTokens": [{
     "offset": 5,
     "token": "ig",
     "type": "UnknownToken",
     "suggestions": [{
       "suggestion": "is",
       "score": 0.8922398888897022
     }]
   }, {
     "offset": 8,
     "token": "buuutifull",
     "type": "UnknownToken",
     "suggestions": [{
       "suggestion": "beautiful",
       "score": 0.8922398888897022
     }]
   }, {
     "offset": 27,
     "token": "tyme",
     "type": "UnknownToken",
     "suggestions": [{
       "suggestion": "time",
       "score": 0.8922398888897022
     }]
   }]
 }
  

In this article, I showed how to call the Bing Spell Check service with either a GET or POST HTTP request.

Wednesday, August 14, 2019 8:53:00 AM (GMT Daylight Time, UTC+01:00)

The Bing Spell Check API allows you to call a simple web service to perform spell checking on your text.

Before you get started, you must log into a Microsoft Azure account and create a new Bing Spell Check Service. Here are the steps to do this:

In the Azure Portal, click the [Create a resource] button (Fig. 1); then, search for and select "Bing Spell Check", as shown in Fig. 2.

sc01-CreateResourceButton
Fig. 1

sc02-SearchForBingSpellCheck
Fig. 2

The "Bing Spell Check" (currently on version 7) page displays, which describes the service and provides links to documentation and information about the service, as shown in Fig. 3

sc03-BingSpellCheckLandingPage
Fig. 3

Click the [Create] button to open the "Create" blade, as shown in Fig. 4.

sc04-CreateSpellCheckBlade
Fig. 4

At the "Name" field, enter a unique name for your service.

At the "Subscription" dropdown, select the subscription in which to create the service. Most of you will have only one subscription.

At the "Pricing Tier" dropdown, select the free or paid tier, as shown in Fig. 5.

sc05-PricingTiers
Fig. 5

The number of calls are severely limited for the free tier, so this is most useful for testing and learning the service. You may only create one free Spell Check service per subscription.

At the "Resource Group" field, select a resource group to associate with this service or click the "Create new" link to associate it with a newly-created resource group. A resource group provides a way to group together related service, making it easier to manage them together.

Click the [Create] button to begin creating the service. This process takes only a few seconds.

Open the service and select the "Keys" blade, as shown in Fig. 6.

sc06-KeysBlade
Fig. 6

Either one of the keys listed on this page must be passed in the header of your web service call.

Save a copy of one of these keys. You will need it when I show you how to call the Bing Spell Check Service in tomorrow’s article.

Wednesday, August 14, 2019 1:46:16 AM (GMT Daylight Time, UTC+01:00)
# Monday, August 12, 2019

Episode 575

Kevin Gates on Cloud Architecture

Cloud Solution Architect Kevin Gates walks us through the architecture of a sample application migrated from on-premise to Azure.

http://www.dreaddontdie.com/

Monday, August 12, 2019 9:17:00 AM (GMT Daylight Time, UTC+01:00)
# Sunday, August 11, 2019

DeathComesForTheArchbishopDeath Comes for the Archbishop by Willa Cather follows the life of Bishop Jean Marie Latour, appointed to lead the new diocese of New Mexico, when the region is annexed by the United States, following the Mexican-American War.

Latour travels to New Mexico from his home in Ohio with his friend and colleague Joseph Vaillant. They encounter many challenges: the rough environment; the lack of roads; a clash of cultures; widespread poverty; murderers who prey on travelers; rogue priests; and those who refuse to recognize the transition of authority to the new archbishop.

In addition to the story of Latour, we hear other tales of the place and time where this novel takes place. One memorable story was of a drunken, ill-tempered priest, who killed a young Indian servant when the boy spilled some food on him. The priest was captured and killed by the locals a few days later.

The story is loosely best on the lives of Jean-Baptiste Lamy and Joseph Projectus Machebeuf, who served as Catholic Bishops in the southwest US during the mid-19th century.

Cather is at her best when describing the New Mexico sky and landscape. She tells a straightforward story without much fanfare and she does a good job of contrasting the personalities of the two missionaries: the stoic Latour and the outgoing Vaillant. Each man loves God and believes in their mission but tackles it in his own way.

Like most biographies, Death Comes for the Archbishop covers the main character’s life to the end. So, while the title comes from the last chapter, it is a bit misleading. The book is far more about the Archbishop's life than about his death. As Latour himself put it: "I shall not die of a cold, my son. I shall die of having lived."

Sunday, August 11, 2019 8:18:00 AM (GMT Daylight Time, UTC+01:00)
# Saturday, August 10, 2019

TropicOfCancerI don't really know how to take Tropic of Cancer by Henry Miller.

The novel is written in the first person and the narrator has the same name as the author. So, am I to believe that Mr. Miller was a sex addict and all his friends were sex addicts and misogynists?

The story follows Miller and his friends - mostly bohemian American expatriates - as they navigate the squalid neighborhoods of Paris searching for sexual partners, artistic fulfillment, and survival. Miller tries to embrace the pleasures of life, sleeping with a variety of prostitutes and other women; but it is a challenge. He doesn't know where his next meal is coming from; but he is still focused on finding his next lay. His wife recently left for New York and he wonders why she has not communicated with him in the months since her departure. He expects her return, but it seems unlikely she will.

Still, it's difficult to feel sorry for Miller and his friends, as their descriptions are littered with misogyny. Virtually, every woman in the novel is referred to as a "c*nt" and most exist only as sexual outlets for Miller and his friends. In one scene, Miller steals his money back from a prostitute when she leaves the room after they have sex.

Many will dismiss this novel because of its strong sexual content. The writing is salacious and often shocking. Many men spend their youths focused on sex; and a few, like Miller, extend this obsession into middle age and this is his and their story.

Although very sexual, I cannot call this writing erotic. At no point did I find myself aroused by the exploits of Miller and his friends.

The novel has an important place in the history of literature. Its explicit sexuality pushed a lot of boundaries when it was first published in 1934. And it was banned as pornographic in the United States for 3 decades, until a 1961 obscenity trial that escalated to the Supreme Court. It is also significant culturally. One can hear Miller's strong influence on the beat writers of the 1950s and 1960s - from his stream-of-consciousness prose to his rejection of society's norms to his casual and frank discussion of alcohol and drug abuse. 

And this book is important for the prose that Miller brings to his writing. Here is how he describes his adopted city:

"Paris is like a whore. From a distance she seems ravishing, you can’t wait until you have her in your arms. And five minutes later you feel empty, disgusted with yourself. You feel tricked."

Tropic of Cancer is worth reading for its influence on literature and for its celebration of the joys of living.

Saturday, August 10, 2019 9:42:00 AM (GMT Daylight Time, UTC+01:00)
# Thursday, August 8, 2019

GCast 60:

Text Recognition Cognitive Service with Binary Images

The Text Recognition Service supports sending a binary image and reading any text in that image. This video shows you how.

Thursday, August 8, 2019 1:24:10 PM (GMT Daylight Time, UTC+01:00)