Friday, March 17, 2017

Twitter from C# .NET MVC..

 I wrote the Stock tracker application as mostly just a learning drill for Angular 1 and 2.
But I have found, it could be actually useful, with a touch of Twitter.

I find myself searching Twitter for charting,analysis, links, and just plain prognosticating by people, by searching the stock symbol preceded by "$".

So I set out to add some Twitter links to my site today.
There was a ton of helpful documentation by others already on the internet. Problem was the format and varying usage needs. I really needed to get the grunt work done in C# .NET. Some of the Gurus are using Node.js and other things, and some others were just making it harder than it needed for me to be.

What I wanted to end up with, and now have looks like this :




So the user can click the bird and see what has been tweeted about a company stock.

I would offer up that the heavy lifting is done by getting an OAuth token from Twitter first, and post that code for reference sake :

public string GetAuthToken()
        {

            string encodedKeyAndSecret = Convert.ToBase64String(
                new System.Text.UTF8Encoding().GetBytes(
                  ConsumerKey + ":" + ConsumerSecret));

            string urlToken = "https://api.twitter.com/oauth2/token";

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(urlToken));

            req.Headers.Add("Authorization","Basic " + encodedKeyAndSecret);
              
            req.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
            req.Method = "POST";

            string requestBody = "grant_type=client_credentials";

            Stream dataStream = req.GetRequestStream();

            byte[] byteArray = new System.Text.UTF8Encoding().GetBytes(requestBody);
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            WebResponse response = req.GetResponse();

            string jsonResponse = "";

            using (var reader = new StreamReader(response.GetResponseStream()))
            {
               jsonResponse = reader.ReadToEnd();
            }

            TokenResponse t = 
                Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse);
      
            return t.access_token;

        }

The rest of the act of querying Twitter is super easy to code, but can take some to get working, deserializing to something useable in your app, and then modeled by your classes and client-side objects.


Tuesday, March 07, 2017

ASP .NET MVC Angular 2, what I have so far..

So I set out to upgrade the first project of this kind which was written using Angular 1. My first few attempts with downloadable samples, were of the new MVC "wwwroot" Core format. These either did not run for me, or I could not see how to learn what I needed to learn using them.

So I had to start humble and create a traditional ASP .NET project and add MVC functionality to it. I figured this would be a good way to be able to download and learn from the quickstart at :

https://angular.io/docs/ts/latest/cookbook/visual-studio-2015.html

I got some help from the internet with adding MVC "areas" to my project, so as to add MVC functionality, but not lose the traditional ASP application behaviors.

I don't remember where exactly I picked up the "AREAS" education, but the topic is heavily explained :
https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=how+to+add+MVC+areas&*

Jumping to the end, my new stock tracker application runs at :
https://www.customconfiguration.net/ng/Stocks/StocksAngular


New users would want to be logged into Twitter for easy signup, or create a new account to use the app.

Forgiving my choice of traditional (Non-WebApi) controllers for a second, it only seems useful to look at a couple of sample methods :

   
     [Authorize()]
        [AcceptVerbs(HttpVerbs.Get)]
        public JsonResult ReadStockQuote(string Id)
        {
            Quote q;

            if (Id.Length > 0)
            {
                q = getQuoteFromYahoo(Id);
            }
            else
            {
                q = new Quote();
            }

            return Json(q, JsonRequestBehavior.AllowGet);
        }


        [Authorize()]
        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(Duration = 360, VaryByParam = "Id")]
        public JsonResult ReadPrice(string Id)
        {
            string lastTrade = string.Empty;
            string color = string.Empty;
            string error = string.Empty;

            try
            {
                Quote q = getQuoteFromYahoo(Id);

                lastTrade = "$" + q.LastTrade;
                if (q.PercentChange.Contains("-"))
                {
                    color = "Red";
                }
                else
                {
                    color = "Green";
                }

            }
            catch (System.Exception exception)
            {
                lastTrade = "error";
                error = exception.Message;
            }

            return Json(
             new Stock()
             {
                 Price = lastTrade,
                 Color = color,
                 //                Message = error,
                 Symbol = Id
             },
             JsonRequestBehavior.AllowGet);
        }

So the code for the controllers just does basic Add, Delete, Get, And some RSS feed reading from Yahoo's generous web services.

