<?php namespace EmotionHero\Api; use EmotionHero\Application as EH; use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; use Silex\Api\ControllerProviderInterface; use EmotionHero\Models; class ScoreControllerProvider implements ControllerProviderInterface { /** @var EH */ protected $_eh; public function __construct() { $this->_eh = EH::getInstance(); } public function connect(Application $app) { // creates a new controller based on the default route $controllers = $app['controllers_factory']; $controllers->get('/', function (Application $app) { return "OK"; }); $controllers->get('/levels', function (Application $app) { $levels = $this->_eh->getEm()->getRepository(Models\Level::class)->findAll(); return $app['serializer']->serialize($levels, 'json'); }); $controllers->get('/emotions', function (Application $app) { $levels = $this->_eh->getEm()->getRepository(Models\Emotion::class)->findAll(); return $app['serializer']->serialize($levels, 'json'); }); $controllers->get('/me', function (Application $app) { $token = $app['security.token_storage']->getToken(); $user = $token->getUser(); return $app['serializer']->serialize($user, 'json'); }); $controllers->get('/games/{gameId}', function(Request $request, Application $app) { $game = $request->attributes->get('game'); if(empty($game)) { return new CustomJsonResponse(['message' => 'Game not found'], function($data) use ($app){return $app['serializer']->serialize($data, 'json');}, 404); } return new CustomJsonResponse($game, $app['serializer.json'], 200); }) ->bind('game') ->convert('game', function($game, Request $request) use ($app){ return $app['entity.manager']->getRepository(Models\Game::class)->find($request->attributes->get('gameId'));}); /** * Expects a full game + hits in the request */ $controllers->post('/games', function (Request $request, Application $app) { $data = json_decode($request->getContent(), true); $token = $app['security.token_storage']->getToken(); $user = $token->getUser(); if($data == null || !isSet($data['lvl_id'])) { return new JsonResponse(['success' => false, 'message' => "Invalid JSON body"], 400); } $level = $this->_eh->getEm()->getRepository(Models\Level::class)->find($data['lvl_id']); if(empty($level)) { return new JsonResponse(['success' => false, 'message' => "Invalid level" , "lvl_id" => (int) $data['lvl_id']], 400); } $game = new Models\Game(); $game->setUser($user); $game->setLevel($level); $game->setLostFaceTime((float) $data['lost_face_time']); $game->setOriginalGameAt(new \DateTime($data['time'])); $map_hits = []; foreach($data['hits'] as $data_hit) { $hit = new Models\Hit(); $target = $this->_eh->getEm()->getRepository(Models\Target::class)->findOneBy(['position' => $data_hit['target_index'], 'level' => $level]); if(empty($target)){ return new JsonResponse(['success' => false, 'message' => "Invalid target for hit"], 400); } $hit->setTarget($target); $hit->setScore($data_hit['score']); // attributes $hit->getAttributes()->setGlasses($data_hit['glasses']); foreach($hit->getAttributes()::$ATTRIBUTES as $attribute) { $hit->getAttributes()->setAttribute($attribute, $data_hit[$attribute]); } // emotions foreach($hit->getEmotions()::$EMOTIONS as $emotion) { $hit->getEmotions()->setEmotion($emotion, $data_hit['emotions'][$emotion]); } //expressions foreach($hit->getExpressions()::$EXPRESSIONS as $expression) { $hit->getExpressions()->setExpression($expression, $data_hit['expressions'][$expression]); } //points foreach(range(0, 33) as $i) { $hit->getPoints()->setPoint($i, $data_hit['points']["$i"]['x'], $data_hit['points']["$i"]['y']); } // set game appends hit to game $hit->setGame($game); $map_hits[$data_hit['id']] = $hit; } $this->_eh->getEm()->persist($game); $this->_eh->getEm()->flush(); $hits_array = []; foreach($map_hits as $hit_player_id => $hit) { $hits_array[$hit_player_id] = $hit->getId(); } $achievement_ids = []; foreach($game->getAchievements() as $achievement) { $achievement_ids[] = $achievement->getId(); } $achievement_ids = [2]; // override for test purpose /* @var $userRepo Models\UserRepository */ $userRepo = $this->_eh->getEm()->getRepository(Models\User::class); $userRepo->updateTotalForUser($user); return new JsonResponse(['success' => true, 'id' => $game->getId(), 'hits' => $hits_array, 'achievements' => $achievement_ids], 200); }); /** * Expects a full game + hits in the request * add images as post multipart data. * filename: hit_id-imagetype.jpg (ie. 12341-brows.jpg) */ $controllers->post('/images', function (Request $request, Application $app) { $token = $app['security.token_storage']->getToken(); /* @var $user Models\User */ $user = $token->getUser(); if($request->files->count() < 1) { return new JsonResponse(['success' => false, 'message' => "no files given"], 400); } $valid_parts = Models\Hit::$FEATURES; foreach($valid_parts as $part) { $path = $app['file.path'] . "/hits/$part"; if(!is_dir($path)) { mkdir($path, 0777, true); } } foreach($request->files->all() as $id => $file) { /* @var $file \Symfony\Component\HttpFoundation\File\UploadedFile */ $filename = $file->getClientOriginalName(); $ext = $file->getClientOriginalExtension(); if($ext != 'jpg' || $file->getMimeType() != 'image/jpeg') { return new JsonResponse(['success' => false, 'message' => "Image not image/jpeg but " . $file->getMimeType() . " (".$ext.")" ], 400); } // name without .jpg $id_string = substr($filename, 0, -4); list($hit_id, $part) = \explode('-', $id_string); if(!in_array($part, $valid_parts)) { return new JsonResponse(['success' => false, 'message' => "unknown face part '$part'"], 400); } /* @var $em \Doctrine\ORM\EntityManager */ $em = $app['entity.manager']; /* @var $hitRepo Models\HitRepository */ $hitRepo = $em->getRepository(Models\Hit::class); /* @var $hit Models\Hit */ $hit = $hitRepo->find($hit_id); if(empty($hit) || $hit->getGame()->getUser()->getId() != $user->getId()) { return new JsonResponse(['success' => false, 'message' => "Hit not found."], 400); } $targetDirname = $hit->getFeatureDirname($part); if($targetDirname == null) { return new JsonResponse(['success' => false, 'message' => "Something went wrong. Invalid part?", "part" => (string) $part], 400); } $file->move($targetDirname, $hit->getId().'.jpg'); $hit->setHasImage(true); $em->persist($hit); } $em->flush(); return new JsonResponse(['success' => true, 'message' => "images saved."], 200); }); $controllers->get('/hits/{hitId}', function(Request $request, Application $app) { $hit = $request->attributes->get('hit'); if(empty($hit)) { return new JsonResponse(['message' => 'Hit not found'], 404); } return new CustomJsonResponse($hit, $app['serializer.json'], 200); }) ->bind('hit') ->convert('hit', function($hit, Request $request) use ($app){ return $app['entity.manager']->getRepository(Models\Hit::class)->find($request->attributes->get('hitId'));}); /** * Convert the hit to a mhpose file for use with MakeHuman */ $controllers->get('/hits/{hitId}.mhpose', function(Request $request, Application $app) { $hit = $request->attributes->get('hit'); if(empty($hit)) { return new JsonResponse(['message' => 'Hit not found'], 404); } $conversions = [ 'roll' => [], 'pitch' => [], 'yaw' => [], 'inter_ocular_distance' => [], // 'mouth_open' => ["JawDropStretched"], 'mouth_open' => ["UpperLipUp","lowerLipDown"], 'lip_press' => ["lowerLipUp"], 'brow_raise' => ["RightOuterBrowUp","LeftOuterBrowUp"], 'nose_wrinkler' => ["NasolabialDeepener"], 'lip_depressor' => ["MouthRightPullDown","MouthLeftPullDown"], 'brow_furrow' => ["NoseWrinkler"], 'attention' => [], // 'smile' => ["MouthRightPullUp","MouthLeftPullUp", "UpperLipUp","lowerLipDown"], 'smile' => ["UpperLipStretched"], 'inner_brow_raiser' => ["RightInnerBrowUp","LeftInnerBrowUp"], 'chin_raiser' => ["ChinForward"], 'smirk' => ["MouthLeftPullUp"], 'lip_suck' => ["UpperLipBackward","lowerLipBackward"], 'upper_lip_raiser' => ["UpperLipUp"], // 'lip_pucker' => ["LipsKiss"], 'lip_pucker' => ["UpperLipForward","lowerLipForward"], 'eye_closure' => ["RightUpperLidClosed","LeftUpperLidClosed"], 'engagement' => [], 'valence' => [], 'dimpler' => ["MouthRightPullSide","MouthLeftPullSide"], 'cheek_raise' => ["LeftCheekUp","RightCheekUp"], 'eye_widen' => ["RightUpperLidOpen","LeftUpperLidOpen"], 'jaw_drop' => ["JawDrop"], 'lid_tighten' => ["LeftLowerLidUp","RightLowerLidUp"], 'lip_stretch' => ["MouthRightPullSide","MouthLeftPullSide"], // <!-- platysma?? --> ]; $exprs = ""; foreach($hit->getExpressions()::$EXPRESSIONS as $expr) { $exprs .= " $expr {$hit->getExpressions()->getExpressionScore($expr)}%."; } $data = [ "license" => "AGPL3", "name" => "hit_{$hit->getId()}", "copyright" => "None", "tags" => [ "no tag", "expression" ], "author" => "Ruben van de Ven & player {$hit->getGame()->getUser()->getId()}", "unit_poses" => [], "homepage" => "http://emotionhero.com", "description" => "Hit {$hit->getId()} in the Emotion Hero game by player {$hit->getGame()->getUser()->getId()}. Points awarded for hit: {$hit->getScore()}. $exprs" ]; foreach($conversions as $from => $to) { $value = $hit->getExpressions()->getExpressionScore($from) / 100; foreach($to as $t) { $data['unit_poses'][$t] = isSet($data['unit_poses'][$t]) ? min(1,$data['unit_poses'][$t]+$value) : $value; // $data['description'] .= " $from => $t"; } } return new CustomJsonResponse($data, $app['serializer.json'], 200); }) ->bind('hit') ->convert('hit', function($hit, Request $request) use ($app){ return $app['entity.manager']->getRepository(Models\Hit::class)->find($request->attributes->get('hitId'));}); return $controllers; } }