quarta-feira, 20 de setembro de 2017

Dynamics 365 Web Api - CRM REST Builder

Uma excelente ferramenta construtora de chamadas de Web Api está disponível no Github, chamada CRM REST Builder!

Com ela podemos acelerar e muito a construção das nossas queries, quais atributos usar (se são case sensitive, por exemplo), quais as actions e functions disponíveis, etc...

Baixe agora mesmo a [Solution] e importe no seu CRM - CRMRESTBuilder Solution - RELEASE


domingo, 10 de setembro de 2017

Um Tutorial para sua Primeira Aplicação AngularJS

O que é AngularJS?

AngularJS é um framework JavaScript MVC desenvolvido pelo Google, que permite que você construa aplicações front-end bem estruturadas, fácil de verificar e manter.

Por que eu deveria usá-lo?

Se ainda não experimentou o AngularJS, está perdendo. A estrutura consiste em um conjunto de ferramentas bem integradas que o ajudará a criar aplicativos bem estruturados e ricos do lado do cliente de forma modular, com menos código e mais flexibilidade.

O AngularJS amplia o HTML fornecendo diretrizes que adicionam funcionalidades à sua marcação e permitem que você crie modelos dinâmicos poderosos. Você também pode criar suas próprias diretrizes, criando componentes reutilizáveis ​​que preencham suas necessidades e abstraindo toda a lógica de manipulação de DOM.

Ele também implementa vinculação de dados bidirecionais, conectando seu HTML (visualizações) aos seus objetos JavaScript (modelos) de forma transparente. Em termos simples, isso significa que qualquer atualização em seu modelo será imediatamente refletida em sua visão sem a necessidade de manipulação de DOM ou manipulação de eventos (por exemplo, com jQuery).

Finalmente, adoro Angular devido à sua flexibilidade em relação à comunicação do servidor. Como a maioria dos frameworks MVC do JavaScript, ele permite que você trabalhe com qualquer tecnologia do lado do servidor, desde que possa atender seu aplicativo através de uma API da Web RESTful. Mas Angular também fornece serviços no topo do XHR que simplificam dramaticamente o seu código e permitem que você abstraia as chamadas da API em serviços reutilizáveis. Como resultado, você pode mover sua lógica de modelo e de negócios para o front-end e criar aplicativos web agnósticos back-end. Nesta publicação, faremos exatamente isso, um passo de cada vez.

Então, onde eu começo?

Primeiro, vamos decidir a natureza do aplicativo que queremos construir. Neste guia, preferimos não gastar muito tempo no back-end, então vamos escrever algo com base em dados facilmente acessíveis na Internet - como um aplicativo de alimentação esportiva!

Como eu sou um grande fã de automobilismo e Fórmula 1, usarei um serviço de API de autosport para atuar como nosso back-end. Felizmente, os caras da Ergast são gentis o suficiente para fornecer uma API gratuita de automobilismo que será perfeita para nós.

Para um pico furtivo no que vamos construir, dê uma olhada na demo ao vivo. Para embelezar a demonstração e mostrar alguns modelos angulares, apliquei um tema Bootstrap de WrapBootstrap, mas vendo que este artigo não é sobre CSS, vou resumi-lo dos exemplos e deixá-lo fora.

Tutorial de início

Vamos começar o nosso aplicativo de exemplo. Eu recomendo o projeto de sementes angulares, pois não só fornece um ótimo esqueleto para bootstrapping, mas também define o solo para teste de unidade com Karma e Jasmine (não vamos fazer nenhum teste nesta demo, então vamos apenas Deixar as coisas de lado por enquanto, consulte a Parte 2 deste tutorial para obter mais informações sobre a configuração do seu projeto para testes de unidade e de ponta a ponta).

EDIT (maio de 2014): desde que escrevi este tutorial, o projeto de sementes angulares passou por algumas mudanças pesadas (incluindo a adição de Bower como gerenciador de pacotes). Se você tiver dúvidas sobre como implantar o projeto, veja rapidamente a primeira seção de seu guia de referência. Na parte 2 do tutorial, o Bower , entre outras ferramentas, é coberto com maior detalhe.

OK, agora que clonamos o repositório e instalamos as dependências, o esqueleto do nosso aplicativo ficará assim:



Agora podemos começar a codificar. Como estamos tentando construir uma alimentação esportiva para um campeonato de corrida, vamos começar com a visão mais relevante: a tabela do campeonato.



Dado que já temos uma lista de drivers definida no nosso escopo (fique comigo - vamos chegar lá) e ignorando qualquer CSS (para legibilidade), nosso HTML pode parecer:

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
</body>


A primeira coisa que você notará neste modelo é o uso de expressões ("{{" e "}}") para retornar valores variáveis. Em AngularJS, as expressões permitem que você execute alguma computação para retornar o valor desejado. Algumas expressões válidas seriam:

{{ 1 + 1 }}
{{ 946757880 | date }}
{{ user.name }}


Efetivamente, as expressões são snippets com JavaScript. Mas apesar de ser muito poderoso, você não deve usar expressões para implementar qualquer lógica de nível superior. Para isso, usamos diretrizes.

Compreender as Diretivas Básicas

A segunda coisa que você notará é a presença de ng-attributes, que você não veria na marcação típica. São diretrizes.

Em um nível alto, as diretivas são marcadores (como atributos, tags e nomes de classes) que indicam ao AngularJS para anexar um determinado comportamento a um elemento DOM (ou transformá-lo, substituí-lo, etc.). Vamos dar uma olhada nos que já vimos:

A ng-app diretiva é responsável por iniciar seu aplicativo definindo seu escopo. No AngularJS, você pode ter vários aplicativos na mesma página, então esta diretiva define onde cada aplicativo distinto começa e termina.
A ng-controller diretiva define qual controlador será responsável por sua visão. Nesse caso, denotamos o driversController, que fornecerá nossa lista de drivers ( driversList).
A ng-repeat diretriz é uma das mais usadas e serve para definir seu escopo de modelo ao fazer um loop pelas coleções. No exemplo acima, ele replica uma linha na tabela para cada driver em driversList.

Adicionando controladores

Claro, não há nenhum uso para nossa visão sem um controlador. Vamos adicionar driversController aos nossos controladores.js:

angular.module('F1FeederApp.controllers', []).
controller('driversController', function($scope) {
    $scope.driversList = [
      {
          Driver: {
              givenName: 'Sebastian',
              familyName: 'Vettel'
          },
          points: 322,
          nationality: "German",
          Constructors: [
              {name: "Red Bull"}
          ]
      },
      {
          Driver: {
          givenName: 'Fernando',
              familyName: 'Alonso'
          },
          points: 207,
          nationality: "Spanish",
          Constructors: [
              {name: "Ferrari"}
          ]
      }
    ];
});


Você pode ter notado a $scope variável que estamos passando como um parâmetro para o controlador. A $scope variável deve ligar o controlador e as visualizações. Em particular, ele contém todos os dados que serão usados ​​em seu modelo. Qualquer coisa que você adicionar a ele (como driversList no exemplo acima) será diretamente acessível em suas visualizações. Por enquanto, vamos trabalhar com uma matriz de dados estática, que nós vamos substituir mais tarde com nosso serviço API.

Agora, adicione isso ao app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers'
]);


Com esta linha de código, nós realmente inicializamos nosso aplicativo e registramos os módulos nos quais ele depende. Voltaremos a esse arquivo (app.js) mais tarde.