Following the example provided at the Angular website, I created and "app" folder with "components" and "services" folders included> Everything prescribed in the tutorial, I copied into my project unchanged.

I then built the app I wanted following the example where relevant. I jumped around to get ahead to Routing, because I really wanted that feature even though my small app could certainly have existed in one view.

My main view for the single page app, just has one selector tag, along with the referenced scripts in the head tag :
 <base href="@Url.Content("~")">

 @Styles.Render("~/Content/css")

         <!-- Polyfill(s) for older browsers -->
         <script src="~/node_modules/core-js/client/shim.min.js"></script>
             <script src="~/node_modules/zone.js/dist/zone.js"></script>
 <script src="~/node_modules/systemjs/dist/system.src.js"></script>
 <script src="~/systemjs.config.js"></script>
 <script>

 System.
import('main.js')
.
catch(function (err) { console.
error(err); });

</script>



<stocks>Loading AppComponent content here </stocks>



The app Module script file has been updated from the example with my components :

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule, JsonpModule } from '@angular/http';
import { Http } from '@angular/http';
import { Router } from '@angular/router';

import { AppServiceStocks } from './services/app.service.stocks';
import { AppStocks } from './components/app.component.stocks';
import { AddComponent } from './components/app.component.stockadd';
import { RemComponent } from './components/app.component.stockrem';
import { NewsComponent } from './components/app.component.stocknews';
import { AppComponent } from './components/app.component';

import { PageNotFoundComponent } from './components/not-found.component';
import { AppRoutingModule } from './app-routing';

@NgModule({
    imports: [BrowserModule, HttpModule, FormsModule, AppRoutingModule],
    declarations: [
        AppComponent,
        AppStocks,
        AddComponent,
        PageNotFoundComponent,
        RemComponent,
        NewsComponent],
    bootstrap: [AppComponent]
})
export class AppModule {

    // Diagnostic only: inspect router configuration
    constructor(router: Router) {
       // console.log('Routes: ', JSON.stringify(router.config, undefined, 2));
    }
}



The app Routing script file also was built from tutorial and just fleshed out with my own paths :


import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AppComponent } from './components/app.component';
import { AppStocks } from './components/app.component.stocks';
import { AddComponent } from './components/app.component.stockadd';
import { RemComponent } from './components/app.component.stockrem';
import { NewsComponent } from './components/app.component.stocknews';

import { PageNotFoundComponent } from './components/not-found.component';

// import { CanDeactivateGuard } from './can-deactivate-guard.service';
// import { AuthGuard } from './auth-guard.service';

import { SelectivePreloadingStrategy } from './components/selective-preloading-strategy';

const appRoutes: Routes = [
    {
        path: 'Stocks/StocksAngular/Add',
        component: AddComponent
    },
    {
        path: 'Stocks/StocksAngular/Delete/:id',
        component: RemComponent
    },
    {
        path: 'Stocks/StocksAngular/News/:id',
        component: NewsComponent
    },
    {
        path: 'Stocks/StocksAngular',
        component: AppStocks
    },    
    { path: '', redirectTo: 'Stocks/StocksAngular', pathMatch: 'full' },
    { path: '**', component: PageNotFoundComponent }
];

@NgModule({
    imports: [
        RouterModule.forRoot(
            appRoutes,
            { preloadingStrategy: SelectivePreloadingStrategy }
        )
    ],
    exports: [
        RouterModule
    ],
    providers: [
   //     CanDeactivateGuard,
        SelectivePreloadingStrategy
    ]
})
export class AppRoutingModule { }


It is important to remember to not allow MVC to interfere with Angular's routing :
In the RouteConfig file place something like -

  routes.MapRoute(
              name: "ngOverride",
              url: "Stocks/StocksAngular/{*.}",
              defaults: new { controller = "Stocks", action = "StocksAngular" }
            );


The app service script file is called app.service.stocks.js :

