Skip to content
Open

hw #405

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
.idea/
code/vendor/
1 change: 0 additions & 1 deletion README.md

This file was deleted.

11 changes: 11 additions & 0 deletions code/app.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

require_once('vendor/autoload.php');

try {
$app = new App\Application();
$app->run();
}
catch(Exception $e) {
App\Response::generateBadRequestResponse($e->getMessage());
}
60 changes: 60 additions & 0 deletions code/app_src/Application.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace App;

require 'vendor/autoload.php';

use Elasticsearch\ClientBuilder;
use App\Storage\ESStorage;

class Application
{
private $request;
private $ElasticSearchInterface;

public function __construct()
{
if (!RequestValidator::checkRequestType('POST')) {
throw new \Exception('Wrong request method');
}
if (RequestValidator::checkRequestIsEmpty($_POST)) {
throw new \Exception('Empty request');
}

$this->ElasticSearchInterface = new ESStorage();
$this->statisticsManager = new StatisticsManager($this->ElasticSearchInterface);

if (!RequestValidator::checkMainFields($_POST, $this->ElasticSearchInterface)) {
throw new \Exception('Main fields missing or empty');
}

$this->request = $_POST;
}

public function run()
{
switch($this->request['tab-btn']) {
case 'add':
$result = $this->ElasticSearchInterface->insert($this->request);
break;

case 'delete':
$result = $this->ElasticSearchInterface->delete($this->request);
break;

case 'info':
$result = $this->statisticsManager->getChannelSummary($this->request['id']);
break;

case 'top':
$result = $this->statisticsManager->getTopRatedChannels();
break;

default:
throw new \Exception('Empty request');
break;
}

print_r($result);
}
}
12 changes: 12 additions & 0 deletions code/app_src/Interface/StorageInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Interface;

interface StorageInterface
{
public function insert(string $arData);

public function delete(string $arData);

public function search(string $arData);
}
27 changes: 27 additions & 0 deletions code/app_src/RequestValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App;

class RequestValidator
{
public static function checkRequestType(string $typeNeeded) : bool
{
return $_SERVER['REQUEST_METHOD'] == $typeNeeded ? true : false;
}

public static function checkRequestIsEmpty(string $request) : bool
{
return empty($request) ? true : false;
}

public static function checkMainFields(string $request, array $storageInterface) : bool
{
foreach ($storageInterface->getMainFields() as $field) {
if (!isset($request[$field]) || empty($request[$field])) {
return false;
}
}

return true;
}
}
18 changes: 18 additions & 0 deletions code/app_src/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace App;

class Response
{
public static function generateOkResponse(string $responseText) : void
{
header('HTTP/1.0 200 Ok');
echo $responseText.PHP_EOL;
}

public static function generateBadRequestResponse(string $responseText) : void
{
header('HTTP/1.0 400 Bad Request');
echo $responseText.PHP_EOL;
}
}
64 changes: 64 additions & 0 deletions code/app_src/StatisticsManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);

namespace App;

class StatisticsManager
{
private $storageInterface;

public function __construct(string $storageInterface)
{
$this->storageInterface = $storageInterface;
}

public function getChannelSummary(string $channelId) : array
{
$arChannelVideos = $this->storageInterface->search(
[
'index' => 'youtube_video',
'body' => [
'query' => [
'match' => [
'channel_id' => $channelId
]
]
]
]
);

$likesCount = 0;
$dislikesCount = 0;
foreach ($arChannelVideos as $singleVideo) {
$likesCount += $singleVideo['_source']['likes'];
$dislikesCount += $singleVideo['_source']['dislikes'];
}

return [
'likes' => $likesCount,
'dislikes' => $dislikesCount,
];
}

public function getTopRatedChannels() : array
{
$arRate = [];
$arChannels = $this->storageInterface->search(
[
'index' => 'youtube_channel',
]
);

foreach ($arChannels as $channel) {
$arChannelSummary = $this->getChannelSummary($channel['_source']['channel_id']);
try {
$arRate[$channel['_source']['channel_name']] = $arChannelSummary['likes'] / $arChannelSummary['dislikes'];
} catch (Exception $e) {
$arRate[$channel['_source']['channel_name']] = PHP_MAX_INT;
}
}

asort($arRate, SORT_NUMERIC);
return $arRate;
}
}
82 changes: 82 additions & 0 deletions code/app_src/Storage/ESStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace App\Storage;

use Elasticsearch\ClientBuilder;
use App\Interface\StorageInterface;

class ESStorage implements StorageInterface
{
private $mainFields = ['index', 'id'];
private $client;
private $hosts = [
'elastic_search',
];

public function __construct()
{
$this->client = ClientBuilder::create()->setHosts($this->hosts)->build();
}

public function insert(string $request) : array
{
return $this->client->index($this->getRequestBody($request));
}

public function delete(string $request)
{
return $this->client->delete($this->getRequestBody($request));
}

public function search(string $arData) : array
{
return $this->client->search($arData)['hits']['hits'];
}

private function getRequestBody(array $request) : array
{
switch ($request['index']) {
case 'youtube_channel':
return $this->makeChannelArray($request);

case 'youtube_video':
return $this->makeVideoArray($request);

default:
throw new \Exception('Wrong index passed');

}
}

private function makeChannelArray(array $request) : array
{
return [
'index' => $request['index'],
'id' => $request['id'],
'body' => [
'channel_id' => $request['id'],
'channel_name' => $request['name']
]
];
}

private function makeVideoArray($request)
{
return [
'index' => $request['index'],
'id' => $request['id'],
'body' => [
'video_id' => $request['id'],
'title' => $request['name'],
'likes' => $request['video_likes'],
'dislikes' => $request['video_dislikes'],
'channel_id' => $request['video_channel_id'],
]
];
}

public function getMainFields() : array
{
return $this->mainFields;
}
}
14 changes: 14 additions & 0 deletions code/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"require": {
"php": "~7.4",
"ext-json": "*",
"ext-sockets": "*",
"elasticsearch/elasticsearch": "^7.0",
"google/apiclient": "~2.0"
},
"autoload": {
"psr-4": {
"App\\": "app_src"
}
}
}
Loading