Agora, vamos colocar tudo em index.html:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp" ng-controller="driversController">
  <table>
    <thead>
      <tr><th colspan="4">Drivers Championship Standings</th></tr>
    </thead>
    <tbody>
      <tr ng-repeat="driver in driversList">
        <td>{{$index + 1}}</td>
        <td>
          <img src="img/flags/{{driver.Driver.nationality}}.png" />
          {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
        </td>
        <td>{{driver.Constructors[0].name}}</td>
        <td>{{driver.points}}</td>
      </tr>
    </tbody>
  </table>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>


Pequenos módulos, menores erros; agora você pode inicializar seu aplicativo e verificar sua lista (estática) de drivers.

Carregando dados do servidor

Como já sabemos exibir os dados do nosso controlador em nossa visão, é hora de buscar dados em tempo real de um servidor RESTful.

Para facilitar a comunicação com servidores HTTP, o AngularJS fornece os serviços $http e $resource. O primeiro é apenas uma camada em cima de XMLHttpRequest ou JSONP, enquanto o último fornece um nível de abstração mais alto. Nós usaremos $http.

Para abstrair as chamadas da API do servidor do controlador, vamos criar nosso próprio serviço personalizado que irá buscar nossos dados e atuar como um wrapper ao redor $http, adicionando isso ao nosso services.js:

angular.module('F1FeederApp.services', []).
  factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });


Com as duas primeiras linhas, criamos um novo módulo (F1FeederApp.services) e registramos um serviço dentro desse módulo (ergastAPIservice). Observe que passamos $http como um parâmetro para esse serviço. Isso diz ao mecanismo de injeção de dependência da Angular que nosso novo serviço requer (ou depende) do serviço $http.

De forma semelhante, precisamos dizer ao Angular que inclua nosso novo módulo em nosso aplicativo. Vamos registrar com app.js, substituindo nosso código existente por:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers',
  'F1FeederApp.services'
]);


Agora, tudo o que precisamos fazer é ajustar nosso controller.js um pouco, incluir ergastAPIservice como uma dependência e pronto:

angular.module('F1FeederApp.controllers', []).
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];

    ergastAPIservice.getDrivers().success(function (response) {
        //Dig into the responde to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  });


Agora recarregue o aplicativo e confira o resultado. Observe que não fizemos nenhuma alteração no nosso modelo, mas adicionamos uma nameFilter variável ao nosso escopo. Vamos colocar essa variável para usar.

Filtros

Ótimo! Temos um controlador funcional. Mas isso mostra apenas uma lista de drivers. Vamos adicionar algumas funcionalidades implementando uma simples entrada de pesquisa de texto que irá filtrar nossa lista. Vamos adicionar a seguinte linha ao nosso index.html, logo abaixo da tag:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>


Agora estamos fazendo uso da diretiva ng-model. Esta diretiva vincula nosso campo de texto à $scope.nameFilter variável e garante que seu valor esteja sempre atualizado com o valor de entrada. Agora, vamos visitar index.html mais uma vez e fazer um pequeno ajuste na linha que contém a ng-repeat:

<tr ng-repeat="driver in driversList | filter: nameFilter">


Esta linha informa ao ng-repeat que, antes de enviar os dados, a driversList matriz deve ser filtrada pelo valor armazenado nameFilter.

Neste ponto, a ligação de dados bidirecionais é iniciada: sempre que um valor é inserido no campo de busca, Angular imediatamente garante que o $scope.nameFilter que associamos é atualizado com o novo valor. Uma vez que a ligação funciona de ambas as formas, no momento em que o nameFilter valor é atualizado, a segunda diretiva associada a ela (ou seja, a ng-repeat) também obtém o novo valor e a visualização é atualizada imediatamente.

Recarregue o aplicativo e confira a barra de pesquisa.



Observe que este filtro procurará a palavra-chave em todos os atributos do modelo, incluindo aqueles que não estamos usando. Vamos dizer que nós só desejamos filtrar Driver.givenName e Driver.familyName: Primeiro, adicione a driversController, logo abaixo da $scope.driversList = []; linha:

$scope.searchFilter = function (driver) {
    var keyword = new RegExp($scope.nameFilter, 'i');
    return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName);
};


Agora, de volta index.html, atualizamos a linha que contém a diretiva ng-repeat:

<tr ng-repeat="driver in driversList | filter: searchFilter">


Recarregue o aplicativo mais uma vez e agora temos uma pesquisa por nome.

Rotas

Nosso próximo objetivo é criar uma página de detalhes do driver que nos permita clicar em cada driver e ver os detalhes de sua carreira.

Primeiro, vamos incluir o serviço $routeProvider (em app.js) que nos ajudará a lidar com essas variadas rotas de aplicação. Então, vamos adicionar duas dessas rotas: uma para a tabela do campeonato e outra para os detalhes do driver. Aqui está o nosso novo app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.services',
  'F1FeederApp.controllers',
  'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
  $routeProvider.
 when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}).
 when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}).
 otherwise({redirectTo: '/drivers'});
}]);


Com essa mudança, navegue para http://domain/#/drivers, carregue o driversController e procure a visão parcial para renderizar partials/drivers.html. Mas espere! Ainda não temos visualizações parciais, certo? Nós também precisamos criar esses.

Visões parciais

O AngularJS permitirá que você vincule suas rotas a controladores e visualizações específicos.

Mas primeiro, precisamos dizer a Angular para onde renderizar essas visualizações parciais. Para isso, usaremos a diretiva ng-view, modificando nossa index.html para refletir o seguinte:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp">
  <ng-view></ng-view>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>


Agora, sempre que navegarmos pelas rotas do nosso aplicativo, Angular irá carregar a visão associada e renderizá-la no lugar da tag. Tudo o que precisamos fazer é criar um arquivo chamado partials/drivers.html e colocar nossa tabela de campeonato HTML lá. Nós também usaremos essa chance de vincular o nome do driver com a rota de detalhes do driver:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>
<table>
<thead>
  <tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
  <tr ng-repeat="driver in driversList | filter: searchFilter">
    <td>{{$index + 1}}</td>
    <td>
      <img src="img/flags/{{driver.Driver.nationality}}.png" />
      <a href="#/drivers/{{driver.Driver.driverId}}">
    {{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
   </a>
 </td>
    <td>{{driver.Constructors[0].name}}</td>
    <td>{{driver.points}}</td>
  </tr>
</tbody>
</table>


Finalmente, vamos decidir o que queremos mostrar na página de detalhes. Que tal um resumo de todos os fatos relevantes sobre o piloto (por exemplo, nascimento, nacionalidade), juntamente com uma tabela contendo seus resultados recentes? Para fazer isso, adicionamos a services.js:

angular.module('F1FeederApp.services', [])
  .factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverDetails = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverRaces = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });


Desta vez, fornecemos a identificação do driver para o serviço para que possamos recuperar as informações relevantes apenas para um driver específico. Agora, modificamos controllers.js:

