update to pc

This commit is contained in:
silas
2018-05-23 19:04:35 +02:00
parent f7cbd1da71
commit 5cc52cf062
38 changed files with 2148 additions and 997 deletions

View File

@@ -11,6 +11,7 @@ import {
import './settings'
// import {ClockSite} from "../module/Application/pwa/js/site/ClockSite";
import {LevelSite} from "../module/Application/pwa/js/site/LevelSite";
import {SynchronizeSite} from "../module/Application/pwa/js/site/SynchronizeSite";
applyPolyfills();
@@ -39,7 +40,7 @@ app.addDefaultAction(Translator.generateChangeLanguageMenuAction());
// window["Translator"]["setLanguage"] = Translator.setLanguage;
InitPromise.resolve(app).then(function(){
app.start(LevelSite);
app.start(SynchronizeSite);
Translator.setLanguage("de");
});

View File

@@ -1,5 +1,13 @@
import { Fragment, Helper, Translator } from './pwa-lib.js';
class DelayPromise extends Promise{
constructor(delay) {
super((resolve) => {
setTimeout(resolve, delay);
});
}
}
class TabbedFragment extends Fragment {
constructor(site) {
super(site, 'pwaAssets/html/fragment/tabbedFragment.html');
@@ -65,4 +73,4 @@ class TabbedFragment extends Fragment {
}
}
export { TabbedFragment };
export { DelayPromise, TabbedFragment };

View File

@@ -3,14 +3,13 @@
namespace Application;
use Ainias\Core\Factory\Controller\ServiceActionControllerFactory;
use Application\Controller\AuthorController;
use Application\Controller\PwaController;
use Application\Controller\StoryController;
use Application\Controller\SyncController;
return array(
'controllers' => [
'factories' => [
Controller\IndexController::class => ServiceActionControllerFactory::class,
SyncController::class => ServiceActionControllerFactory::class
],
],
);

View File

@@ -2,6 +2,10 @@
namespace Application;
use Ainias\Core\Factory\Model\Manager\DefaultManagerFactory;
use Application\Model\Manager\AuthTokenManager;
use Application\Model\Manager\LevelManager;
use Application\Model\Manager\RatingManager;
use Application\Model\Manager\WordManager;
return array(
'service_manager' => array(
@@ -11,6 +15,10 @@ return array(
'translator' => 'MvcTranslator',
),
'factories' => array(
AuthTokenManager::class => DefaultManagerFactory::class,
LevelManager::class => DefaultManagerFactory::class,
RatingManager::class => DefaultManagerFactory::class,
WordManager::class => DefaultManagerFactory::class,
),
),
);

View File

@@ -0,0 +1,69 @@
<?php
namespace Application;
use Application\Controller\SyncController;
use Zend\Router\Http\Segment;
return array(
'router' => [
'routes' => [
'data' => [
'child_routes' => [
'wordRotator' => [
'type' => Segment::class,
'options' => [
'route' => '/wordRotator',
],
'child_routes' => [
// 'words' => [
// 'type' => Segment::class,
// 'options' => [
// 'route' => '/words',
// 'defaults' => [
// 'controller' => SyncController::class,
// 'action' => 'getWords',
// 'resource' => 'default',
// ]
// ],
// ],
'levels' => [
'type' => Segment::class,
'options' => [
'route' => '/levels',
'defaults' => [
'controller' => SyncController::class,
'action' => 'getLevels',
'resource' => 'default',
]
],
],
// 'getAuthToken' => [
// 'type' => Segment::class,
// 'options' => [
// 'route' => '/token',
// 'defaults' => [
// 'controller' => SyncController::class,
// 'action' => 'getAuthToken',
// 'resource' => 'default',
// ]
// ],
// ],
// 'rate' => [
// 'type' => Segment::class,
// 'options' => [
// 'route' => '/rate',
// 'defaults' => [
// 'controller' => SyncController::class,
// 'action' => 'rate',
// 'resource' => 'default',
// ]
// ],
// ],
],
],
],
],
],
],
);

View File

@@ -1,4 +0,0 @@
<div class = 'row'>
<div class = 'small-12 smedium-6 columns' data-translation="current-time">Current time:</div>
<div class = 'small-12 smedium-6 columns' id = 'current-time'>???</div>
</div>

View File

@@ -0,0 +1,3 @@
<div>
Sync
</div>

View File

@@ -0,0 +1,38 @@
import {Helper, MyDb} from "../../../../js/lib/pwa-lib";
export class WordRotatorDb extends MyDb {
static getInstance() {
if (Helper.isNull(WordRotatorDb.instance)) {
WordRotatorDb.instance = new WordRotatorDb();
}
return WordRotatorDb.instance;
}
constructor() {
super("wordRotator", 2);
}
upgrade(db, oldVersion, newVersion, e) {
if (Helper.isNull(oldVersion) || oldVersion < 1 && newVersion >= 1) {
let levelObjectStore = db.createObjectStore(WordRotatorDb.OBJECT_STORE.LEVEL, {"keyPath": "id"});
}
if (Helper.isNull(oldVersion) || oldVersion < 2 && newVersion >= 2) {
let levelObjectStore = e.target.transaction.objectStore(WordRotatorDb.OBJECT_STORE.LEVEL);
levelObjectStore.createIndex("played", ["played", "difficulty", "id"], {"unique": false});
}
};
async saveManyLevels(levels) {
return this.saveMany(levels, WordRotatorDb.OBJECT_STORE.LEVEL);
}
async loadLevel(levelId) {
return this.load(levelId, WordRotatorDb.OBJECT_STORE.LEVEL);
}
}
WordRotatorDb.OBJECT_STORE = {
LEVEL: "level",
};
WordRotatorDb.instance = null;

View File

@@ -1,21 +0,0 @@
import {DataManager} from "../../../../../js/lib/pwa-core";
import {AbstractSite} from "../../../../../js/lib/pwa-lib";
export class ClockSite extends AbstractSite
{
constructor(siteManager) {
super(siteManager, "html/application/clock.html");
}
onConstruct(args) {
this.setTitle("clock");
return super.onConstruct(args);
}
onFirstStart(){
DataManager.load("clock").then(function(data){
document.getElementById("current-time").innerText = data.result.date;
});
}
}

View File

@@ -31,7 +31,6 @@ export class LevelSite extends AbstractSite{
let templateContainer = new TemplateContainer(leafSegmentTemplate, parentSegmentTemplate, rowSegmentTemplate);
let level = new SimpleLevel(templateContainer);
level.setWords([
"Dynamo",
@@ -39,8 +38,17 @@ export class LevelSite extends AbstractSite{
]);
level.setStartRotations([0,90,180]);
level.getWonPromise().then(()=>{
console.log("has won");
});
level.createSegments();
level.getRootSegment()._updateElement();
this.findBy("#level").appendChild(level.getRootSegment().getElement());
}
async nextLevel()
{
}
}

View File

@@ -0,0 +1,58 @@
import {AbstractSite, Helper} from "../../../../../js/lib/pwa-lib";
import {DataManager} from "../../../../../js/lib/pwa-core";
import {WordRotatorDb} from "../WordRotatorDb";
import {LevelSite} from "./LevelSite";
export class SynchronizeSite extends AbstractSite {
constructor(siteManager) {
super(siteManager, "html/application/sync.html");
}
async onConstruct(args) {
let res = await super.onConstruct(args);
await this.loadLevels();
return res;
}
onFirstStart() {
super.onFirstStart();
this.startSite(LevelSite);
}
async loadLevels() {
const dateLastSync = Helper.nonNull(localStorage.getItem("date-last-sync"), 0);
const db = WordRotatorDb.getInstance();
let newLastSync = null;
let maxRuns = 1;
let levelPromises = [];
for (let run = 0; run < maxRuns; run++) {
let res = await DataManager.load("wordRotator/levels" + DataManager.buildQuery({
"currentRun": run,
"dateLastSync": dateLastSync
}));
if (!res["success"]) {
break;
}
res = res["result"];
newLastSync = Helper.nonNull(newLastSync, res["currentSyncDate"]);
maxRuns = res["maxRuns"];
let levels = res["levels"];
for (let i = 0, n = levels.length; i < n; i++) {
let currentLevel = levels[i];
levelPromises.push(db.loadLevel(levels[i]["id"]).then(level => {
currentLevel["played"] = (Helper.nonNull(Helper.nonNull(level, {}).played, false));
return currentLevel;
}));
}
}
let levels = await Promise.all(levelPromises);
await db.saveManyLevels(levels);
localStorage.setItem("date-last-sync", newLastSync);
}
}

View File

@@ -7,6 +7,28 @@ export class Level {
this.words = [];
this.startRotations = [];
this.templateContainer = templateContainer;
this.hasWon = false;
this.wonResolver = null;
this.giveUpResolver = null;
const self = this;
this.wonPromise = new Promise((resolve, reject) => {
self.wonResolver = resolve;
self.giveUpResolver = reject;
});
}
getLevel()
{
return this;
}
setRootSegment(rootSegment)
{
this.rootSegment = rootSegment;
this.rootSegment.setParent(this);
}
setWords(words)
@@ -22,8 +44,25 @@ export class Level {
this.startRotations = rotations;
}
hasWon() {
return this.rootSegment.isSolved();
getHasWon()
{
return this.hasWon;
}
checkHasWon(delayPromise) {
if (this.rootSegment.isSolved()){
this.hasWon = true;
const self = this;
delayPromise.then(()=>{
self.wonResolver(true);
});
return true;
}
return false;
}
getWonPromise(){
return this.wonPromise;
}
getRootSegment(){

View File

@@ -0,0 +1,31 @@
import {Level} from "./Level";
import {RowSegment} from "../Segment/RowSegment";
import {ParentSegment} from "../Segment/ParentSegment";
export class RowLevel extends Level {
constructor(container, wordLength) {
super(container);
this.wordLength = wordLength;
}
createSegments() {
if (this.words.length >= 2 && this.words[0].length >= this.wordLength && this.words[1].length >= this.wordLength) {
let leafsWordOne = Level._createLeafsForWord(this.words[0], this.templateContainer.copyLeafTemplate());
let leafsWordTwo = Level._createLeafsForWord(this.words[1], this.templateContainer.copyLeafTemplate());
let rootSegment = new RowSegment(this.templateContainer.copyRowTemplate());
for (let i = 0, n = this.wordLength / 2; i < n; i++) {
let parent = new ParentSegment(this.templateContainer.copyParentTemplate());
parent.addChild(leafsWordOne[2 * i]);
parent.addChild(leafsWordOne[2 * i + 1]);
parent.addChild(leafsWordTwo[2 * i]);
parent.addChild(leafsWordTwo[2 * i + 1]);
rootSegment.addChild(parent);
}
rootSegment.applyRotations(this.startRotations);
this.setRootSegment(rootSegment)
}
}
}

View File

@@ -1,40 +1,8 @@
import {Level} from "./Level";
import {ParentSegment} from "../Segment/ParentSegment";
import {RowSegment} from "../Segment/RowSegment";
import {RowLevel} from "./RowLevel";
export class SimpleLevel extends Level{
export class SimpleLevel extends RowLevel{
createSegments() {
if (this.words.length >= 2 && this.words[0].length >= 6 &&this.words[1].length >= 6){
let leafsWordOne = Level._createLeafsForWord(this.words[0], this.templateContainer.copyLeafTemplate());
let leafsWordTwo = Level._createLeafsForWord(this.words[1], this.templateContainer.copyLeafTemplate());
let segmentOne = new ParentSegment(this.templateContainer.copyParentTemplate());
let segmentTwo = new ParentSegment(this.templateContainer.copyParentTemplate());
let segmentThree = new ParentSegment(this.templateContainer.copyParentTemplate());
segmentOne.addChild(leafsWordOne[0]);
segmentOne.addChild(leafsWordOne[1]);
segmentOne.addChild(leafsWordTwo[0]);
segmentOne.addChild(leafsWordTwo[1]);
segmentTwo.addChild(leafsWordOne[2]);
segmentTwo.addChild(leafsWordOne[3]);
segmentTwo.addChild(leafsWordTwo[2]);
segmentTwo.addChild(leafsWordTwo[3]);
segmentThree.addChild(leafsWordOne[4]);
segmentThree.addChild(leafsWordOne[5]);
segmentThree.addChild(leafsWordTwo[4]);
segmentThree.addChild(leafsWordTwo[5]);
this.rootSegment = new RowSegment(this.templateContainer.copyRowTemplate());
this.rootSegment.addChild(segmentOne);
this.rootSegment.addChild(segmentTwo);
this.rootSegment.addChild(segmentThree);
this.rootSegment.applyRotations(this.startRotations);
}
constructor(container) {
super(container, 6);
}
}

View File

@@ -1,4 +1,5 @@
import {Segment} from "./Segment";
import {DelayPromise} from "../../../../../../js/lib/pwa-assets";
export class ParentSegment extends Segment {
constructor(element) {
@@ -7,11 +8,17 @@ export class ParentSegment extends Segment {
this.class = "rotate-0";
}
rotate() {
this.rotation += 90;
this.rotation %= 360;
async rotate() {
if (!this.getLevel().getHasWon()) {
this.rotation += 90;
this.rotation %= 360;
this._updateRotationClass()
this._updateRotationClass();
this.getLevel().checkHasWon(new Promise((resolve, reject)=>{
this.element.addEventListener("animationend", resolve);
}));
return new DelayPromise(250);
}
}
applyRotations(rotations) {
@@ -42,6 +49,7 @@ export class ParentSegment extends Segment {
addChild(child) {
this.children.push(child);
child.setParent(this);
this._updateElement();
}
@@ -49,26 +57,10 @@ export class ParentSegment extends Segment {
// this.style.transform = "rotate("+this.rotation+"deg)";
this.element.classList.remove(this.class);
this.class = "rotate-" + this.rotation;
if (this.class === "rotate-0")
{
if (this.class === "rotate-0") {
this.class = "rotate-360";
}
this.element.classList.add(this.class);
// if (this.rotation === 0) {
// const self = this;
// self.element.classList.add("no-transition");
//
// setTimeout(() => {
// if (self.class === "rotate-0") {
// requestAnimationFrame(()=>{
//
// self.element.classList.remove("rotate-0");
// self.element.classList.remove("no-transition");
// });
// }
// }, 250);
// }
}
_updateElement() {

View File

@@ -4,13 +4,29 @@ export class Segment{
constructor(element){
this.rotation = 0;
this.element = element;
this.parent = null;
}
setParent(parent)
{
this.parent = parent;
}
getLevel()
{
if (this.parent!==null)
{
return this.parent.getLevel();
}
}
isSolved(){
return (this.rotation === 0);
}
rotate(){};
async rotate(){
return Promise.resolve();
};
_updateElement(){};

View File

@@ -0,0 +1,219 @@
<?php
/**
* Created by PhpStorm.
* User: silas
* Date: 10.11.16
* Time: 11:59
*/
namespace Application\Controller;
use Ainias\Core\Controller\JsonController;
use Ainias\Core\Controller\ServiceActionController;
use Ainias\Core\Module;
use Application\Model\AuthToken;
use Application\Model\Level;
use Application\Model\Manager\AuthTokenManager;
use Application\Model\Manager\LevelManager;
use Application\Model\Manager\RatingManager;
use Application\Model\Manager\WordManager;
use Application\Model\Rating;
use Zend\Log\Logger;
use Zend\View\Model\ViewModel;
class SyncController extends JsonController
{
const SYNC_MAX_WORDS = 300;
const SYNC_MAX_LEVELS = 300;
const MAX_RATINGS_PER_LEVEL = 2;
// public function getAuthTokenAction()
// {
// if (!$this->getRequest()->isPost()) {
// return $this->triggerDispatchError(404);
// }
//
// /** @var AuthTokenManager $authTokenManager */
// $authTokenManager = $this->get(AuthTokenManager::class);
//
// $token = new AuthToken();
// $name = $this->getRequest()->getPost("name");
// if (trim($name) == "") {
// return $this->triggerDispatchError(400);
// }
// $token->setName($name);
// $token->setToken($authTokenManager->generateNewAuthToken());
// $authTokenManager->save($token);
//
// $this->layout("layout/ajaxData");
// $viewModel = new ViewModel();
// $viewModel->setTemplate("ajax/json");
// $viewModel->setVariable("json", [
// "result" => true,
// "data" => [
// "authToken" => $token->getToken(),
// ],
// ]);
// return $viewModel;
// }
// public function getWordsAction()
// {
// $request = $this->getRequest();
// if (!$request->isPost()) {
// return $this->triggerDispatchError();
// }
//
// $this->getEventManager()->trigger(Module::EVENT_LOG, null, array(
// "message" => "Synchronized Words: " . $request->toString(),
// "level" => Logger::INFO
// ));
//
// $currentRun = (int)$request->getPost("currentRun", null);
// $dateLastSync = $request->getPost("dateLastSync", null);
// if ($dateLastSync != null) {
// try {
// $dateLastSync = \DateTime::createFromFormat(self::DATETIME_SYNC_FORMAT, $dateLastSync);
// } catch (\Throwable $t) {
// $dateLastSync = null;
// }
// }
//
// /** @var WordManager $wordManager */
// $wordManager = $this->get(WordManager::class);
// $newDate = new \DateTime();
// $words = $wordManager->wordsToArray($wordManager->findNewerThan($dateLastSync, $currentRun));
// $numberWordsToSync = $wordManager->countNewerThan($dateLastSync);
//
// $this->layout("layout/ajaxData");
// $viewModel = new ViewModel();
// $viewModel->setTemplate("ajax/json");
// $viewModel->setVariable("json", [
// "result" => true,
// "data" => [
// "countWords" => $numberWordsToSync,
// "currentSyncDate" => $newDate->format(self::DATETIME_SYNC_FORMAT),
// "currentRun" => $currentRun,
// "maxRuns" => ceil($numberWordsToSync / SyncController::SYNC_MAX_WORDS),
// "words" => $words,
// ],
// ]);
// return $viewModel;
// }
public function getLevelsAction()
{
$request = $this->getRequest();
$currentRun = (int)$request->getQuery("currentRun", null);
$dateLastSync = $request->getQuery("dateLastSync", null);
try {
$dateLastSync = new \DateTime("@" . $dateLastSync);
} catch (\Throwable $e) {
$dateLastSync = new \DateTime();
}
/** @var LevelManager $levelManager */
$levelManager = $this->get(LevelManager::class);
$newDate = new \DateTime();
$levels = $levelManager->levelsToArray($levelManager->findNewerThan($dateLastSync, $currentRun));
$numberLevelsToSync = $levelManager->countNewerThan($dateLastSync);
return [
"countLevels" => $numberLevelsToSync,
"currentSyncDate" => $newDate->getTimestamp(),
"currentRun" => $currentRun,
"maxRuns" => ceil($numberLevelsToSync / SyncController::SYNC_MAX_LEVELS),
"levels" => $levels,
];
}
// public function rateAction()
// {
// $request = $this->getRequest();
// if (!$request->isPost()) {
// return $this->triggerDispatchError(404);
// }
//
// $this->getEventManager()->trigger(Module::EVENT_LOG, null, array(
// "message" => "Synchronized Level: " . $request->toString(),
// "level" => Logger::INFO
// ));
//
// /** @var AuthTokenManager $authTokenManager */
// $authTokenManager = $this->get(AuthTokenManager::class);
// $authToken = $authTokenManager->findOneByToken($request->getPost("authToken"));
//
// if ($authToken == null) {
// return $this->triggerDispatchError(403);
// }
//
// /** @var RatingManager $ratingManager */
// $ratingManager = $this->get(RatingManager::class);
//
// /** @var LevelManager $levelManager */
// $levelManager = $this->get(LevelManager::class);
// $levelsArray = json_decode($request->getPost("levels"), true);
//
// $levels = [];
// foreach ($levelsArray as $levelArray) {
// $levelManager->beginTransaction();
//
// $words = json_encode($levelArray["words"]);
// $rotations = json_encode($levelArray["rotations"]);
// $level = $levelManager->findOneBy([
// "words" => $words,
// "positions" => $rotations,
// "renderer" => $levelArray["rendererType"],
// "lang" => $levelArray["language"],
// ]);
// if ($level == null) {
// $level = new Level();
// $level->setDeleted(false);
// $level->setWords($words);
// $level->setPositions($rotations);
// $level->setRenderer($levelArray["rendererType"]);
// $level->setLang($levelArray["language"]);
// $level->setLastUpdated(new \DateTime());
// $level->setDeleted(false);
// $level->setDifficulty($levelArray["difficulty"]);
// $levelManager->save($level);
// }
// $rating = $ratingManager->findOneBy([
// "level" => $level,
// "authToken" => $authToken,
// ]);
//
// if ($rating == null) {
// $rating = new Rating();
// $rating->setAuthToken($authToken);
// $rating->setLevel($level);
// $level->getRatings()->add($rating);
// }
//
// $ratingScore = $levelArray["rating"];
// $rating->setRating($ratingScore);
//
// if ($level->getRatings()->count() >= self::MAX_RATINGS_PER_LEVEL || $ratingScore < 0) {
// $level->setDeleted(true);
// $level->setLastUpdated(new \DateTime());
//// $levelManager->save($level);
// }
//
// $ratingManager->save($rating);
// $levelManager->endTransaction();
// $levels[] = $level;
// }
// $this->layout("layout/ajaxData");
// $viewModel = new ViewModel();
// $viewModel->setTemplate("ajax/json");
// $viewModel->setVariable("json", [
// "result" => true,
// "data" => [
// "levels" => $levelManager->levelsToArray($levels),
// ],
// ]);
// return $viewModel;
// }
}

View File

@@ -0,0 +1,134 @@
<?php
namespace Application\Model;
use Ainias\Core\Model\StandardModel;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/** @ORM\Entity(repositoryClass="Application\Model\Repository\AuthTokenRepository")
*/
class AuthToken extends StandardModel
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @ORM\Column(type="text")
* @var string
*/
protected $token;
/**
* @ORM\Column(type="text")
* @var string
*/
protected $name;
/**
* @ORM\OneToMany(targetEntity="\Application\Model\Rating", mappedBy="authToken", cascade="all")
* @var Rating[]
*/
protected $ratings;
/**
* @ORM\Column(type="datetime", nullable=true)
* @var \DateTime
*/
protected $creationDate;
/**
* AuthToken constructor.
*/
public function __construct()
{
$this->id = null;
$this->token = null;
$this->ratings = new ArrayCollection();
$this->creationDate = new \DateTime();
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return string
*/
public function getToken()
{
return $this->token;
}
/**
* @param string $token
*/
public function setToken($token)
{
$this->token = $token;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return Rating[]
*/
public function getRatings()
{
return $this->ratings;
}
/**
* @param Rating[] $ratings
*/
public function setRatings($ratings)
{
$this->ratings = $ratings;
}
/**
* @return \DateTime
*/
public function getCreationDate()
{
return $this->creationDate;
}
/**
* @param \DateTime $creationDate
*/
public function setCreationDate($creationDate)
{
$this->creationDate = $creationDate;
}
}

View File

@@ -0,0 +1,222 @@
<?php
namespace Application\Model;
use Ainias\Core\Model\StandardModel;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="Application\Model\Repository\LevelRepository")
* @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="levelCore",columns={"words", "positions", "renderer", "lang"})})
*/
class Level extends StandardModel
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @ORM\Column(type="string", length=255)
* @var string
*/
protected $words;
/**
* @ORM\Column(type="string", length=255)
* @var string
*/
protected $positions;
/**
* @ORM\Column(type="integer")
* @var int
*/
protected $renderer;
/**
* @ORM\Column(type="datetime")
* @var \DateTime
*/
protected $lastUpdated;
/**
* @ORM\Column(type="integer")
* @var int
*/
protected $lang;
/**
* @ORM\Column(type="boolean")
* @var bool
*/
protected $deleted;
/**
* @ORM\Column(type="integer")
* @var int
*/
protected $difficulty;
/**
* @ORM\OneToMany(targetEntity="\Application\Model\Rating", mappedBy="level", cascade="all")
* @var Rating[]
*/
protected $ratings;
/**
* Level constructor.
*/
public function __construct()
{
$this->id = null;
$this->ratings = new ArrayCollection();
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return string
*/
public function getWords()
{
return $this->words;
}
/**
* @param string $words
*/
public function setWords($words)
{
$this->words = $words;
}
/**
* @return string
*/
public function getPositions()
{
return $this->positions;
}
/**
* @param string $positions
*/
public function setPositions($positions)
{
$this->positions = $positions;
}
/**
* @return Rating[]|ArrayCollection
*/
public function getRatings()
{
return $this->ratings;
}
/**
* @param Rating[] $ratings
*/
public function setRatings($ratings)
{
$this->ratings = $ratings;
}
/**
* @return int
*/
public function getRenderer()
{
return $this->renderer;
}
/**
* @param int $renderer
*/
public function setRenderer($renderer)
{
$this->renderer = $renderer;
}
/**
* @return \DateTime
*/
public function getLastUpdated()
{
return $this->lastUpdated;
}
/**
* @param \DateTime $lastUpdated
*/
public function setLastUpdated($lastUpdated)
{
$this->lastUpdated = $lastUpdated;
}
/**
* @return int
*/
public function getLang()
{
return $this->lang;
}
/**
* @param int $lang
*/
public function setLang($lang)
{
$this->lang = $lang;
}
/**
* @return boolean
*/
public function isDeleted()
{
return $this->deleted;
}
/**
* @param boolean $deleted
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
}
/**
* @return int
*/
public function getDifficulty()
{
return $this->difficulty;
}
/**
* @param int $difficulty
*/
public function setDifficulty($difficulty)
{
$this->difficulty = $difficulty;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Application\Model\Manager;
use Ainias\Core\Model\Manager\StandardManager;
use Application\Model\Repository\AuthTokenRepository;
use Application\Model\AuthToken;
class AuthTokenManager extends StandardManager
{
/** @var AuthTokenRepository */
protected $repository;
public function __construct(AuthTokenRepository $repository, AuthToken $entity = null)
{
$this->repository = $repository;
parent::__construct($repository, $entity);
}
/**
* @param int $id
* @return AuthToken
*/
public function getEntityById($id)
{
return parent::getEntityById($id);
}
public function generateNewAuthToken()
{
do {
$token = "";
for ($i = 0; $i < 256; $i++) {
$rand = rand(0, 61);
if ($rand > 9) {
if ($rand > 35) {
$rand += ord('a') - 36;
} else {
$rand += ord('A') - 10;
}
$rand = chr($rand);
}
$token .= $rand;
}
} while($this->findOneByToken($token) != null);
return $token;
}
/**
* @param $token
* @return null|AuthToken
*/
public function findOneByToken($token)
{
return $this->findOneBy(["token" => $token]);
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Application\Model\Manager;
use Ainias\Core\Model\Manager\StandardManager;
use Application\Model\Repository\LevelRepository;
use Application\Model\Level;
class LevelManager extends StandardManager
{
/** @var LevelRepository */
protected $repository;
public function __construct(LevelRepository $repository, Level $entity = null)
{
$this->repository = $repository;
parent::__construct($repository, $entity);
}
/**
* @param int $id
* @return Level
*/
public function getEntityById($id)
{
return parent::getEntityById($id);
}
public function beginTransaction()
{
$this->repository->beginTransaction();
}
public function endTransaction()
{
$this->repository->endTransaction();
}
/**
* @param \DateTime $dateTime
* @param int $offset
* @return array
*/
public function findNewerThan($dateTime, $offset = 0)
{
return $this->repository->findNewerThanDate($dateTime, $offset);
}
public function countNewerThan($dateTime)
{
return $this->repository->countNewerThanDate($dateTime);
}
public function levelToArray(Level $level)
{
return [
"id" => $level->getId(),
"words" => json_decode($level->getWords()),
"rotations"=> json_decode($level->getPositions()),
"lastUpdated" => $level->getLastUpdated()->format("Y-m-d"),
"language" => $level->getLang(),
"rendererType" => $level->getRenderer(),
"difficulty" => $level->getDifficulty(),
"deleted" => $level->isDeleted(),
];
}
public function levelsToArray($levels)
{
$returnArray = [];
/** @var Level $level */
foreach ($levels as $level)
{
$returnArray[] = $this->levelToArray($level);
}
return $returnArray;
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Application\Model\Manager;
use Ainias\Core\Model\Manager\StandardManager;
use Application\Model\Repository\RatingRepository;
use Application\Model\Rating;
class RatingManager extends StandardManager
{
/** @var RatingRepository */
protected $repository;
public function __construct(RatingRepository $repository, Rating $entity = null)
{
$this->repository = $repository;
parent::__construct($repository, $entity);
}
/**
* @param int $id
* @return Rating
*/
public function getEntityById($id)
{
return parent::getEntityById($id);
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace Application\Model\Manager;
use Ainias\Core\Model\Manager\StandardManager;
use Application\Controller\SyncController;
use Application\Model\Repository\WordRepository;
use Application\Model\Word;
class WordManager extends StandardManager
{
const ACTION_CHECKED = 1;
const ACTION_UNSURE = 2;
const ACTION_DELETED = 3;
/** @var WordRepository */
protected $repository;
public function __construct(WordRepository $repository, Word $entity = null)
{
$this->repository = $repository;
parent::__construct($repository, $entity);
}
/**
* @param Word $word
* @param $action
* @return mixed
*/
public static function doAction($word, $action)
{
switch($action)
{
case self::ACTION_UNSURE:
{
$word->setChecked(-1);
break;
}
case self::ACTION_CHECKED:
{
$word->setChecked(1);
$word->setDeleted(false);
break;
}
case self::ACTION_DELETED:
{
$word->setChecked(1);
$word->setDeleted(true);
break;
}
}
$word->setLastUpdated(new \DateTime());
return $word;
}
/**
* @param int $id
* @return Word
*/
public function getEntityById($id)
{
return parent::getEntityById($id);
}
/**
* @param \DateTime $dateTime
* @param int $offset
* @return array
*/
public function findNewerThan($dateTime, $offset = 0)
{
return $this->repository->findNewerThanDate($dateTime, $offset);
}
public function countNewerThan($dateTime)
{
return $this->repository->countNewerThanDate($dateTime);
}
public function wordToArray(Word $word)
{
return [
"id" => $word->getId(),
"word" => $word->getWord(),
// "created" => $word->getCreated()->format(SyncController::DATETIME_SYNC_FORMAT),
// "lastUpdated" => $word->getLastUpdated()->format(SyncController::DATETIME_SYNC_FORMAT),
"language" => $word->getLang(),
"deleted" => $word->isDeleted(),
];
}
public function wordsToArray($words)
{
$returnArray = [];
/** @var Word $word */
foreach ($words as $word)
{
$returnArray[] = $this->wordToArray($word);
}
return $returnArray;
}
public function getRandomWordNotChecked($minLength = 0, $maxLength = 100)
{
return $this->repository->getRandomWordNotChecked($minLength, $maxLength);
}
public function countNotChecked($minLength = 0, $maxLength = 100)
{
return $this->repository->countNotChecked($minLength, $maxLength);
}
public function countDeleted($minLength = 0, $maxLength = 100)
{
return $this->repository->countDeleted($minLength, $maxLength);
}
public function countChecked($minLength = 0, $maxLength = 100)
{
return $this->repository->countChecked($minLength, $maxLength);
}
public function countUnsure($minLength = 0, $maxLength = 100)
{
return $this->repository->countUnsure($minLength, $maxLength);
}
}

View File

@@ -0,0 +1,112 @@
<?php
namespace Application\Model;
use Ainias\Core\Model\StandardModel;
use Doctrine\ORM\Mapping as ORM;
/** @ORM\Entity(repositoryClass="Application\Model\Repository\RatingRepository")
*/
class Rating extends StandardModel
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="\Application\Model\Level", inversedBy="ratings")
* @ORM\JoinColumn(name="levelId", referencedColumnName="id")
* @var Level
*/
protected $level;
/**
* @ORM\ManyToOne(targetEntity="\Application\Model\AuthToken", inversedBy="ratings")
* @ORM\JoinColumn(name="authTokenId", referencedColumnName="id")
* @var AuthToken
*/
protected $authToken;
/**
* @ORM\Column(type="integer")
* @var int
*/
protected $rating;
/**
* Rating constructor.
*/
public function __construct()
{
$this->id = null;
$this->authToken = null;
$this->level = null;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return Level
*/
public function getLevel()
{
return $this->level;
}
/**
* @param Level $level
*/
public function setLevel($level)
{
$this->level = $level;
}
/**
* @return AuthToken
*/
public function getAuthToken()
{
return $this->authToken;
}
/**
* @param AuthToken $authToken
*/
public function setAuthToken($authToken)
{
$this->authToken = $authToken;
}
/**
* @return int
*/
public function getRating()
{
return $this->rating;
}
/**
* @param int $rating
*/
public function setRating($rating)
{
$this->rating = $rating;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Application\Model\Repository;
use Ainias\Core\Model\Repository\StandardRepository;
class AuthTokenRepository extends StandardRepository
{
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Application\Model\Repository;
use Ainias\Core\Model\Repository\StandardRepository;
use Application\Controller\SyncController;
use Application\Model\Level;
class LevelRepository extends StandardRepository
{
/**
* @param \DateTime $date
* @param int $offset
* @return array
*/
public function findNewerThanDate($date, $offset = 0)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("l")->from(Level::class, "l");
if ($date != null)
{
$queryBuilder->andWhere($queryBuilder->expr()->gte("l.lastUpdated", ":fromDate"));
$queryBuilder->setParameter("fromDate", $date->format("Y-m-d H:i:00"));
}
$queryBuilder->setMaxResults(SyncController::SYNC_MAX_LEVELS);
$queryBuilder->setFirstResult($offset * SyncController::SYNC_MAX_LEVELS);
return $queryBuilder->getQuery()->getResult();
}
public function countNewerThanDate($date)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("COUNT(l.id) as number")->from(Level::class, "l");
if ($date != null)
{
$queryBuilder->andWhere($queryBuilder->expr()->gte("l.lastUpdated", ":fromDate"));
$queryBuilder->setParameter("fromDate", $date->format("Y-m-d H:i:00"));
}
return $queryBuilder->getQuery()->getSingleScalarResult();
}
public function beginTransaction()
{
$this->_em->getConnection()->beginTransaction();
}
public function endTransaction()
{
try{
$this->_em->getConnection()->commit();
} catch (\Exception $e)
{
$this->_em->getConnection()->rollBack();
throw $e;
}
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Application\Model\Repository;
use Ainias\Core\Model\Repository\StandardRepository;
class RatingRepository extends StandardRepository
{
}

View File

@@ -0,0 +1,129 @@
<?php
namespace Application\Model\Repository;
use Ainias\Core\Model\Repository\StandardRepository;
use Doctrine\ORM\Internal\HydrationCompleteHandler;
use Doctrine\ORM\Query;
use Application\Controller\SyncController;
use Application\Model\Word;
class WordRepository extends StandardRepository
{
/**
* @param \DateTime $date
* @param int $offset
* @return array
*/
public function findNewerThanDate($date, $offset = 0)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("w")->from(Word::class, "w");
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.checked", "1"));
if ($date != null)
{
$queryBuilder->andWhere($queryBuilder->expr()->gte("w.lastUpdated", ":fromDate"));
$queryBuilder->setParameter("fromDate", $date->format("Y-m-d"));
}
$queryBuilder->setMaxResults(SyncController::SYNC_MAX_WORDS);
$queryBuilder->setFirstResult($offset * SyncController::SYNC_MAX_WORDS);
return $queryBuilder->getQuery()->getResult();
}
public function countNewerThanDate($date)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("COUNT(w.id) as number")->from(Word::class, "w");
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.checked", "1"));
if ($date != null)
{
$queryBuilder->andWhere($queryBuilder->expr()->gte("w.lastUpdated", ":fromDate"));
$queryBuilder->setParameter("fromDate", $date->format("Y-m-d"));
}
return $queryBuilder->getQuery()->getSingleScalarResult();
}
public function getRandomWordNotChecked($minLength = 0, $maxLength = 100)
{
$numberWords = $this->countNotChecked($minLength, $maxLength);
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("w")->from(Word::class, "w");
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.checked", "0"));
$queryBuilder->andWhere($queryBuilder->expr()->gte($queryBuilder->expr()->length("w.word"), ":minLength"));
$queryBuilder->andWhere($queryBuilder->expr()->lte($queryBuilder->expr()->length("w.word"), ":maxLength"));
$queryBuilder->setParameter("minLength", $minLength);
$queryBuilder->setParameter("maxLength", $maxLength);
$queryBuilder->setMaxResults(1);
$queryBuilder->setFirstResult(rand(0,$numberWords));
$res = $queryBuilder->getQuery()->getResult();
if (is_array($res))
{
return $res[0];
}
return $res;
}
public function countNotChecked($minLength = 0, $maxLength = 100)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("COUNT(w.id)")->from(Word::class, "w");
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.checked", "0"));
$queryBuilder->andWhere($queryBuilder->expr()->gte($queryBuilder->expr()->length("w.word"), ":minLength"));
$queryBuilder->andWhere($queryBuilder->expr()->lte($queryBuilder->expr()->length("w.word"), ":maxLength"));
$queryBuilder->setParameter("minLength", $minLength);
$queryBuilder->setParameter("maxLength", $maxLength);
$queryBuilder->setMaxResults(1);
return $queryBuilder->getQuery()->getSingleScalarResult();
}
public function countDeleted($minLength, $maxLength)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("COUNT(w.id)")->from(Word::class, "w");
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.checked", "1"));
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.deleted", "1"));
$queryBuilder->andWhere($queryBuilder->expr()->gte($queryBuilder->expr()->length("w.word"), ":minLength"));
$queryBuilder->andWhere($queryBuilder->expr()->lte($queryBuilder->expr()->length("w.word"), ":maxLength"));
$queryBuilder->setParameter("minLength", $minLength);
$queryBuilder->setParameter("maxLength", $maxLength);
$queryBuilder->setMaxResults(1);
return $queryBuilder->getQuery()->getSingleScalarResult();
}
public function countChecked($minLength, $maxLength)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("COUNT(w.id)")->from(Word::class, "w");
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.checked", "1"));
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.deleted", "0"));
$queryBuilder->andWhere($queryBuilder->expr()->gte($queryBuilder->expr()->length("w.word"), ":minLength"));
$queryBuilder->andWhere($queryBuilder->expr()->lte($queryBuilder->expr()->length("w.word"), ":maxLength"));
$queryBuilder->setParameter("minLength", $minLength);
$queryBuilder->setParameter("maxLength", $maxLength);
$queryBuilder->setMaxResults(1);
return $queryBuilder->getQuery()->getSingleScalarResult();
}
public function countUnsure($minLength, $maxLength)
{
$queryBuilder = $this->_em->createQueryBuilder();
$queryBuilder->select("COUNT(w.id)")->from(Word::class, "w");
$queryBuilder->andWhere($queryBuilder->expr()->eq("w.checked", "-1"));
$queryBuilder->andWhere($queryBuilder->expr()->gte($queryBuilder->expr()->length("w.word"), ":minLength"));
$queryBuilder->andWhere($queryBuilder->expr()->lte($queryBuilder->expr()->length("w.word"), ":maxLength"));
$queryBuilder->setParameter("minLength", $minLength);
$queryBuilder->setParameter("maxLength", $maxLength);
$queryBuilder->setMaxResults(1);
return $queryBuilder->getQuery()->getSingleScalarResult();
}
}

View File

@@ -0,0 +1,188 @@
<?php
namespace Application\Model;
use Ainias\Core\Model\StandardModel;
use Doctrine\ORM\Mapping as ORM;
/** @ORM\Entity(repositoryClass="Application\Model\Repository\WordRepository")
*/
class Word extends StandardModel
{
//Level:
/*
* Ansage
* Termin
*
*/
CONST LANGUAGE_DE = 1;
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @ORM\Column(type="text")
* @var string
*/
protected $word;
/**
* @ORM\Column(type="datetime")
* @var \DateTime
*/
protected $created;
/** @ORM\Column(type="datetime")
* @var \DateTime
*/
protected $lastUpdated;
/**
* @ORM\Column(type="boolean")
* @var bool
*/
protected $deleted;
/**
* @ORM\Column(type="integer")
* @var int
*/
protected $lang;
/**
* @ORM\Column(type="integer")
* @var int
*/
protected $checked;
/**
* Word constructor.
*/
public function __construct()
{
$this->id = null;
$this->created = new \DateTime();
$this->lastUpdated = new \DateTime();
$this->deleted = false;
$this->lang = self::LANGUAGE_DE;
$this->word = "";
$this->checked = -1;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return string
*/
public function getWord()
{
return $this->word;
}
/**
* @param string $word
*/
public function setWord($word)
{
$this->word = $word;
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @param \DateTime $created
*/
public function setCreated($created)
{
$this->created = $created;
}
/**
* @return \DateTime
*/
public function getLastUpdated()
{
return $this->lastUpdated;
}
/**
* @param \DateTime $lastUpdated
*/
public function setLastUpdated($lastUpdated)
{
$this->lastUpdated = $lastUpdated;
}
/**
* @return boolean
*/
public function isDeleted()
{
return $this->deleted;
}
/**
* @param boolean $deleted
*/
public function setDeleted($deleted)
{
$this->deleted = $deleted;
}
/**
* @return int
*/
public function getLang()
{
return $this->lang;
}
/**
* @param int $lang
*/
public function setLang($lang)
{
$this->lang = $lang;
}
/**
* @return int
*/
public function getChecked()
{
return $this->checked;
}
/**
* @param int $checked
*/
public function setChecked($checked)
{
$this->checked = $checked;
}
}

View File

@@ -58,6 +58,7 @@ $rotationDegrees: (90 180 270 360);
text-align: center;
position: relative;
transition: .0s;
user-select: none;
&.segment-row {
display: block;
@@ -66,8 +67,9 @@ $rotationDegrees: (90 180 270 360);
}
&.segment-leaf {
min-width: 15px;
height: 30px;
min-width: 17px;
height: 34px;
line-height: 34px;
}
&.segment-parent {