Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fernando Guerra Rodrigues Machado #80

Open
wants to merge 44 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
9f1f623
.
Pequem Feb 26, 2019
ba923f4
.
Pequem Feb 26, 2019
0e7d346
configurando container
Pequem Feb 26, 2019
3854543
.
Pequem Feb 27, 2019
dbeb938
Docker, Elastic Import
Pequem Feb 27, 2019
f647d7f
.
Pequem Feb 27, 2019
dfef48d
.
Pequem Feb 27, 2019
a2217e5
.
Pequem Feb 27, 2019
f1e3fdf
add vendor php
Pequem Feb 27, 2019
1330612
.
Pequem Feb 27, 2019
cb418f0
Merge branch 'master' of https://github.com/Pequem/trabalhe-conosco-b…
Pequem Feb 27, 2019
f48ca11
front done
Pequem Feb 27, 2019
2d2ac20
finish
Pequem Feb 28, 2019
9b26e11
.
Pequem Feb 28, 2019
128675c
solved rank bug
Pequem Feb 28, 2019
6a5717a
Update LEAIME.txt
Pequem Feb 28, 2019
9aaa725
.
Pequem Feb 28, 2019
c00ecda
.
Pequem Feb 28, 2019
8f9f3e3
Update LEAIME.txt
Pequem Feb 28, 2019
934f5e8
.
Pequem Feb 28, 2019
9d65216
Update LEIAME.txt
Pequem Feb 28, 2019
934a58d
Update lista_relevancia_2.txt
Pequem Feb 28, 2019
04ed090
Update LEIAME.txt
Pequem Feb 28, 2019
364a80d
comentarios comentados
Pequem Feb 28, 2019
fa29b65
oauth, login interface
Pequem Mar 2, 2019
15b1e7f
Auth
Pequem Mar 3, 2019
946291b
refatorando codigo e pequenos ajustes
Pequem Mar 3, 2019
8c01e11
Update README.md
Pequem Mar 3, 2019
8fe7189
Update README.md
Pequem Mar 3, 2019
12a9d38
Update README.md
Pequem Mar 3, 2019
e3155fe
Update README.md
Pequem Mar 3, 2019
da9a848
Delete LEIAME.txt
Pequem Mar 3, 2019
1cba952
colocando o ES Status no banco
Pequem Mar 3, 2019
cb0ac02
.
Pequem Mar 3, 2019
f86704d
Implementado o serviço
Pequem Mar 3, 2019
543f92c
preparativos finais
Pequem Mar 3, 2019
04fbf79
Update README.md
Pequem Mar 3, 2019
584404f
Update README.md
Pequem Mar 3, 2019
4dc8922
Update README.md
Pequem Mar 3, 2019
b3d7d1c
Update README.md
Pequem Mar 3, 2019
1db5cc8
Update ImportCSVCommand.php
Pequem Mar 3, 2019
388f183
Update init.sh
Pequem Mar 3, 2019
64ca5a2
Update README.md
Pequem Mar 9, 2019
3ddd583
Update README.md
Pequem Mar 9, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
UID=1000
GID=888
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