angular.module('F1FeederApp.controllers', []).

  /* Drivers controller */
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];
    $scope.searchFilter = function (driver) {
        var re = new RegExp($scope.nameFilter, 'i');
        return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);
    };

    ergastAPIservice.getDrivers().success(function (response) {
        //Digging into the response to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  }).

  /* Driver controller */
  controller('driverController', function($scope, $routeParams, ergastAPIservice) {
    $scope.id = $routeParams.id;
    $scope.races = [];
    $scope.driver = null;

    ergastAPIservice.getDriverDetails($scope.id).success(function (response) {
        $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; 
    });

    ergastAPIservice.getDriverRaces($scope.id).success(function (response) {
        $scope.races = response.MRData.RaceTable.Races; 
    }); 
  });


O importante para notar aqui é que acabamos de injetar o serviço $routeParams no controlador do driver. Este serviço nos permitirá acessar nossos parâmetros de URL (para o id, neste caso) usando $routeParams.id.

Agora que temos nossos dados no escopo, precisamos apenas da visão parcial restante. Vamos criar um arquivo chamado partials/driver.html e adicionar:

<section id="main">
  <a href="./#/drivers"><- Back to drivers list</a>
  <nav id="secondary" class="main-nav">
    <div class="driver-picture">
      <div class="avatar">
        <img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" />
        <img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/>
        {{driver.Driver.givenName}} {{driver.Driver.familyName}}
      </div>
    </div>
    <div class="driver-status">
      Country: {{driver.Driver.nationality}}   <br/>
      Team: {{driver.Constructors[0].name}}<br/>
      Birth: {{driver.Driver.dateOfBirth}}<br/>
      <a href="{{driver.Driver.url}}" target="_blank">Biography</a>
    </div>
  </nav>

  <div class="main-content">
    <table class="result-table">
      <thead>
        <tr><th colspan="5">Formula 1 2013 Results</th></tr>
      </thead>
      <tbody>
        <tr>
          <td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td>
        </tr>
        <tr ng-repeat="race in races">
          <td>{{race.round}}</td>
          <td><img  src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td>
          <td>{{race.Results[0].Constructor.name}}</td>
          <td>{{race.Results[0].grid}}</td>
          <td>{{race.Results[0].position}}</td>
        </tr>
      </tbody>
    </table>
  </div>

</section>


Note que estamos agora colocando a directiva ng-show em bom uso. Esta diretiva mostrará apenas o elemento HTML se a expressão fornecida for true (ou seja, nem false, nem null). Nesse caso, o avatar só será exibido uma vez que o objeto do driver tenha sido carregado no escopo pelo controlador.

Toques finais

Adicione um monte de CSS e renderize sua página. Você deve acabar com algo como isto:



Agora você está pronto para ativar seu aplicativo e certifique-se de que ambas as rotas funcionem conforme desejado. Você também pode adicionar um menu estático index.html para melhorar as capacidades de navegação do usuário. As possibilidades são infinitas.

EDIT (maio de 2014): recebi muitos pedidos para uma versão para download do código que criamos neste tutorial. Por isso, decidi liberá-lo AQUI (despojado de qualquer CSS). No entanto, eu realmente não recomendo baixá-lo, pois este guia contém todas as etapas que você precisa para construir o mesmo aplicativo com suas próprias mãos, o que será um exercício de aprendizagem muito mais útil e eficaz.

Conclusão

Neste ponto do tutorial, cobrimos tudo o que você precisaria para escrever um aplicativo simples (como um alimentador de Fórmula 1). Cada uma das páginas restantes da demonstração ao vivo (por exemplo, tabela de campeão do construtor, detalhes da equipe, calendário) compartilham a mesma estrutura e conceitos básicos que revisamos aqui.

Finalmente, tenha em mente que a Angular é uma estrutura muito poderosa e que mal conseguimos arranhar a superfície em termos de tudo o que tem para oferecer. Na Parte 2 deste tutorial, daremos exemplos de por que Angular se destaca entre seus frameworks MVC front-end: testabilidade. Analisaremos o processo de construção e execução de testes unitários com o Karma, conseguindo uma integração contínua com Yeomen, Grunt e Bower e outros pontos fortes desta fantástica estrutura de front-end.

POR RAONI BOAVENTURA - ENGENHEIRO JAVASCRIPT @ TOPTAL

quarta-feira, 30 de agosto de 2017

Dynamics 365 Web API - Batch Operation in C#

Uma dica rápida para quem precisar usar o recurso de Web API - Batch Operation no Dynamics 365, só que usando C#.

A vantagem deste tipo de operação é que podemos, em um único POST HTTP, enviar um LOTE de registros para serem, por exemplo, criados no CRM.

Abaixo segue trecho de código que desenvolvi para inserir dois Contatos (CTT1, CTT2) no Dynamics 365. O exemplo está inserido em um projeto do tipo ASP.NET MVC - WEB API.


        [HttpGet]
        public void CreateDyn365BatchContacts()
        {
            ConnectToCRM(null);
            Task.WaitAll(Task.Run(async () => await RunBatchAsync()));
        }



        public async Task RunBatchAsync()
        {
            await getWebAPIVersion();
            webApiURI = config.ServiceUrl + "api/data/" + getVersionedWebAPIPath() + "$batch";

            var batchId = Guid.NewGuid().ToString();
            var changeSetId = "BBB456";

            var contact1 = new JObject();
            var contact2 = new JObject();
            contact1.Add("firstname", "CTT");
            contact1.Add("lastname", "1");
            contact2.Add("firstname", "CTT");
            contact2.Add("lastname", "2");

            var batchContent = new MultipartContent("mixed", "batch_" + batchId);

            // CTT1
            var changeSetContent = new MultipartContent("mixed", "changeset_" + changeSetId);
            var httpRM1 = new HttpRequestMessage(HttpMethod.Post, config.ServiceUrl + "api/data/" + getVersionedWebAPIPath() + "contacts");
            httpRM1.Content = new StringContent(JsonConvert.SerializeObject(contact1), Encoding.UTF8, "application/json");
            var httpMC1 = new HttpMessageContent(httpRM1);
            httpMC1.Headers.Remove("Content-Type");
            httpMC1.Headers.Add("Content-Type", "application/http");
            httpMC1.Headers.Add("Content-Transfer-Encoding", "binary");
            httpMC1.Headers.Add("Content-ID", "1");
            httpMC1.Headers.Add("OData-MaxVersion", "4.0");
            httpMC1.Headers.Add("OData-Version", "4.0");
            changeSetContent.Add(httpMC1);

            // CTT2
            var httpRM2 = new HttpRequestMessage(HttpMethod.Post, config.ServiceUrl + "api/data/" + getVersionedWebAPIPath() + "contacts");
            httpRM2.Content = new StringContent(JsonConvert.SerializeObject(contact2), Encoding.UTF8, "application/json");
            var httpMC2 = new HttpMessageContent(httpRM2);
            httpMC2.Headers.Remove("Content-Type");
            httpMC2.Headers.Add("Content-Type", "application/http");
            httpMC2.Headers.Add("Content-Transfer-Encoding", "binary");
            httpMC2.Headers.Add("Content-ID", "2");
            httpMC2.Headers.Add("OData-MaxVersion", "4.0");
            httpMC2.Headers.Add("OData-Version", "4.0");
            changeSetContent.Add(httpMC2);

            batchContent.Add(changeSetContent);

            var response = SendCrmRequestAsync(HttpMethod.Post, webApiURI, batchContent).Result;

            var text = await response.Content.ReadAsStringAsync();
        }



        private async Task<HttpResponseMessage> SendCrmRequestAsync(HttpMethod method, string query, MultipartContent mpContent = null, Boolean formatted = false, int maxPageSize = 10)
        {
            var request = new HttpRequestMessage();
            request.Method = method;
            request.RequestUri = new Uri(query);

            request.Headers.Add("Prefer", "odata.maxpagesize=" + maxPageSize.ToString());
            if (formatted)
                request.Headers.Add("Prefer", "odata.include-annotations=OData.Community.Display.V1.FormattedValue");

            if (mpContent != null) {
                request.Content = mpContent;
            }

            return await httpClient.SendAsync(request);
        }


O código fonte completo está disponnível em Dyn365WebApiSamples

segunda-feira, 7 de agosto de 2017

Tecnologia da Guerra Fria: ainda está aqui e ainda está sendo usada

Eu sou um garoto da Guerra Fria. Eu cresci assistindo as notícias das implantações de Pershing II e SS-20 na Europa, a guerra soviética no Afeganistão, com alguma ação de VHS da Terminator e Top Gun ao lado. A Iugoslávia estava tentando jogar dos dois lados, e por um tempo funcionou como um encanto. Tudo caiu alguns anos depois que o Muro de Berlim caiu, tornando a nossa proeza desalinhada e sem sentido.

Eu admito que esta é uma introdução estranha para um blog de tecnologia, mas tenha paciência comigo; Começará a fazer sentido. Ao contrário da maioria dos europeus, tivemos boas relações com ambos os blocos. Nós vendemos tanques para o Kuwait e artilharia de foguete para Saddam, compramos combustível barato e MiGs dos soviéticos, e, em troca, exportamos algumas coisas que não se conseguem diretamente do Ocidente. Conheço pessoas que ficariam nos hotéis de Berlim Oriental porque eram mais baratas, depois atravessavam a fronteira para Berlim Ocidental para trabalhar, brincar e fazer compras, apenas para cruzar através de cruzamentos fronteiriços praticamente não utilizados como Checkpoint Charlie, tudo em questão de horas.

Em uma dessas viagens, meu pai pegou um Commodore C64, que foi pressionado no serviço como nossa máquina de jogos da Guerra Fria. A maioria dos videogames dos anos 80 e, de fato, muita música e filmes, foram inspirados por inúmeras guerras de proxy e a ameaça de um apocalipse nuclear. À medida que o Muro caiu, muitas pessoas assumiram que seria o fim das despesas de defesa e que o mundo seria um lugar mais seguro. Não funcionou exatamente assim, não é? No entanto, o efeito a longo prazo da Guerra Fria em ciência e tecnologia é mais profundo do que os 99 Luftbalons de Nena, ou qualquer filme do Vietnam de Oliver Stone.

Minuteman: um estudo de caso de Guerra Fria

Se você está lendo isso, você já está usando uma tecnologia desenvolvida para guerreiros frios; A Internet. Isso não é tudo. Muitas tecnologias e infra-estrutura que damos por certo foram desenvolvidas, ou pelo menos concebidas, durante essas décadas tumultuadas.

E essa constelação de satélites GPS orbitando a Terra? Não foi colocado lá para geotag selfies ou obter um passeio de Uber; Foi projetado para ajudar o Comando Aéreo Estratégico dos EUA a entregar centenas de megatons de sol instantâneo em alvos soviéticos com precisão. Circuitos integrados, transistores, computação em estado sólido? Sim, todos desenvolvidos para as forças armadas e pagos pelo contribuinte dos EUA.

Apenas um exemplo: o míssil balístico intercontinental (ICBM) lustroso e insondávelmente mortal. Não foi o primeiro ICBM lá fora, mas quando apareceu, foi revolucionário. Era um míssil de combustível sólido, o que significava que poderia responder a uma ameaça e lançar em um minuto sem ter que ser alimentado, daí o nome. Mas o combustível sólido era apenas parte da história: o estado sólido era muito mais interessante a partir de uma perspectiva geek. Antes do Minuteman, os ICBMs dependiam de computadores analógicos com giroscópios mecânicos e sensores primitivos. Uma vez que foram conectados a um alvo específico, o pacote de destino não poderia ser alterado facilmente. Minuteman foi a primeira implementação em massa de um computador digital de propósito geral; Ele integrou um sistema de guia de mísseis e piloto automático em um pacote, com armazenamento confiável que poderia levar o estresse de um lançamento de silo.

Os transistores não eram nada novos nesse ponto; Eles foram desenvolvidos anos antes pela Bell Labs. Sim, esses transistores primitivos estavam quase exclusivamente reservados para o complexo militar-industrial. Tio Sam era o único cliente de praticamente todos os primeiros computadores e chips, acumulando montes de dinheiro. Esses primeiros transistores ofereceram um salto quântico sobre os tubos de vácuo, mas não eram perfeitos. Segundo os padrões de hoje, eram lixo. A confiabilidade simplesmente não estava lá, e se você precisasse lançar algumas centenas de ogivas termonucleares em todo o planeta, você precisava de um sistema de orientação que não falharia assim que a vela fosse acesa.

Então, o que você faria quando se deparasse com um problema técnico que você não pode resolver com o dinheiro? Simples: você joga mais dinheiro nisso, e é exatamente isso que a Força Aérea dos EUA fez. Eles queimaram milhões para fazer as malditas coisas serem confiáveis ​​o bastante para serem usadas em ambientes hostis e sobreviverem ao estresse de uma elevação de alta G para o espaço. Isso era conhecido como o programa Minuteman High Confiabilidade (Hi-Rel).



O primeiro computador digital realmente móvel foi um pouco mais mortal do que seu notebook e iPhone.

Funcionou, mas a USAF ganhou um pouco mais do que eles esperavam. Ao tentar melhorar um único sistema de armas, a USAF acabou dando um enorme impulso à indústria de tecnologia em geral. Eventualmente, o Minuteman foi atualizado para incluir um novo sistema de orientação baseado em microchip, com uma forma primitiva de armazenamento em estado sólido. Esta relíquia da Guerra Fria está em serviço desde a administração Kennedy, e a atual encarnação existe há 45 anos, recebendo várias atualizações de hardware e software ao longo dos anos.

Assim, ao delinear o desenvolvimento e a evolução de um único sistema estratégico de entrega de armas, toquei em uma série de tecnologias vitais que damos por certo: transistores confiáveis, chips, armazenamento em estado sólido, computadores programáveis ​​produzidos em massa e assim por diante. O Minuteman também foi o primeiro computador digital móvel.

Alguns podem argumentar que o legado de tais armas é que a Destruição mutuamente assegurada (MAD), garantida pela tríade nuclear, que impediu as superpotências de irem a uma guerra total. Provavelmente o fez, mas, ao fazê-lo, também permitiu que engenheiros em todo o mundo desenvolvessem tecnologias e conceitos aplicáveis ​​em várias indústrias e áreas de estudo.

Seu legado real reside em todos os circuitos integrados do planeta.

Pioneiros capitalistas tentam ganhar dinheiro

O que poderia ser mais capitalista do que monetizar instrumentos de assassinato em massa? Os contribuintes pagaram seu desenvolvimento, não os capitalistas de risco!

Brincadeiras a parte, pode-se argumentar que o Red Scare dos anos 50 criou o Silicon Valley. A maior parte do dinheiro realmente veio de contribuintes, e a maioria das empresas que obtiveram contratos de defesa lucrativos foi rápido para ganhar dinheiro com a tecnologia de dupla utilização desenvolvida para os militares. Lembra-se de Bell Labs? Algumas de suas pessoas mais brilhantes passaram a co-encontrar Fairchild Semiconductor e, eventualmente, criaram a Intel uma década depois. O computador de orientação Minuteman atualizado foi baseado em chips de outro gigante semicondutor: Texas Instruments.

Não estou discutindo o brilho de pessoas como os co-fundadores da Intel, Robert Noyce e Gordon Moore. Não tenho dúvidas de que eles marcaram a indústria da tecnologia, mas também é difícil contestar que a indústria de tecnologia não teria desenvolvido ao mesmo ritmo se não houvesse financiamento do governo. Sim, os contribuintes efetivamente subsidiaram a indústria de tecnologia há décadas, mas, no longo prazo, provavelmente estão melhores. A Westinghouse não precisava de subsídios para desenvolver máquinas de lavar e refrigeradores, porque a demanda dos consumidores era forte, mas nos primeiros dias da computação, praticamente não havia demanda do consumidor. É por isso que os governos tiveram que intervir.

Mas o que o contribuinte obteve?



A Internet, o GPS, transistores confiáveis ​​e chips: tecnologia da Guerra Fria possibilitada por gastos de defesa desenfreados.

O espaço e a corrida de armamentos geraram uma série de tecnologias que, por sua vez, criaram inúmeras oportunidades de negócios. Mesmo os computadores primitivos tiveram um impacto profundo na indústria. Eles tornaram as redes de energia e infra-estrutura de transporte mais eficientes, ajudaram a melhorar a segurança em instalações industriais, incluindo instalações químicas e nucleares sensíveis, mudaram os bancos, comunicações, entretenimento e assim por diante.

O melhor de tudo, de alguma forma, conseguimos não nos arremessar com as armas que essas tecnologias possibilitaram, mas, ao mesmo tempo, transformamos as espadas em arados. Nos anos cinquenta, os EUA e a URSS lançaram iniciativas destinadas a examinar os usos civis da energia nuclear (incluindo os esquemas de explosivos nucleares de engenharia civil, que foram terrivelmente errados), mas não representavam nada. Não foi a força do átomo que mudou o mundo, foi o microchip humilde e as tecnologias auxiliares desenvolvidas para inúmeros programas de defesa.

Antes de terem marcado a ciência e bater Gary Kasparov na mesa de xadrez, os supercomputadores e seus predecessores analógicos foram usados ​​para simular processos físicos vitais no desenvolvimento de armas termonucleares. Uma vantagem em poder de computação pura poderia render avanços em inúmeros campos. As simulações computacionais permitiram que as marinhas ocidentais desenvolvessem submarinos mais silenciosos com novos parafusos, otimizados digitalmente para evitar a cavitação. Os processadores de sinal digital (DSPs) tornaram os sonares muito mais sensíveis, e algumas décadas depois, os DSP avançados tornaram o som da música melhor. O design assistido por computador não foi usado apenas para reduzir a seção transversal do radar dos aviões, além de tornar nossos edifícios e carros mais baratos, mais seguros e mais eficientes em termos energéticos.

Alguns desses esforços resultaram em um ponto morto tecnológico, mas a maioria não o fez. Um dos meus problemas tecnológicos favoritos foi Blue Peacock, uma mina nuclear britânica (sim, mina terrestre, e não bomba), pesando 7,2 toneladas. Uma vez que dependia da tecnologia do início da década de 50 e teve que ser enterrado no campo alemão, os engenheiros rapidamente perceberam que o frio poderia matar a eletrônica por dentro, então eles tentaram descobrir como manter os circuitos quentes. Sua solução era tão estranha que foi confundida com uma brincadeira do dia de abril quando o design foi desclassificado em 1 de abril de 2004.



Nenhuma galinha foi prejudicada na criação deste post no blog, ou no programa de minas terrestres nucleares Blue Peacock.

Um frango deveria ser selado dentro da carcaça, com comida e água suficientes para permanecer vivo por uma semana. Seu calor corporal manteria a eletrônica da bomba operacional.

À medida que as indústrias civis começaram a implementar essas tecnologias de ponta em massa, nossa qualidade de vida e produtividade aumentaram exponencialmente. Nossas TVs, carros, telefones, roupas que usamos e praticamente qualquer produto de consumo que compramos: tudo melhor graças ao maior desperdício de dinheiro na história. Concedido, todos nós temos vestígios de Strontium 90 nos nossos ossos, mas no grande esquema das coisas, é um pequeno preço a pagar pelo mundo de alta tecnologia que gostamos tanto.

Ah, sim, nós também conseguimos jogos de vídeo fora dele. Cargas e muitos jogos de vídeo.

Kickstarting Game Development

Os vídeo games foram pioneiros nos primeiros computadores digitais (e alguns analógicos também). De fato, Tennis for Two , possivelmente o primeiro jogo a usar uma exibição gráfica, foi desenvolvido para um computador analógico em 1958. No entanto, nem mesmo os vilões de Bond tinham computadores nesse ponto, então o aumento da indústria de video games teve que aguardar Hardware para amadurecer.

Em meados dos anos setenta, os microchips tornaram-se suficientemente baratos para aplicações de mercado de massa. Agora que nós possuímos o hardware, precisávamos alguns desenvolvedores de software e um caso de uso para chips baratos. Como o consumidor médio não estava interessado em computadores caros e complicados que foram projetados para grandes negócios, a atenção mudou para o jogo; Arcades, consoles de jogos e computadores baratos, como o ZX e o C64.

Essas humildes máquinas trouxeram computadores programáveis ​​para milhões de famílias, engatando uma geração de crianças no entretenimento digital e criando oportunidades para desenvolvedores de jogos. Consoles e computadores baratos trouxeram o arcade para a sala de estar, iniciando uma nova era de video games e criando inúmeros empregos na indústria. Mesmo os soviéticos entraram com Tetris, o primeiro jogo atrás da cortina de ferro.



O advento de computadores domésticos e consoles de jogos baratos criou uma geração engajada em computação e codificação.

Não era apenas entretenimento. Ao contrário dos consoles, os ZX e C64 eram computadores apropriados, e crianças geeky rapidamente encontraram novos usos para eles. Começaram a fazer demonstrações, começaram a codificar. É provável que você conheça muitas dessas crianças, e se você está lendo isso, provavelmente você trabalha com algumas delas.

Se você está interessado no desenvolvimento de games, e o que a Guerra Fria tem a ver com eles, sugiro que você verifique as frutas nucleares; Um novo documentário que é imperdível para todos os geeks e gamers nascidos nos anos 70 e início dos anos 80.

Esses caras e galões passaram a desenvolver uma nova série de video games, criar negócios online bem-sucedidos, criar novas tecnologias e revolucionar o mundo digital, tudo no espaço de uma década. Uma geração que cresceu com a constante ameaça da guerra nuclear, desfrutando de ficção científica distópica, ajudou a tornar o mundo um lugar melhor. Eles não desenvolveram o Skynet, eles desenvolveram milhões de aplicativos móveis e web.

Então, não há Terminators. Pelo menos ainda não.

Guerra fria 2.0 e a emergência de novas ameaças

Este não é um blog geopolítico, mas se você seguir as notícias, provavelmente você sabe que o mundo é um lugar desarrumado. Não, o fim da Guerra Fria não trouxe uma era de paz e estabilidade, e já se fala de uma "Segunda Guerra Fria", ou pior, de uma guerra "quente". Embora a maior parte dessas preocupações seja nada além do hype e do sensacionalismo, permanecem várias ameaças graves. A ameaça da aniquilação nuclear está longe, mas a tecnologia que tanto amamos criou uma série de potenciais ameaças e problemas, que vão desde a privacidade e a segurança até preocupações éticas.

Felizmente, não é provável que vejamos uma corrida de armamentos para rivalizar com a que testemunhamos no século XX, mas não precisamos. A mesma tecnologia que torna nossa vida mais fácil e mais produtiva também pode ser usada contra nós. A infra-estrutura digital em que contamos para trabalhar e jogar é frágil e pode ser alvo de criminosos, governos estrangeiros, atores não estatais e até mesmo nutrinos solitários com rancor.

Estas novas ameaças incluem, mas não estão limitadas a:

  • Cibercrime
  • Cibercafé patrocinado pelo Estado
  • Uso indevido da tecnologia de veículos autônomos
  • Violações de privacidade
  • Abusos de vigilância em massa
  • Uso de comunicações seguras para atividades criminosas / terroristas

Todos representam um desafio sério e a indústria está tendo problemas para manter-se. O meu argumento é simples: não precisamos mais desenvolver tecnologia inovadora para obter uma vantagem nas lutas geopolíticas, mas continuaremos a desenvolver tecnologias e métodos para enfrentar novas ameaças e problemas. É um círculo vicioso, uma vez que estas novas ameaças são tornam possíveis pela dependência das comunicações digitais e pela ampla disponibilidade de várias tecnologias que podem ser empregadas por organizações e indivíduos hostis.



Uma nova geração de ameaças emergentes está voltando a reunir os líderes da indústria e os governos em torno de uma causa comum.

O cibercrime geralmente é associado com roubo de identidade e fraude de cartão de crédito, mas não está mais limitado a esses campos. O advento de canais de comunicação seguros permitiu que os criminosos se expandissem para novos nichos. A cena percorreu um longo caminho desde as façanhas romantizadas de phreaks de telefone como Steve Wozniak. Alguns oferecem hacking para contratar, outros estão dispostos a hospedar todo tipo de conteúdo ilícito, sem perguntas. Alguns grupos se especializam em lavagem de dinheiro, bazares de drogas darknet e assim por diante. A maior ameaça com esta nova geração de cibercrime é que você não precisa mais possuir muitas habilidades para se envolver. À medida que o cibercrime amadurece, diferentes grupos se especializam em diferentes atividades, e podem ser contratados.

A guerra cibernética patrocinada pelo Estado representa uma séria ameaça para a infra-estrutura, sistemas financeiros e segurança nacional. No entanto, realmente não há muito o que um indivíduo possa fazer diante dessas ameaças, então não tem como objetivo perder tempo nelas nesta postagem. Outra forma de guerra econômica poderia ser privar uma nação ou região de acesso à Internet. Já aconteceu antes, às vezes por acidente, às vezes por decreto governamental e ação inimiga.

Os drones comerciais não têm muito em comum com seus homólogos militares. Seu alcance e carga são muito limitados e, enquanto um drone militar geralmente pode se deslocar sobre uma área por horas a fio, a resistência dos drones de hobby é limitada a minutos em vez de horas. Isso não significa que eles não podem ser usados ​​para o crime; Eles ainda podem invadir a privacidade de alguém, contrabandear drogas em uma fronteira, ou até mesmo transportar explosivos. Os carros autônomos ainda estão em sua infância, então eu não sinto a necessidade de discutir a miríade de perguntas que eles criarão.

A privacidade continua sendo uma das maiores preocupações relacionadas à Internet expressadas pela pessoa mediana. Isso é compreensível; Mudamos nossa vida diária para a esfera digital, colocando nossa privacidade em risco. As pessoas nem precisam ser especificamente direcionadas para que sua privacidade e integridade pessoal sejam comprometidas. A maioria dos dados que abre caminho on-line é lançado na forma de despejos maciços após uma violação de segurança que afeta muitos, se não todos, usuários de um determinado serviço on-line. As pessoas continuarão a exigir mais privacidade e, por sua vez, os clientes exigirão mais segurança dos engenheiros de software (que não são trabalhadores milagrosos e não podem garantir segurança e privacidade absolutas).

A vigilância de massa geralmente é realizada por governos e não deve representar uma ameaça para o cidadão ou empresa comum. No entanto, ainda é uma ameaça potencial, pois pode ser abusada por trabalhadores descontentes, governos estrangeiros ou por meio de violações de dados. O outro problema é o custo para o contribuinte; A vigilância de massa não é barata e continuaremos a ver mais.

A maioria dos governos não se incomodaria com programas de vigilância em massa e metadados se não estivessem enfrentando ameaças muito reais. A mesma tecnologia desenvolvida para manter nossas comunicações e atividades on-line privadas pode ser abusada por todos os tipos de indivíduos que não gostaríamos de encontrar em um beco sombrio. A lista inclui sindicatos de crimes multinacionais, terroristas e insurgentes. No entanto, nem toda essa comunicação precisa ser criptografada e segura. O ponto de propaganda é torná-lo amplamente disponível para qualquer um, e a Internet deu a cada crackpot com um smartphone o maior megafone da história, com alcance global, gratuitamente. Você pode usar a Internet para reunir um milhão de pessoas em torno de uma boa causa em questão de dias, mas os mesmos princípios podem ser aplicados a uma causa ruim. Se o público-alvo estiver disposto a participar de um culto da morte com uma propensão para as bandeiras negras, você não precisa de um milhão de pessoas; Você só precisa de algumas dúzias.

A diferença entre ciência e ficção científica

Apesar de todo o seu brilho, os autores de ficção científica que ajudaram a moldar a cultura popular no século 20 não viram o futuro real. Eles não visualizaram exatamente a Internet, e muito menos o seu profundo impacto na sociedade.

Desculpe estourar sua bolha, mas Terminators e Inteligência Artificial (AI) não são uma ameaça ainda, e não será em breve. As ameaças reais são mais baixas, mas isso não significa que podemos ignorá-las. Você não precisa de um Terminator para criar estragos, tudo o que você precisa é algumas linhas de código realmente desagradável que podem interromper a infra-estrutura, causando todos os tipos de problemas. Você não precisa de um autômato super-inteligente do futuro para causar danos. Uma vez que o eBay não possui Terminators, é muito mais fácil usar um drone off-the-shelf, programado para entregar uma carga útil para um alvo específico: drogas para um traficante ou uma carga explosiva para um VIP.

Mas estas não são as maiores ameaças, são apenas ameaças potenciais: algo para um roteiro de Hollywood, e não um blog de tecnologia.

As ameaças reais são de natureza criminosa, mas eles tendem a permanecer no domínio cibernético. Você não precisa mover fisicamente nada para mover dinheiro sujo e informações on-line. A aplicação da lei já está tendo dificuldade em acompanhar o cibercrime, o que parece piorar. Embora seja verdade que a taxa de criminalidade nos países desenvolvidos está a diminuir, essas estatísticas não pintam o quadro completo. Algumas semanas atrás, o Escritório Britânico de Estatísticas Nacionais (ONS) reportou um duplo aumento na taxa de criminalidade para a Inglaterra e o País de Gales, totalizando mais de 11,6 milhões de ofensas. A taxa de criminalidade tradicional continuou a cair, mas as estatísticas incluíram 5,1 milhões de incidentes de fraude em linha.

O custo do crime físico está diminuindo, mas o custo do cibercrime está começando a recuperar o atraso. Eu acredito firmemente que a indústria terá que fazer mais para reforçar a segurança, e os governos terão que investir em segurança online e prevenção de crime também.

Apenas no caso de você estar em ficção distópica e não encontrar ameaças criminosas excitantes, outro desenvolvimento assustador seria a monopolização de dados: um processo no qual os gigantes da indústria iriam controlar a competitividade, tornada possível por sua vasta base de usuários, para tornar a concorrência Inútil, assim sufocando a inovação.

Sim, estou ciente de que os Terminators fariam para um futuro mais agitado e uma postagem de blog interessante, mas ainda não estamos lá.

BY NERMIN HAJDARBEGOVIC - TECHNICAL EDITOR @ TOPTAL

terça-feira, 25 de julho de 2017

Apache Spark Streaming Tutorial: Identificando as Hashtags de Tendência do Twitter

Hoje em dia, os dados estão crescendo e se acumulando mais rápido do que nunca. Atualmente, cerca de 90% de todos os dados gerados em nosso mundo foram gerados somente nos últimos dois anos. Devido a essa taxa de crescimento impressionante, as grandes plataformas de dados tiveram que adotar soluções radicais para manter esses enormes volumes de dados.

Uma das principais fontes de dados hoje são as redes sociais. Permita-me demonstrar um exemplo da vida real: lidar, analisar e extrair informações de dados de redes sociais em tempo real usando uma das soluções eco de Big Data mais importantes, Apache Spark e Python.



Neste artigo, vou ensinar-lhe como criar um aplicativo simples que lê fluxos online do Twitter usando o Python e, em seguida, processa os tweets usando o Apache Spark Streaming para identificar hashtags e, finalmente, retorna as principais hashtags de tendências e representa esses dados em um painel de controle.

Criando suas próprias credenciais para as APIs do Twitter
Para obter tweets do Twitter, você precisa se registrar no TwitterApps clicando em "Criar novo aplicativo" e, em seguida, preencha o formulário abaixo, depois clique em "Criar seu aplicativo do Twitter".



Em segundo lugar, acesse seu aplicativo recém-criado e abra a guia "Keys and Access Tokens". Em seguida, clique em "Gerar meu token de acesso".



Seus novos tokens de acesso aparecerão abaixo.



E agora você está pronto para o próximo passo.

Criando o Twitter HTTP Client
Nesta etapa, vou mostrar-lhe como criar um cliente simples que obterá os tweets da API do Twitter usando o Python e os passará para a instância Spark Streaming. Vai ser fácil de seguir para qualquer Python Developer.

Primeiro, vamos criar um arquivo chamado [twitter_app.py] e, em seguida, adicionaremos o código juntos como abaixo.

Importe as bibliotecas que usaremos abaixo:


import socket
import sys
import requests
import requests_oauthlib
import json


E adicione as variáveis que serão usadas no OAuth para se conectar ao Twitter conforme abaixo:


# Replace the values below with yours
ACCESS_TOKEN = 'YOUR_ACCESS_TOKEN'
ACCESS_SECRET = 'YOUR_ACCESS_SECRET'
CONSUMER_KEY = 'YOUR_CONSUMER_KEY'
CONSUMER_SECRET = 'YOUR_CONSUMER_SECRET'
my_auth = requests_oauthlib.OAuth1(CONSUMER_KEY, CONSUMER_SECRET,ACCESS_TOKEN, ACCESS_SECRET)


Agora, vamos criar uma nova função chamada get_tweets que chamará o URL da API do Twitter e retornará a resposta para um fluxo de tweets.

def get_tweets():
 url = 'https://stream.twitter.com/1.1/statuses/filter.json'
 query_data = [('language', 'en'), ('locations', '-130,-20,100,50'),('track','#')]
 query_url = url + '?' + '&'.join([str(t[0]) + '=' + str(t[1]) for t in query_data])
 response = requests.get(query_url, auth=my_auth, stream=True)
 print(query_url, response)
 return response


Em seguida, crie uma função que obtenha a resposta acima e extraia o texto dos tweets do objeto JSON de todos os tweets. Depois disso, envie todos os tweets à instância Spark Streaming (serão discutidos mais tarde) através de uma conexão TCP.

def send_tweets_to_spark(http_resp, tcp_connection):
 for line in http_resp.iter_lines():
     try:
         full_tweet = json.loads(line)
         tweet_text = full_tweet['text']
         print("Tweet Text: " + tweet_text)
         print ("------------------------------------------")
         tcp_connection.send(tweet_text + '\n')
     except:
         e = sys.exc_info()[0]
         print("Error: %s" % e)


Agora vamos fazer a parte principal, um app host de conexão socket, na qual o Spark vai se conectar. Vamos configurar o IP aqui para ser o localhost, pois todos serão executados na mesma máquina e na porta 9009. Então chamaremos o método get_tweets, que fizemos acima, para obter os tweets do Twitter e passar sua resposta junto com a conexão de socket para send_tweets_to_spark para enviar os tweets para Spark.

TCP_IP = "localhost"
TCP_PORT = 9009
conn = None
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
print("Waiting for TCP connection...")
conn, addr = s.accept()
print("Connected... Starting getting tweets.")
resp = get_tweets()
send_tweets_to_spark(resp, conn)


Configurando nossa aplicação de streaming de Apache Spark
Vamos construir o nosso aplicativo Spark streaming que fará o processamento em tempo real para os tweets recebidos, extrairá os hashtags deles e calculará quantas hashtags foram mencionadas.



Primeiro, temos que criar uma instância de Contexto Spark (sc), então criamos o Contexto de Transmissão ssc de sc com um intervalo de lote de dois segundos que fará a transformação em todos os fluxos recebidos a cada dois segundos. Observe que estabelecemos o nível de log para ERROR para desabilitar a maioria dos logs que o Spark escreve.

Nós definimos um ponto de controle aqui para permitir a verificação periódica do RDD; Isso é obrigatório para ser usado em nosso aplicativo, pois usaremos transformações com estado (serão discutidas mais adiante na mesma seção).

Em seguida, definimos nosso DStream dataStream principal que se conectará ao servidor de socket que criamos antes, na porta 9009, e leia os tweets dessa porta. Cada registro no DStream será um tweet.

from pyspark import SparkConf,SparkContext
from pyspark.streaming import StreamingContext
from pyspark.sql import Row,SQLContext
import sys
import requests
# create spark configuration
conf = SparkConf()
conf.setAppName("TwitterStreamApp")
# create spark context with the above configuration
sc = SparkContext(conf=conf)
sc.setLogLevel("ERROR")
# create the Streaming Context from the above spark context with interval size 2 seconds
ssc = StreamingContext(sc, 2)
# setting a checkpoint to allow RDD recovery
ssc.checkpoint("checkpoint_TwitterApp")
# read data from port 9009
dataStream = ssc.socketTextStream("localhost",9009)


Agora, vamos definir a nossa lógica de transformação. Primeiro vamos dividir todos os tweets em palavras e colocá-los em palavras RDD. Então, vamos filtrar apenas hashtags de todas as palavras e mapeá-los para um par de (hashtag, 1) e colocá-los no hashtags RDD.

Então precisamos calcular quantas vezes a hashtag foi mencionada. Nós podemos fazer isso usando a função reductionByKey. Esta função calculará quantas vezes a hashtag foi mencionada por cada lote, ou seja, irá redefinir as contagens em cada lote.

No nosso caso, precisamos calcular as contagens em todos os lotes, então usaremos outra função chamada updateStateByKey, pois esta função permite que você mantenha o estado de RDD ao atualizá-lo com novos dados. Desta forma, é chamado de Transformação de Estado.

Note que, para usar updateStateByKey, você precisa configurar um ponto de controle e o que fizemos na etapa anterior.

# split each tweet into words
words = dataStream.flatMap(lambda line: line.split(" "))
# filter the words to get only hashtags, then map each hashtag to be a pair of (hashtag,1)
hashtags = words.filter(lambda w: '#' in w).map(lambda x: (x, 1))
# adding the count of each hashtag to its last count
tags_totals = hashtags.updateStateByKey(aggregate_tags_count)
# do processing for each RDD generated in each interval
tags_totals.foreachRDD(process_rdd)
# start the streaming computation
ssc.start()
# wait for the streaming to finish
ssc.awaitTermination()


O updateStateByKey assume uma função como um parâmetro chamado de função de atualização. Ele é executado em cada item no RDD e faz a lógica desejada.

No nosso caso, criamos uma função de atualização chamada aggregate_tags_count que somará todos os novos valores para cada hashtag e os adiciona ao total_sum que é a soma em todos os lotes, e salva os dados em tags_totals RDD.

def aggregate_tags_count(new_values, total_sum):
 return sum(new_values) + (total_sum or 0)


Em seguida, fazemos o processamento em tags_totals RDD em cada lote para convertê-lo em tabela temporária usando o Spark SQL Context e, em seguida, execute uma instrução de seleção para recuperar os dez principais hashtags com suas contagens e colocá-los em frame de dados hashtag_counts_df.

def get_sql_context_instance(spark_context):
 if ('sqlContextSingletonInstance' not in globals()):
        globals()['sqlContextSingletonInstance'] = SQLContext(spark_context)
 return globals()['sqlContextSingletonInstance']
def process_rdd(time, rdd):
 print("----------- %s -----------" % str(time))
 try:
     # Get spark sql singleton context from the current context
     sql_context = get_sql_context_instance(rdd.context)
     # convert the RDD to Row RDD
     row_rdd = rdd.map(lambda w: Row(hashtag=w[0], hashtag_count=w[1]))
     # create a DF from the Row RDD
     hashtags_df = sql_context.createDataFrame(row_rdd)
     # Register the dataframe as table
     hashtags_df.registerTempTable("hashtags")
     # get the top 10 hashtags from the table using SQL and print them
     hashtag_counts_df = sql_context.sql("select hashtag, hashtag_count from hashtags order by hashtag_count desc limit 10")
     hashtag_counts_df.show()
     # call this method to prepare top 10 hashtags DF and send them
     send_df_to_dashboard(hashtag_counts_df)
 except:
     e = sys.exc_info()[0]
     print("Error: %s" % e)


O último passo no nosso aplicativo Spark é enviar o quadro de dados hashtag_counts_df para o aplicativo do painel. Então, vamos converter o quadro de dados em dois arrays, um para os hashtags e outro para suas contagens. Em seguida, nós os enviaremos para o aplicativo do painel de controle através da API REST.

def send_df_to_dashboard(df):
 # extract the hashtags from dataframe and convert them into array
 top_tags = [str(t.hashtag) for t in df.select("hashtag").collect()]
 # extract the counts from dataframe and convert them into array
 tags_count = [p.hashtag_count for p in df.select("hashtag_count").collect()]
 # initialize and send the data through REST API
 url = 'http://localhost:5001/updateData'
 request_data = {'label': str(top_tags), 'data': str(tags_count)}
 response = requests.post(url, data=request_data)


Finalmente, aqui está um exemplo de saída do Spark Streaming durante a execução e a impressão do hashtag_counts_df, você notará que a saída é impressa exatamente a cada dois segundos, de acordo com os intervalos do lote.



Crie um painel de controle em tempo real simples para representar os dados
Agora, vamos criar um aplicativo de painel simples que será atualizado em tempo real pela Spark. Vamos construí-lo usando Python, Flask e Charts.js.

Primeiro, vamos criar um projeto Python com a estrutura abaixo e baixar e adicionar o arquivo Chart.js no diretório estático.



Então, no arquivo da aplicação.py, criamos uma função chamada update_data, que será chamada pelo Spark através da URL http://localhost:5001/ updateData para atualizar as matrizes de valores e etiquetas globais.

Além disso, a função refresh_graph_data é criada para ser chamada pela solicitação AJAX para retornar os novos rótulos e matrizes de valores atualizados como JSON. A função get_chart_page renderizará a página chart.html quando chamada.

from flask import Flask,jsonify,request
from flask import render_template
import ast
app = Flask(__name__)
labels = []
values = []
@app.route("/")
def get_chart_page():
 global labels,values
 labels = []
 values = []
 return render_template('chart.html', values=values, labels=labels)
@app.route('/refreshData')
def refresh_graph_data():
 global labels, values
 print("labels now: " + str(labels))
 print("data now: " + str(values))
 return jsonify(sLabel=labels, sData=values)
@app.route('/updateData', methods=['POST'])
def update_data():
 global labels, values
 if not request.form or 'data' not in request.form:
     return "error",400
 labels = ast.literal_eval(request.form['label'])
 values = ast.literal_eval(request.form['data'])
 print("labels received: " + str(labels))
 print("data received: " + str(values))
 return "success",201
if __name__ == "__main__":
 app.run(host='localhost', port=5001)


Agora, vamos criar um gráfico simples no arquivo chart.html para exibir os dados da hashtag e atualizá-los em tempo real. Conforme definido abaixo, precisamos importar as bibliotecas de JavaScript Chart.js e jquery.min.js.

Na etiqueta do corpo, temos que criar uma tela e dar-lhe uma ID para fazer referência a ela ao exibir o gráfico usando o JavaScript na próxima etapa.

<!DOCTYPE html>
<html>
 <head>
     <meta charset="utf-8"/>
     <title>Top Trending Twitter Hashtags</title>
     <script src='static/Chart.js'></script>
     <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
 
 </head>
 <body>
         <h2>Top Trending Twitter Hashtags</h2>
         <div style="width:700px;height=500px">
             <canvas id="chart"></canvas>
         </div>
 </body>
</html>


Agora, vamos construir o gráfico usando o código JavaScript abaixo. Primeiro, obtemos o elemento de tela, e então criamos um novo objeto de gráfico e passamos o elemento de tela para ele e definimos seu objeto de dados como abaixo.

Observe que os labels de dados e os dados são delimitados com rótulos e variáveis de valores que são retornados enquanto renderiza a página ao chamar a função get_chart_page no arquivo app.py.

A última parte restante é a função que está configurada para fazer uma solicitação Ajax a cada segundo e chama a URL /refreshData, que executará refresh_graph_data em app.py e retornará os novos dados atualizados e, em seguida, atualizará o gráfico que renderiza os novos dados.

<script>
   var ctx = document.getElementById("chart");
   var myChart = new Chart(ctx, {
     type: 'horizontalBar',
     data: {
         labels: [{% for item in labels %}
                   "{{item}}",
                  {% endfor %}],
         datasets: [{
             label: '# of Mentions',
             data: [{% for item in values %}
                       {{item}},
                     {% endfor %}],
             backgroundColor: [
                 'rgba(255, 99, 132, 0.2)',
                 'rgba(54, 162, 235, 0.2)',
                 'rgba(255, 206, 86, 0.2)',
                 'rgba(75, 192, 192, 0.2)',
                 'rgba(153, 102, 255, 0.2)',
                 'rgba(255, 159, 64, 0.2)',
                 'rgba(255, 99, 132, 0.2)',
                 'rgba(54, 162, 235, 0.2)',
                 'rgba(255, 206, 86, 0.2)',
                 'rgba(75, 192, 192, 0.2)',
                 'rgba(153, 102, 255, 0.2)'
             ],
             borderColor: [
                 'rgba(255,99,132,1)',
                 'rgba(54, 162, 235, 1)',
                 'rgba(255, 206, 86, 1)',
                 'rgba(75, 192, 192, 1)',
                 'rgba(153, 102, 255, 1)',
                 'rgba(255, 159, 64, 1)',
                 'rgba(255,99,132,1)',
                 'rgba(54, 162, 235, 1)',
                 'rgba(255, 206, 86, 1)',
                 'rgba(75, 192, 192, 1)',
                 'rgba(153, 102, 255, 1)'
             ],
             borderWidth: 1
         }]
     },
     options: {
         scales: {
             yAxes: [{
                 ticks: {
                     beginAtZero:true
                 }
             }]
         }
     }
   });
   var src_Labels = [];
   var src_Data = [];
   setInterval(function(){
     $.getJSON('/refreshData', {
     }, function(data) {
         src_Labels = data.sLabel;
         src_Data = data.sData;
     });
     myChart.data.labels = src_Labels;
     myChart.data.datasets[0].data = src_Data;
     myChart.update();
   },1000);
</script>


Execução das aplicações em conjunto
Vamos executar os três aplicativos na ordem abaixo:
1. Cliente do aplicativo do Twitter.
2. Spark App.
3. Dashboard Web App.

Então você pode acessar o painel de controle em tempo real usando a URL

Agora, você pode ver seu gráfico sendo atualizado, conforme abaixo:



Apache Streaming - Casos de uso da vida real
Aprendemos a fazer análises de dados simples em dados em tempo real usando Spark Streaming e integrando-o diretamente com um painel simples usando um serviço web RESTful. A partir deste exemplo, podemos ver o quão poderoso é o Spark, pois captura um fluxo maciço de dados, transforma-o e extrai informações valiosas que podem ser usadas facilmente para tomar decisões a qualquer momento. Existem muitos casos de uso úteis que podem ser implementados e que podem servir para diferentes indústrias, como notícias ou marketing.



Exemplo da indústria de notícias
Podemos rastrear as hashtags mais freqüentemente mencionadas para saber quais os temas em que as pessoas estão falando mais nas mídias sociais. Além disso, podemos acompanhar hashtags específicos e seus tweets para saber o que as pessoas estão dizendo sobre tópicos ou eventos específicos no mundo.

Exemplo de Marketing

Podemos coletar o fluxo de tweets e, fazendo análises de sentimentos, classificamo-los e determinemos os interesses das pessoas, a fim de direcioná-los com ofertas relacionadas aos seus interesses.

Além disso, há muitos casos de uso que podem ser aplicados especificamente para grandes análises de dados e podem servir muitas indústrias. Para mais casos de uso de Apache Spark em geral, sugiro que você verifique uma de nossas postagens anteriores.

Eu encorajo você a ler mais sobre Spark Streaming a partir daqui para saber mais sobre suas capacidades e fazer uma transformação mais avançada nos dados para obter mais informações em tempo real.

BY HANEE' MEDHAT - FREELANCE SOFTWARE ENGINEER @TOPTAL