import { Injectable } from '@angular/core';
import { Http, Response, URLSearchParams, RequestOptions, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import { stock } from '../components/stock';
import { rssitem } from '../components/rssitem';
import { quote } from '../components/quote';

@Injectable()
export class AppServiceStocks {

    private _getStocksListUrl = 'Stocks/StocksJSON';
    private _getStockDetailUrl = "Stocks/ReadStockQuote?Id=";
    private _getPriceUrl = "Stocks/ReadPrice?Id=";
    private _getNameUrl = "Stocks/GetNameFromSymbol?Id=";
    private _getNewsUrl = "http://feeds.finance.yahoo.com/rss/2.0/headline?s=";

    private _deleteStockUrl = "Stocks/Remove";
    private _addStockUrl = "Stocks/AddJSON";

    private _stockslist: stock[];

    constructor(private http: Http) {
    }

    stockslist(): Observable {
        return this.http.get(this._getStocksListUrl)
            .map(this.extractData)
            .catch(this.handleError);
    }

    stockDetail(symbol : string): Observable {
        return this.http.get(this._getStockDetailUrl + symbol)
            .map(this.extractData)
            .catch(this.handleError);
    }

    getNameFromSymbol(symbol: string): Observable {
        return this.http.get(this._getNameUrl + symbol)
            .map(this.extractData)
            .catch(this.handleError);
    }

    readPrice(symbol: string): Observable {
        return this.http.get(this._getPriceUrl + symbol)
            .map(this.extractData)
            .catch(this.handleError);
    }

    readNews(symbol: string): Observable {
        var newsLink = this._getNewsUrl
            + symbol
            + "&region=US&lang=en-US";

        let params: URLSearchParams = new URLSearchParams();
        params.set('Link', newsLink);
    
        return this.http.get("Stocks/ReadNewsData?",
            { search: params })
            .map(this.extractData)
            .catch(this.handleError);
    }

    readYahooNews(symbol: string): Observable {
       
        let params: URLSearchParams = new URLSearchParams();
        params.set('Symbol', symbol);

        return this.http.get("Stocks/ReadYahooNewsData",
            { search: params })
            .map(this.extractData)
            .catch(this.handleError);
    }

    remove(s : stock) : Observable {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this.http.post(this._deleteStockUrl, { item : s }, options)
            .map(this.extractData)
            .catch(this.handleError);
    }

    add(s: string): Observable {
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });

        return this.http.post(this._addStockUrl, { Symbol : s }, options)
            .map(this.extractData)
            .catch(this.handleError);
    }

    private handleError(error: Response | any) {
        // In a real world app, we might use a remote logging infrastructure
        let errMsg: string;
        if (error instanceof Response) {
            const body = error.json() || '';
            const err = body.error || JSON.stringify(body);
            errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
        } else {
            errMsg = error.message ? error.message : error.toString();
        }
        console.error(errMsg);
        return Observable.throw(errMsg);
    }

    private extractData(res: Response) {
        let body = res.json();
        return body || {};
    }
The main controller, the Stocks list controller, which rotates down the list getting the current prices from Yahoo : I think this came out cleaner to code than the Angular 1 version of it.
import { Component } from '@angular/core';
import { stock } from './stock';
import { quote } from './quote';
import { Observable } from 'rxjs/Observable';
import { AppServiceStocks } from '../services/app.service.stocks';

@Component({
    selector: 'stocks',
    templateUrl: './app/components/app.component.stocks.html?v=4',
    providers: [AppServiceStocks]
})

export class AppStocks {

    name = 'Angular Stocks';
    stockslist: stock[];
    mode = 'Observable';
    statusMessage = "";
    newStockName = "";
    newStockSym = "";
    newStock = new stock();
    rownum = 0;
    interval = 2;
    
    constructor(private _appService: AppServiceStocks) {
        
    }

    ngOnInit() {
        this.newStockName = "";
        this.getStocks();
    }

    private readPrice() {
        this._appService.readPrice(this.stockslist[this.rownum].Symbol)
            .subscribe(result => {
                this.stockslist[this.rownum].Price = result.Price;
                this.stockslist[this.rownum].Color = result.Color;
                this.rownum++;
                if (this.rownum == this.stockslist.length) {
                    this.rownum = 0;
                }
                this.getNextQuote();
            });
    }

    private getNextQuote() {
      setTimeout(() => { this.readPrice() }, this.interval * 1000);
    }

    getStocks() {
        this._appService.stockslist()
            .subscribe(
            stocks =>
            {
                this.stockslist = stocks;
                this.getNextQuote();
            });
    }

    

}