*.csv
*.gz
54 changes: 32 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,45 @@
![PicPay](https://user-images.githubusercontent.com/1765696/26998603-711fcf30-4d5c-11e7-9281-0d9eb20337ad.png)
# Implantação e Execução
- Rode o comando "sudo sysctl -w vm.max_map_count=262144" no host para liberar a memória requerida pelo ElasticSearch.
- Rode o docker-compose up na pasta do projeto.
- Abra http://(endereço do host)/ no navegador.

# Teste Backend
**OBS**: caso esteja usando **Windows**, verifique se o caractere de quebra de linha do arquivo **php/init.sh** é o **lf**, pois o GitHub para Windows troca o caractere para **crlf**.

O desafio é criar uma API REST que busca usuarios pelo nome e username a partir de uma palavra chave. Faça o download do arquivo [users.csv.gz](https://s3.amazonaws.com/careers-picpay/users.csv.gz) que contém o banco de dados que deve ser usado na busca. Ele contém os IDs, nomes e usernames dos usuários.
# Geral
No Docker Toolbox (Windows 10 HOME) e no Ubuntu 18.04, precisei rodar esse comando na docker-machine, pois o ElasticSearch não conseguiu subir.
sudo sysctl -w vm.max_map_count=262144

###### Exemplo
| ID | Nome | Username |
|--------------------------------------|-------------------|----------------------|
| 065d8403-8a8f-484d-b602-9138ff7dedcf | Wadson marcia | wadson.marcia |
| 5761be9e-3e27-4be8-87bc-5455db08408 | Kylton Saura | kylton.saura |
| ef735189-105d-4784-8e2d-c8abb07e72d3 | Edmundo Cassemiro | edmundo.cassemiro |
| aaa40f4e-da26-42ee-b707-cb81e00610d5 | Raimundira M | raimundiram |
| 51ba0961-8d5b-47be-bcb4-54633a567a99 | Pricila Kilder | pricilakilderitaliani|
A base de dados será baixada durante o build do Docker.

O código fonte do FrontEnd está na pasta front e o do BackEnd está na pasta api.

**ANTES DE INICIAR** verifica qual é o padrão de "final de linha" do arquivo php/init.sh, pois estava tendo problema com isso, porque meu git
troca de lf para crlf quando eu clono o repositório, esse problema aconteceu somente no Windows 10.

Também são fornecidas duas listas de usuários que devem ser utilizadas para priorizar os resultados da busca. A lista 1 tem mais prioridade que a lista 2. Ou seja, se dois usuarios casam com os criterios de busca, aquele que está na lista 1 deverá ser exibido primeiro em relação àquele que está na lista 2. Os que não estão em nenhuma das listas são exibidos em seguida.
Testei a aplicação no Windows 10 Home (virtualbox), Windows 10 PRO (Hyper-V) e Ubuntu 18.04.

As listas podem ser encontradas na raiz deste repositório ([lista_relevancia_1.txt](lista_relevancia_1.txt) e [lista_relevancia_2.txt](lista_relevancia_2.txt)).
Os resultados devem ser retornados paginados de 15 em 15 registros.
Usuário: [email protected]
Senha: admin

Escolha as tecnologias que você vai usar e tente montar uma solução completa para rodar a aplicação.
# BackEnd
O BackEnd (API) irá ficar tentado conectar ao ElasticSearch quando você subir o container,
pois ele sobe muito mais rápido que o Elastic e enquanto ele fica tentado conectar,
ele ficará imprimindo "Failed connect, try again". Isso você pode ignorar, pois
enquanto o Elastic não subir, ele ficará nesse loop.

Faça um ***Fork*** deste repositório e abra um ***Pull Request***, **com seu nome na descrição**, para participar. Assim que terminar, envie um e-mail para ***[email protected]*** com o seu usuário do Github nos avisando.
Quando ele imprimir lista1 ou lista2 significa que ele achou uma entrada que está
na lista de relevância, logo ele atribuirá um peso para essas entradas.

-----
Coloquei 1GB de memória para o php durante a importação do csv, para diminuir o volume de request ao Elastic
e tentar conseguir um desempenho melhor, logo ele importa em blocos de 100 mil registros. Com 1G ele conseguia
subir até 700 mil registros para a memória antes de estoura-la, então considerei 100 mil como uma escolha segura.

### Diferenciais
Você não precisa da keywork completa para fazer uma busca ex: se você quer buscar por "debora", digitando apenas
"debo" já ira aparecer palavras que comecem com "debo"

- Criar um frontend para realizar a busca com uma UX elaborada
- Criar uma solução de autenticação entre o frontend e o backend
- Ter um desempenho elevado num conjunto de dados muito grande
- Utilizar o Docker
# FrontEnd
Estará disponível em http://localhost/ ou no endereço do docker-machine.

O FrontEnd informa o estado do banco (not ready|importing(realizando importação do
csv)|ready), você consegue fazer consultas durante a importação, porem pode acontecer
bugs.
14 changes: 14 additions & 0 deletions api/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
APP_NAME=Lumen
APP_ENV=local
APP_KEY=ye73hdywgetd8uejdhey37eydhsj3873
APP_DEBUG=true
APP_URL=http://localhost
APP_TIMEZONE=UTC

LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=

DB_CONNECTION=sqlite

CACHE_DRIVER=file
QUEUE_CONNECTION=sync
19 changes: 19 additions & 0 deletions api/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
APP_NAME=Lumen
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_TIMEZONE=UTC

LOG_CHANNEL=stack
LOG_SLACK_WEBHOOK_URL=

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

CACHE_DRIVER=file
QUEUE_CONNECTION=sync
3 changes: 3 additions & 0 deletions api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.idea
Homestead.json
Homestead.yaml
Empty file.
169 changes: 169 additions & 0 deletions api/app/Console/Commands/ImportCSVCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

namespace App\Console\Commands;

define("ES_NOT_READY", 0);
define("ES_IMPORTING", 1);
define("ES_READY", 2);

use Exception;
use Illuminate\Console\Command;
use Elasticsearch\ClientBuilder;
use App\Variables;

class ImportCSVCommand extends Command
{
protected $signature = "import:CSV";

protected $description = "";

private $host = [
'esServer'
];

public function handle()
{
//Seta o status do Elastic Search para consultas do frontend
$this->setESStatus(ES_NOT_READY);

//Previne ultrapassar o tempo e o limite de memoria
set_time_limit(0);
ini_set('memory_limit', '1024M');

$client = $this->getESConnection();

//fica tentando conectar ao elasticsearch, pois o mesmo demora a subir
$this->waitES($client);

$numberRowsCsv = $this->getCsvNumRows("/var/www/users.csv");
$dbDocsNumber = $this->getIndexNumRows($client, 'pic');

echo "CSV: $numberRowsCsv\nDB: $dbDocsNumber\n";

//verifica se todos os registro do csv estâo no ES por uma checagem simples de numeros
//para tomar a decisao de importar ou não importar o CSV
if (($dbDocsNumber < $numberRowsCsv)) {
echo "exec import\n";

$this->setESStatus(ES_IMPORTING);

//carrega as lista de prioridade na memoria
$priorityList1 = $this->getPriotityList("/var/www/lista_relevancia_1.txt");
$priorityList2 = $this->getPriotityList("/var/www/lista_relevancia_2.txt");

$this->doImport($client, $priorityList1, $priorityList2, "/var/www/users.csv");
} else {
echo "No import require\n";
}

$this->setESStatus(ES_READY);
echo "\n\n\n\nImport Finish\n\n\n\n";
return 0;
}

private function setESStatus($status)
{
$esstatus = Variables::where("name", 'esstatus')->first();
$esstatus->value = "$status";
$esstatus->save();
return;
}

private function waitES($client)
{
while (true) {
try {
$client->nodes()->stats();
break;
} catch (Exception $e) {
echo "\n\nFailed connect, I Will try again\n\n\n";
sleep(10);
}
}
}

private function getESConnection()
{

return ClientBuilder::create()->setHosts($this->host)->build();
}

private function getCsvNumRows($csvFilePath)
{
$numberRowsCsv = shell_exec("wc -l " . $csvFilePath);
$numberRowsCsv = explode(" ", $numberRowsCsv);
$numberRowsCsv = intval($numberRowsCsv[0]);
return $numberRowsCsv;
}

//recupera o numero atual de documentos no index
private function getIndexNumRows($client, $indice)
{
$params['index'] = $indice;
try {
$response = $client->indices()->stats($params);
return $response["indices"][$indice]["total"]["docs"]["count"];
} catch (Exception $ex) { }
return 0;
}

private function getPriotityList($path)
{
$list = [];
$fl = fopen($path, 'r');
while ($row = fgets($fl)) {
$list[trim($row)] = true;
}
fclose($fl);
return $list;
}

public function doImport($client, $priorityList1, $priorityList2, $filePath)
{
$f = fopen($filePath, 'r');
$nLastBloco = 0;
$Blocos = 0;
$importlimit = 100000;
while ($row = fgetcsv($f)) {
$nLastBloco++;
$params['body'][] = [
'index' => [
'_index' => 'pic',
'_type' => 'pay',
'_id' => $row[0],
]
];

//define peso para fazer a busca ranqueada
$weight = 0;
if (array_key_exists(trim($row[0]), $priorityList1)) {
$weight = 2;
echo "lista1\n";
}
if (array_key_exists(trim($row[0]), $priorityList2)) {
$weight = 1;
echo "lista2\n";
}


$params['body'][] = [
'name' => $row[1],
'username' => $row[2],
'weight' => $weight
];
if ($nLastBloco >= $importlimit) {
$client->bulk($params);
$n = ++$Blocos * $importlimit;
echo $n . "\n";
$nLastBloco = 0;
$params = ['body' => []];
}
}
if ($nLastBloco > 0) {
$client->bulk($params);
$n = ($Blocos * $importlimit) + $nLastBloco;
echo $n . " imports\n";
}
fclose($f);
}
}
29 changes: 29 additions & 0 deletions api/app/Console/Kernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Laravel\Lumen\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
Commands\ImportCSVCommand::class,
];

/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
//
}
}
10 changes: 10 additions & 0 deletions api/app/Events/Event.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace App\Events;

use Illuminate\Queue\SerializesModels;

abstract class Event
{
use SerializesModels;
}
16 changes: 16 additions & 0 deletions api/app/Events/ExampleEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Events;

class ExampleEvent extends Event
{
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
}
50 changes: 50 additions & 0 deletions api/app/Exceptions/Handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;

class Handler extends ExceptionHandler
{
/**
* A list of the exception types that should not be reported.
*
* @var array
*/
protected $dontReport = [
AuthorizationException::class,
HttpException::class,
ModelNotFoundException::class,
ValidationException::class,
];

/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}

/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
}
Loading