diff --git a/.jshintrc b/.jshintrc index a4bf3293..757b37a9 100644 --- a/.jshintrc +++ b/.jshintrc @@ -14,7 +14,7 @@ "node": true, // Allow ES6. - "esversion": 6, + "esversion": 7, /* * ENFORCING OPTIONS diff --git a/README.md b/README.md index 8cc71042..bdf15843 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ Mapping tool for [*EVE ONLINE*](https://www.eveonline.com) - [wiki](https://github.com/exodus4d/pathfinder/wiki) - Developer [Slack](https://slack.com) chat: - https://pathfinder-eve-online.slack.com - - Please send me a mail for invite: pathfinder@exodus4d.de + - Join channel [pathfinder-eve-online.slack.com](https://join.slack.com/t/pathfinder-eve-online/shared_invite/enQtMzMyOTkyMjczMTA3LWI2NGE1OTY5ODBmNDZlMDY3MDIzYjk5ZTljM2JjZjIwNDRkNzMyMTEwMDUzOGQwM2E3ZjE1NGEwNThlMzYzY2Y) + - Can´t join? pathfinder@exodus4d.de **Feel free to check the code for bugs and security issues. Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/issues) section.** @@ -49,7 +50,7 @@ Issues should be reported in the [Issue](https://github.com/exodus4d/pathfinder/ |-- app.js --> require.js config (!required for production!) |-- [0777] logs/ --> log files |-- ... - | -- node_modules/ --> node.js modules (not used for production) + |-- node_modules/ --> node.js modules (not used for production) |-- ... |-- [0755] public/ --> frontend source |-- css/ --> CSS dist/build folder (minified) diff --git a/app/cron.ini b/app/cron.ini index 08671e47..1eb66ce9 100644 --- a/app/cron.ini +++ b/app/cron.ini @@ -59,8 +59,13 @@ deleteStatisticsData = Cron\StatisticsUpdate->deleteStatisticsD ; truncate map history log files truncateMapHistoryLogFiles = Cron\MapHistory->truncateFiles, @halfHour -; updates small amount of static system data from CCP API +; sync "sovereignty" and "faction warfare" data from CCP´s ESI API +updateSovereigntyData = Cron\Universe->updateSovereigntyData, @halfPastHour + +; sync static system data from CCP´s ESI API +; -> Job is WIP! ;updateUniverseSystems = Cron\Universe->updateUniverseSystems, @instant -; setup universe DB with static data from ESI +; bootstrap job for "eve_universe" DB from CCP´s ESI API +; -> Only for development! This job is used to build the initial export/sql/eve_universe.sql ;setup = Cron\Universe->setup, @instant \ No newline at end of file diff --git a/app/lib/db/cortex.php b/app/lib/db/cortex.php index f81379cf..a8e5b5e0 100644 --- a/app/lib/db/cortex.php +++ b/app/lib/db/cortex.php @@ -213,7 +213,7 @@ class Cortex extends Cursor { } else $this->whitelist=$fields; $id=$this->dbsType=='sql'?$this->primary:'_id'; - if (!in_array($id,$this->whitelist)) + if (!in_array($id,$this->whitelist) && !($exclude && in_array($id,$fields))) $this->whitelist[]=$id; $this->applyWhitelist(); return $this->whitelist; @@ -595,7 +595,7 @@ class Cortex extends Cursor { * @param array|null $filter * @param array|null $options * @param int $ttl - * @return CortexCollection + * @return CortexCollection|false */ public function find($filter = NULL, array $options = NULL, $ttl = 0) { $sort=false; @@ -731,12 +731,12 @@ class Cortex extends Cursor { $addToFilter = array($id.' IN ?', $result); } // *-to-one - elseif ($this->dbsType == 'sql') { + elseif (!$deep && $this->dbsType == 'sql') { // use sub-query inclusion $has_filter=$this->mergeFilter([$has_filter, [$this->rel($key)->getTable().'.'.$fromConf[1].'='.$this->getTable().'.'.$id]]); $result = $this->_refSubQuery($key,$has_filter,$has_options); - $addToFilter = ['exists('.$result[0].')']+$result[1]; + $addToFilter = array_merge(['exists('.$result[0].')'],$result[1]); } elseif ($result = $this->_hasRefsIn($key,$has_filter,$has_options,$ttl)) $addToFilter = array($id.' IN ?', $result); @@ -781,11 +781,11 @@ class Cortex extends Cursor { $options['order'] = preg_replace('/\h+DESC(?=\s*(?:$|,))/i',' DESC NULLS LAST',$options['order']); // assemble full sql query for joined queries if ($hasJoin) { + $adhoc=[]; // when in count-mode and grouping is active, wrap the query later // otherwise add a an adhoc counter field here if (!($subquery_mode=($options && !empty($options['group']))) && $count) - $this->adhoc['_rows']=['expr'=>'COUNT(*)','value'=>NULL]; - $adhoc=[]; + $adhoc[]='(COUNT(*)) as _rows'; if (!$count) // add bind parameters for filters in adhoc fields if ($this->preBinds) { @@ -1224,7 +1224,7 @@ class Cortex extends Cursor { // m:m save cascade if (!empty($this->saveCsd)) { foreach($this->saveCsd as $key => $val) { - if($fields[$key]['relType'] == 'has-many') { + if ($fields[$key]['relType'] == 'has-many') { $relConf = $fields[$key]['has-many']; if ($relConf['hasRel'] == 'has-many') { $mmTable = $this->mmTable($relConf,$key); @@ -1236,12 +1236,12 @@ class Cortex extends Cursor { $filter[] = $id; } // delete all refs - if (is_null($val)) + if (empty($val)) $mm->erase($filter); // update refs elseif (is_array($val)) { $mm->erase($filter); - foreach($val as $v) { + foreach(array_unique($val) as $v) { if ($relConf['isSelf'] && $v==$id) continue; $mm->set($key,$v); @@ -1256,7 +1256,7 @@ class Cortex extends Cursor { $rel = $this->getRelInstance($relConf[0],$relConf,$key); // find existing relations $refs = $rel->find([$relConf[1].' = ?',$this->getRaw($relConf['relField'])]); - if (is_null($val)) { + if (empty($val)) { foreach ($refs?:[] as $model) { $model->set($relConf[1],NULL); $model->save(); @@ -1470,7 +1470,7 @@ class Cortex extends Cursor { // handle relations if (isset($fields[$key]['belongs-to-one'])) { // one-to-many, one-to-one - if (is_null($val)) + if (empty($val)) $val = NULL; elseif (is_object($val) && !($this->dbsType=='mongo' && ( @@ -1489,7 +1489,7 @@ class Cortex extends Cursor { $val = $this->db->legacy() ? new \MongoId($val) : new \MongoDB\BSON\ObjectId($val); } elseif (isset($fields[$key]['has-one'])){ $relConf = $fields[$key]['has-one']; - if (is_null($val)) { + if (empty($val)) { $val = $this->get($key); $val->set($relConf[1],NULL); } else { @@ -2554,7 +2554,7 @@ class CortexQueryParser extends \Prefab { function($match) use($db) { if (!isset($match[1])) return $match[0]; - if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[1])) + if (preg_match('/\b(AND|OR|IN|LIKE|NOT|HAVING|SELECT|FROM|WHERE)\b/i',$match[1])) return $match[1]; return $db->quotekey($match[1]); }, $cond); @@ -2576,7 +2576,7 @@ class CortexQueryParser extends \Prefab { function($match) use($table) { if (!isset($match[3])) return $match[1]; - if (preg_match('/\b(AND|OR|IN|LIKE|NOT)\b/i',$match[3])) + if (preg_match('/\b(AND|OR|IN|LIKE|NOT|HAVING|SELECT|FROM|WHERE)\b/i',$match[3])) return $match[0]; return $match[2].$table.'.'.$match[3]; }, $cond); diff --git a/app/main/controller/accesscontroller.php b/app/main/controller/accesscontroller.php index 26692c7e..ab2dca79 100644 --- a/app/main/controller/accesscontroller.php +++ b/app/main/controller/accesscontroller.php @@ -36,14 +36,15 @@ class AccessController extends Controller { } /** - * get current character and check if it is a valid character + * check login status and look or a valid character * @param \Base $f3 * @return string * @throws \Exception */ protected function isLoggedIn(\Base $f3) : string { $loginStatus = 'UNKNOWN'; - if($character = $this->getCharacter()){ + // disable ttl cache time here. Further getCharacter() calls should use a short ttl + if($character = $this->getCharacter(0)){ if($character->checkLoginTimer()){ if(( $authStatus = $character->isAuthorized()) === 'OK'){ $loginStatus = 'OK'; diff --git a/app/main/controller/admin.php b/app/main/controller/admin.php index 0ceccb00..7c9670df 100644 --- a/app/main/controller/admin.php +++ b/app/main/controller/admin.php @@ -89,7 +89,7 @@ class Admin extends Controller{ protected function getAdminCharacter(\Base $f3){ $adminCharacter = null; if( !$f3->exists(Sso::SESSION_KEY_SSO_ERROR) ){ - if( $character = $this->getCharacter() ){ + if( $character = $this->getCharacter(0) ){ if(in_array($character->roleId->name, ['SUPER', 'CORPORATION'], true)){ // current character is admin $adminCharacter = $character; @@ -288,7 +288,7 @@ class Admin extends Controller{ $characters = []; // check if kickCharacters belong to same Corp as admin character // -> remove admin char from valid characters... - if( !empty($characterIds = array_diff( [$characterId], [$character->_id])) ){ + if( !empty($characterIds = array_diff([$characterId], [$character->_id])) ){ if($character->roleId->name === 'SUPER'){ if($filterCharacters = CharacterModel::getAll($characterIds)){ $characters = $filterCharacters; @@ -337,7 +337,7 @@ class Admin extends Controller{ $maps = $filterMaps; } }else{ - $maps = $character->getCorporation()->getMaps([$mapId], ['addInactive' => true, 'ignoreMapCount' => true]); + $maps = $character->getCorporation()->getMaps($mapId, ['addInactive' => true, 'ignoreMapCount' => true]); } return $maps; @@ -404,7 +404,7 @@ class Admin extends Controller{ $corporations = $this->getAccessibleCorporations($character); foreach($corporations as $corporation){ - if($maps = $corporation->getMaps([], ['addInactive' => true, 'ignoreMapCount' => true])){ + if($maps = $corporation->getMaps(null, ['addInactive' => true, 'ignoreMapCount' => true])){ $data->corpMaps[$corporation->name] = $maps; } } diff --git a/app/main/controller/api/map.php b/app/main/controller/api/map.php index 53f6898e..1fa570a2 100644 --- a/app/main/controller/api/map.php +++ b/app/main/controller/api/map.php @@ -52,13 +52,10 @@ class Map extends Controller\AccessController { * @throws Exception */ public function initData(\Base $f3){ - // expire time in seconds - $expireTimeCache = 60 * 60; - - if(!$f3->exists(self::CACHE_KEY_INIT, $return)){ - // response should not be cached if invalid -> e.g. missing static data - $validInitData = true; + $validInitData = true; + $ttl = 60 * 60; + if(!$exists = $f3->exists(self::CACHE_KEY_INIT, $return)){ $return = (object) []; $return->error = []; @@ -179,7 +176,10 @@ class Map extends Controller\AccessController { // get third party APIs ----------------------------------------------------------------------------------- $return->url = [ 'ccpImageServer' => Config::getPathfinderData('api.ccp_image_server'), - 'zKillboard' => Config::getPathfinderData('api.z_killboard') + 'zKillboard' => Config::getPathfinderData('api.z_killboard'), + 'eveeye' => Config::getPathfinderData('api.eveeye'), + 'dotlan' => Config::getPathfinderData('api.dotlan'), + 'anoik' => Config::getPathfinderData('api.anoik') ]; // Character default config ------------------------------------------------------------------------------- @@ -199,29 +199,33 @@ class Map extends Controller\AccessController { // structure status --------------------------------------------------------------------------------------- $structureStatus = Pathfinder\StructureStatusModel::getAll(); - $structureData = []; + $structureStatusData = []; foreach($structureStatus as $status){ - $structureData[$status->_id] = $status->getData(); + $structureStatusData[$status->_id] = $status->getData(); } - $return->structureStatus = $structureData; + $return->structureStatus = $structureStatusData; - $validInitData = $validInitData ? !empty($structureData) : $validInitData; + $validInitData = $validInitData ? !empty($structureStatusData) : $validInitData; // get available wormhole types --------------------------------------------------------------------------- /** - * @var $wormhole Universe\WormholeModel + * @var $groupUniverseModel Universe\GroupModel */ - $wormhole = Universe\AbstractUniverseModel::getNew('WormholeModel'); + $groupUniverseModel = Universe\AbstractUniverseModel::getNew('GroupModel'); + $groupUniverseModel->getById(Config::ESI_GROUP_WORMHOLE_ID); $wormholesData = []; - if($rows = $wormhole->find(null, ['order' => 'name asc'])){ - foreach($rows as $rowData){ - $wormholesData[$rowData->name] = $rowData->getData(); + /** + * @var $typeModel Universe\TypeModel + */ + foreach($types = $groupUniverseModel->getTypes(false) as $typeModel){ + if( + ($wormholeData = $typeModel->getWormholeData()) && + mb_strlen((string)$wormholeData->name) === 4 + ){ + $wormholesData[$wormholeData->name] = $wormholeData; } - - $wormhole->reset(); - $wormhole->name = 'K162'; - $wormholesData[$wormhole->name] = $wormhole->getData(); } + ksort($wormholesData); $return->wormholes = $wormholesData; $validInitData = $validInitData ? !empty($wormholesData) : $validInitData; @@ -231,20 +235,20 @@ class Map extends Controller\AccessController { * @var $categoryUniverseModel Universe\CategoryModel */ $categoryUniverseModel = Universe\AbstractUniverseModel::getNew('CategoryModel'); - $categoryUniverseModel->getById(6); - $shipData = $categoryUniverseModel->getData(['mass']); - $categoryUniverseModel->getById(65); - $structureData = $categoryUniverseModel->getData(); - $return->universeCategories = [ - 6 => $shipData, - 65 => $structureData + Config::ESI_CATEGORY_SHIP_ID => + ($categoryUniverseModel->getById(Config::ESI_CATEGORY_SHIP_ID) && $categoryUniverseModel->valid()) ? $categoryUniverseModel->getData(['mass']) : null, + Config::ESI_CATEGORY_STRUCTURE_ID => + ($categoryUniverseModel->getById(Config::ESI_CATEGORY_STRUCTURE_ID) && $categoryUniverseModel->valid()) ? $categoryUniverseModel->getData() : null, ]; - $validInitData = $validInitData ? !empty($return->universeCategories[65]) : $validInitData; + $validInitData = $validInitData ? !count(array_filter($return->universeCategories, function($v){ + return empty(array_filter((array)$v->groups)); + })) : $validInitData; + // response should not be cached if invalid -> e.g. missing static data if($validInitData){ - $f3->set(self::CACHE_KEY_INIT, $return, $expireTimeCache ); + $f3->set(self::CACHE_KEY_INIT, $return, $ttl); } } @@ -257,6 +261,9 @@ class Map extends Controller\AccessController { $ssoError->message = $message; $return->error[] = $ssoError; $f3->clear(Controller\Ccp\Sso::SESSION_KEY_SSO_ERROR); + }elseif($validInitData){ + // no errors and valid data -> send Cache header + $f3->expire(Config::ttlLeft($exists, $ttl)); } echo json_encode($return); @@ -871,6 +878,7 @@ class Map extends Controller\AccessController { $getMapUserData = (bool)$postData['getMapUserData']; $mapTracking = (bool)$postData['mapTracking']; $systemData = (array)$postData['systemData']; + $newSystemPositions = (array)$postData['newSystemPositions']; $activeCharacter = $this->getCharacter(); $return = (object)[]; @@ -886,7 +894,7 @@ class Map extends Controller\AccessController { if( !is_null($map = $activeCharacter->getMap($mapId)) ){ // check character log (current system) and manipulate map (e.g. add new system) if($mapTracking){ - $map = $this->updateMapByCharacter($map, $activeCharacter); + $map = $this->updateMapByCharacter($map, $activeCharacter, $newSystemPositions); } // mapUserData ---------------------------------------------------------------------------------------- @@ -906,14 +914,13 @@ class Map extends Controller\AccessController { // systemData ----------------------------------------------------------------------------------------- if( $mapId === (int)$systemData['mapId'] && - !is_null($system = $map->getSystemById((int)$systemData['systemData']['id'])) + !is_null($system = $map->getSystemById((int)$systemData['id'])) ){ // data for currently selected system $return->system = $system->getData(); $return->system->signatures = $system->getSignaturesData(); $return->system->sigHistory = $system->getSignaturesHistory(); $return->system->structures = $system->getStructuresData(); - } } } @@ -932,10 +939,11 @@ class Map extends Controller\AccessController { * update map connections/systems based on $character´s location logs * @param Pathfinder\MapModel $map * @param Pathfinder\CharacterModel $character + * @param array $newSystemPositions * @return Pathfinder\MapModel * @throws Exception */ - protected function updateMapByCharacter(Pathfinder\MapModel $map, Pathfinder\CharacterModel $character) : Pathfinder\MapModel { + protected function updateMapByCharacter(Pathfinder\MapModel $map, Pathfinder\CharacterModel $character, array $newSystemPositions = []) : Pathfinder\MapModel { // map changed. update cache (system/connection) changed $mapDataChanged = false; @@ -952,6 +960,9 @@ class Map extends Controller\AccessController { $sourceSystemId = (int)$sourceLog->systemId; if($sourceSystemId){ + $defaultPositions = (array)$newSystemPositions['defaults']; + $currentPosition = (array)$newSystemPositions['location']; + $sourceSystem = null; $targetSystem = null; @@ -963,8 +974,8 @@ class Map extends Controller\AccessController { // system coordinates for system tha might be added next $systemOffsetX = 130; $systemOffsetY = 0; - $systemPosX = 0; - $systemPosY = 30; + $systemPosX = ((int)$defaultPositions[0]['x']) ? : 0; + $systemPosY = ((int)$defaultPositions[0]['y']) ? : 30; // check if previous (solo) system is already on the map ---------------------------------------------- $sourceSystem = $map->getSystemByCCPId($sourceSystemId, [AbstractModel::getFilter('active', true)]); @@ -972,12 +983,10 @@ class Map extends Controller\AccessController { // if systems don´t already exists on map -> get "blank" system // -> required for system type check (e.g. wormhole, k-space) if($sourceSystem){ + // system exists $sourceExists = true; - - // system exists -> add target to the "right" - $systemPosX = $sourceSystem->posX + $systemOffsetX; - $systemPosY = $sourceSystem->posY + $systemOffsetY; }else{ + // system not exists -> get"blank" system $sourceSystem = $map->getNewSystem($sourceSystemId); } @@ -992,6 +1001,11 @@ class Map extends Controller\AccessController { if($targetSystem){ $targetExists = true; + + if($targetSystemId === (int)$currentPosition['systemId']){ + $systemPosX = (int)$currentPosition['position']['x']; + $systemPosY = (int)$currentPosition['position']['y']; + } }else{ $targetSystem = $map->getNewSystem($targetSystemId); } @@ -1062,6 +1076,24 @@ class Map extends Controller\AccessController { break; } + // check for "abyss" systems ===================================================================== + if(!$map->trackAbyssalJumps){ + if( + $sourceSystem->isAbyss() || + $targetSystem->isAbyss() + ){ + $addConnection = false; + + if($sourceSystem->isAbyss()){ + $addSourceSystem = false; + } + + if($targetSystem->isAbyss()){ + $addTargetSystem = false; + } + } + } + // save source system ============================================================================= if( $addSourceSystem && @@ -1074,9 +1106,15 @@ class Map extends Controller\AccessController { $map = $sourceSystem->mapId; $sourceExists = true; $mapDataChanged = true; - // increase system position (prevent overlapping) - $systemPosX = $sourceSystem->posX + $systemOffsetX; - $systemPosY = $sourceSystem->posY + $systemOffsetY; + + if(!empty($defaultPositions[1])){ + $systemPosX = (int)$defaultPositions[1]['x']; + $systemPosY = (int)$defaultPositions[1]['y']; + }else{ + // increase system position (prevent overlapping) + $systemPosX = $sourceSystem->posX + $systemOffsetX; + $systemPosY = $sourceSystem->posY + $systemOffsetY; + } } } diff --git a/app/main/controller/api/rest/signature.php b/app/main/controller/api/rest/signature.php index 08c0bce2..92a0db71 100644 --- a/app/main/controller/api/rest/signature.php +++ b/app/main/controller/api/rest/signature.php @@ -56,7 +56,7 @@ class Signature extends AbstractRestController { $data['groupId'] == 5 || $data['typeId'] == 0 ){ - unset( $data['typeId'] ); + unset($data['typeId']); } // "sig reader" should not overwrite signature group information @@ -65,6 +65,7 @@ class Signature extends AbstractRestController { $signature->groupId > 0 ){ unset($data['groupId']); + unset($data['typeId']); } } @@ -78,13 +79,20 @@ class Signature extends AbstractRestController { // delete "old" signatures ---------------------------------------------------------------------------- if((bool)$requestData['deleteOld']){ + // if linked ConnectionModels should be deleted as well + $deleteConnectionId = (bool)$requestData['deleteConnection']; + $updatedSignatureIds = array_column($signaturesData, 'id'); $signatures = $system->getSignatures(); foreach($signatures as $signature){ if(!in_array($signature->_id, $updatedSignatureIds)){ + // set if potential linked ConnectionModel should be deleted as well + $signature->virtual('connectionIdDeleteCascade', $deleteConnectionId); if($signature->delete()){ $updateSignaturesHistory = true; } + // clear temp virtual field as well + $signature->clearVirtual('connectionIdDeleteCascade'); } } } @@ -190,6 +198,9 @@ class Signature extends AbstractRestController { $system->getById($systemId); if($system->hasAccess($activeCharacter)){ + // if linked ConnectionModels should be deleted as well + $deleteConnectionId = (bool)$requestData['deleteConnection']; + // if there is any changed/deleted/updated signature // -> we need to update signature history data for the system $updateSignaturesHistory = false; @@ -202,11 +213,15 @@ class Signature extends AbstractRestController { $signature->getById($signatureId); // make sure signature belongs to main system (user has access) if($signature->get('systemId', true) == $systemId){ + // set if potential linked ConnectionModel should be deleted as well + $signature->virtual('connectionIdDeleteCascade', $deleteConnectionId); if($signature->delete()){ $deletedSignatureIds[] = $signatureId; $updateSignaturesHistory = true; } $signature->reset(); + // clear temp virtual field as well + $signature->clearVirtual('connectionIdDeleteCascade'); } } diff --git a/app/main/controller/api/rest/system.php b/app/main/controller/api/rest/system.php index 48cc16e3..4a5983a8 100644 --- a/app/main/controller/api/rest/system.php +++ b/app/main/controller/api/rest/system.php @@ -38,6 +38,7 @@ class System extends AbstractRestController { $systemData->signatures = $system->getSignaturesData(); $systemData->sigHistory = $system->getSignaturesHistory(); $systemData->structures = $system->getStructuresData(); + $systemData->stations = $system->getStationsData(); } } diff --git a/app/main/controller/api/route.php b/app/main/controller/api/route.php index 2ac25c50..a19fe354 100644 --- a/app/main/controller/api/route.php +++ b/app/main/controller/api/route.php @@ -91,14 +91,16 @@ class Route extends Controller\AccessController { * -> this data is equal for EACH route search (does not depend on map data) */ private function setStaticJumpData(){ - $query = "SELECT * FROM system_neighbour"; - $rows = $this->getDB()->exec($query, null, $this->staticJumpDataCacheTime); + if($universeDB = $this->getDB('UNIVERSE')){ + $query = "SELECT * FROM system_neighbour"; + $rows = $universeDB->exec($query, null, $this->staticJumpDataCacheTime); - if(count($rows) > 0){ - array_walk($rows, function(&$row){ - $row['jumpNodes'] = array_map('intval', explode(':', $row['jumpNodes'])); - }); - $this->updateJumpData($rows); + if(count($rows) > 0){ + array_walk($rows, function(&$row){ + $row['jumpNodes'] = array_map('intval', explode(':', $row['jumpNodes'])); + }); + $this->updateJumpData($rows); + } } } diff --git a/app/main/controller/api/setup.php b/app/main/controller/api/setup.php index a14d1109..000c5317 100644 --- a/app/main/controller/api/setup.php +++ b/app/main/controller/api/setup.php @@ -9,6 +9,7 @@ namespace Controller\Api; use Controller; +use lib\Config; use Model; class Setup extends Controller\Controller { @@ -23,6 +24,7 @@ class Setup extends Controller\Controller { public function buildIndex(\Base $f3){ $postData = (array)$f3->get('POST'); $type = (string)$postData['type']; + $countAll = (int)$postData['countAll']; $count = (int)$postData['count']; $offset = (int)$postData['offset']; @@ -31,7 +33,7 @@ class Setup extends Controller\Controller { $return->type = $type; $return->count = $count; $return->offset = $offset; - $return->countAll = 0; + $return->countAll = $countAll; $return->countBuild = 0; $return->countBuildAll = 0; $return->progress = 0; @@ -42,7 +44,7 @@ class Setup extends Controller\Controller { * @param int $value * @return int */ - $sum = function(int $carry, int $value){ + $sum = function(int $carry, int $value) : int { $carry += $value; return $carry; }; @@ -62,49 +64,72 @@ class Setup extends Controller\Controller { case 'Systems': $length = 100; $buildInfo = $controller->buildSystemsIndex($offset, $length); + $return->offset = $buildInfo['offset']; $return->countAll = $buildInfo['countAll']; $return->countBuild = $buildInfo['countBuild']; - $return->countBuildAll = $offset; - $return->progress = $percent($return->countAll, $offset); + $return->countBuildAll = $return->offset; + break; + case 'Wormholes': + $groupId = Config::ESI_GROUP_WORMHOLE_ID; + $length = 10; + $buildInfo = $controller->setupGroup($groupId, $offset, $length, true); + + $return->offset = $buildInfo['offset']; + $return->countAll = $buildInfo['countAll']; + $return->countBuild = $buildInfo['count']; + $return->countBuildAll = $return->offset; break; case 'Structures': - $categoryId = 65; - $length = 2; - $offset = $count * $length; + $categoryId = Config::ESI_CATEGORY_STRUCTURE_ID; + $length = 1; $buildInfo = $controller->setupCategory($categoryId, $offset, $length); - $categoryUniverseModel = Model\Universe\AbstractUniverseModel::getNew('CategoryModel'); $categoryUniverseModel->getById($categoryId, 0); - $return->countAll = (int)$f3->get('REQUIREMENTS.DATA.STRUCTURES'); - $return->countBuild = array_reduce($buildInfo, $sum, 0); - $return->countBuildAll = $categoryUniverseModel->getTypesCount(false); - $return->progress = $percent($return->countAll, $return->countBuildAll); + + $return->offset = $buildInfo['offset']; + $return->countBuild = $buildInfo['count']; + $return->countBuildAll = $return->offset; + $return->subCount = [ + 'countBuildAll' => $categoryUniverseModel->getTypesCount(false) + ]; break; case 'Ships': - $categoryId = 6; + $categoryId = Config::ESI_CATEGORY_SHIP_ID; $length = 2; - $offset = $count * $length; $buildInfo = $controller->setupCategory($categoryId, $offset, $length); - $categoryUniverseModel = Model\Universe\AbstractUniverseModel::getNew('CategoryModel'); $categoryUniverseModel->getById($categoryId, 0); - $return->countAll = (int)$f3->get('REQUIREMENTS.DATA.SHIPS'); - $return->countBuild = array_reduce($buildInfo, $sum, 0); - $return->countBuildAll = $categoryUniverseModel->getTypesCount(false); - $return->progress = $percent($return->countAll, $return->countBuildAll); + + $return->offset = $buildInfo['offset']; + $return->countBuild = $buildInfo['count']; + $return->countBuildAll = $return->offset; + $return->subCount = [ + 'countBuildAll' => $categoryUniverseModel->getTypesCount(false) + ]; + break; + case 'SystemStatic': + $length = 300; + $buildInfo = $this->setupSystemStaticTable($offset, $length); + + $return->offset = $buildInfo['offset']; + $return->countAll = $buildInfo['countAll']; + $return->countBuild = $buildInfo['count']; + $return->countBuildAll = $return->offset; break; case 'SystemNeighbour': - // Becomes deprecated with new Universe DB!!! - $this->setupSystemJumpTable(); + $length = 1500; + $buildInfo = $this->setupSystemJumpTable($offset, $length); - $return->countAll = (int)$f3->get('REQUIREMENTS.DATA.NEIGHBOURS'); - $return->countBuild = $f3->DB->getDB('PF')->getRowCount('system_neighbour'); - $return->countBuildAll = $return->countBuild; - $return->progress = $percent($return->countAll, $return->countBuildAll); + $return->offset = $buildInfo['offset']; + $return->countAll = $buildInfo['countAll']; + $return->countBuild = $buildInfo['count']; + $return->countBuildAll = $return->offset; break; } + $return->progress = $percent($return->countAll, $return->countBuildAll); + if($return->countBuildAll < $return->countAll){ $return->count++; } @@ -135,21 +160,87 @@ class Setup extends Controller\Controller { case 'Systems': $controller->clearSystemsIndex(); $systemUniverseModel = Model\Universe\AbstractUniverseModel::getNew('SystemModel'); - $return->countAll = $f3->DB->getDB('UNIVERSE')->getRowCount($systemUniverseModel->getTable()); + $return->countAll = $systemUniverseModel->getRowCount(); + break; + case 'SystemNeighbour': + $systemNeighbourModel = Model\Universe\AbstractUniverseModel::getNew('SystemNeighbourModel'); + $systemNeighbourModel->truncate(); + $return->countAll = (int)$f3->get('REQUIREMENTS.DATA.NEIGHBOURS'); break; } echo json_encode($return); } + /** + * import static 'system_static` table data from *.csv + * @param int $offset + * @param int $length + * @return array + * @throws \Exception + */ + protected function setupSystemStaticTable(int $offset = 0, int $length = 0) : array { + $info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset]; + + /** + * @var $systemStaticModel Model\Universe\SystemStaticModel + */ + $systemStaticModel = Model\Universe\AbstractUniverseModel::getNew('SystemStaticModel'); + if(!empty($csvData = $systemStaticModel::getCSVData($systemStaticModel->getTable()))){ + $info['countAll'] = count($csvData); + if($length){ + $csvData = array_slice($csvData, $offset, $length); + } + $info['countChunk'] = count($csvData); + $cols = ['typeId' => [], 'systemId' => []]; + foreach($csvData as $data){ + $validColCount = 0; + $systemStaticModel->getById((int)$data['id'], 0); + $systemStaticModel->id = (int)$data['id']; + foreach($cols as $col => &$invalidIds){ + if($systemStaticModel->exists($col)){ + $colVal = (int)$data[$col]; + if(!in_array($colVal, $invalidIds)){ + $relModel = $systemStaticModel->rel($col); + $relModel->getById($colVal, 0); + if($relModel->valid()){ + $systemStaticModel->$col = $relModel; + $validColCount++; + }else{ + $invalidIds[] = $colVal; + break; + } + }else{ + break; + } + } + } + + if($validColCount == count($cols)){ + $systemStaticModel->save(); + } + $systemStaticModel->reset(); + + $info['count']++; + $info['offset']++; + } + } + + return $info; + } + /** * This function is just for setting up the cache table 'system_neighbour' which is used * for system jump calculation. Call this function manually when CCP adds Systems/Stargates + * @param int $offset + * @param int $length + * @return array */ - protected function setupSystemJumpTable(){ - $universeDB = $this->getDB('UNIVERSE'); + protected function setupSystemJumpTable(int $offset = 0, int $length = 0) : array { + $info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset]; + $universeDB = $this->getDB('UNIVERSE'); - $query = "SELECT + $query = "SELECT SQL_CALC_FOUND_ROWS `system`.`id` `systemId`, `system`.`name` `systemName`, `system`.`constellationId` `constellationId`, @@ -178,58 +269,73 @@ class Setup extends Controller\Controller { `system`.`security` = :ls OR `system`.`security` = :hs ) + HAVING + `jumpNodes` IS NOT NULL "; - $rows = $universeDB->exec($query, [ + $args = [ ':regionIdJove1' => 10000017, ':regionIdJove2' => 10000019, ':regionIdJove3' => 10000004, ':ns' => '0.0', ':ls' => 'L', ':hs' => 'H' - ]); + ]; - if(count($rows)){ - $pfDB = $this->getDB('PF'); + if($length){ + $query .= ' LIMIT :limit'; + $args[':limit'] = $length; - // clear cache table - $pfDB->exec("TRUNCATE system_neighbour"); + if($offset){ + $query .= ' OFFSET :offset'; + $args[':offset'] = $offset; + } + } + $rows = $universeDB->exec($query, $args); + + if(!empty($countRes = $universeDB->exec("SELECT FOUND_ROWS() `count`")) && isset($countRes[0]['count'])){ + $info['countAll'] = (int)$countRes[0]['count']; + } + + if($info['countChunk'] = count($rows)){ + $placeholderStr = function(string $str) : string { + return ':' . $str; + }; + + $updateRule = function(string $str) : string { + return $str . " = VALUES(" . $str . ")"; + }; + + $universeDB->begin(); foreach($rows as $row){ + $info['count']++; + $info['offset']++; + if(!$row['jumpNodes']){ // should never happen! continue; } - $pfDB->exec(" - INSERT INTO - system_neighbour( - regionId, - constellationId, - systemName, - systemId, - jumpNodes, - trueSec - ) - VALUES( - :regionId, - :constellationId, - :systemName, - :systemId, - :jumpNodes, - :trueSec - )", - [ - ':regionId' => $row['regionId'], - ':constellationId' => $row['constellationId'], - ':systemName' => $row['systemName'], - ':systemId' => $row['systemId'], - ':jumpNodes' => $row['jumpNodes'], - ':trueSec' => $row['trueSec'] - ] - ); + $columns = array_keys($row); + $columnsQuoted = array_map($universeDB->quotekey, $columns); + $placeholder = array_map($placeholderStr, $columns); + $args = array_combine($placeholder, $row); + + $updateSql = array_map($updateRule, $columns); + + $sql = "INSERT INTO + system_neighbour(" . implode(', ', $columnsQuoted) . ") + VALUES(" . implode(', ', $placeholder) . ") + ON DUPLICATE KEY UPDATE + " . implode(', ', $updateSql); + + $universeDB->exec($sql, $args); } + $universeDB->commit(); } + + return $info; } } \ No newline at end of file diff --git a/app/main/controller/api/system.php b/app/main/controller/api/system.php index 1b80de87..31c65dba 100644 --- a/app/main/controller/api/system.php +++ b/app/main/controller/api/system.php @@ -111,7 +111,7 @@ class System extends Controller\AccessController { } /** - * set destination for specific systemIds + * set destination for system, station or structure * @param \Base $f3 * @throws \Exception */ @@ -120,25 +120,25 @@ class System extends Controller\AccessController { $return = (object) []; $return->error = []; - $return->systemData = []; + $return->destData = []; - if( !empty($postData['systemData'] )){ + if(!empty($destData = (array)$postData['destData'])){ $activeCharacter = $this->getCharacter(); $return->clearOtherWaypoints = (bool)$postData['clearOtherWaypoints']; $return->first = (bool)$postData['first']; - if( $accessToken = $activeCharacter->getAccessToken() ){ + if($accessToken = $activeCharacter->getAccessToken()){ $options = [ 'clearOtherWaypoints' => $return->clearOtherWaypoints, 'addToBeginning' => $return->first, ]; - foreach($postData['systemData'] as $systemData){ - $response = $f3->ccpClient()->setWaypoint($systemData['systemId'], $accessToken, $options); + foreach($destData as $data){ + $response = $f3->ccpClient()->setWaypoint((int)$data['id'], $accessToken, $options); if(empty($response)){ - $return->systemData[] = $systemData; + $return->destData[] = $data; }else{ $error = (object) []; $error->type = 'error'; diff --git a/app/main/controller/api/universe.php b/app/main/controller/api/universe.php index 559d0347..95156955 100644 --- a/app/main/controller/api/universe.php +++ b/app/main/controller/api/universe.php @@ -112,7 +112,7 @@ class Universe extends Controller\AccessController { $constellation = Model\Universe\AbstractUniverseModel::getNew('ConstellationModel'); $constellation->getById($constellationId); - if( !$constellation->dry() && $constellation->systems){ + if($constellation->valid() && $constellation->systems){ /** * @var Model\Universe\SystemModel $system */ diff --git a/app/main/controller/api/user.php b/app/main/controller/api/user.php index 03ae0c3a..9c2d387d 100644 --- a/app/main/controller/api/user.php +++ b/app/main/controller/api/user.php @@ -128,7 +128,7 @@ class User extends Controller\Controller{ // character is valid and allowed to login $return->character = reset($characters)->getData(); // get Session status for character - if($activeCharacter = $this->getCharacter()){ + if($activeCharacter = $this->getCharacter(0)){ if($activeUser = $activeCharacter->getUser()){ if($sessionCharacterData = $activeUser->findSessionCharacterData($return->character->id)){ $return->character->hasActiveSession = true; diff --git a/app/main/controller/ccp/sso.php b/app/main/controller/ccp/sso.php index 27e9cf9f..914ea340 100644 --- a/app/main/controller/ccp/sso.php +++ b/app/main/controller/ccp/sso.php @@ -81,7 +81,7 @@ class Sso extends Api\User{ // check if character is valid and exists if( - !$character->dry() && + $character->valid() && $character->hasUserCharacter() && ($activeCharacter->getUser()->_id === $character->getUser()->_id) ){ @@ -217,9 +217,9 @@ class Sso extends Api\User{ $characterModel = $characterModel->updateLog(); // connect character with current user - if( is_null($user = $this->getUser()) ){ + if(is_null($user = $this->getUser())){ // connect character with existing user (no changes) - if( is_null( $user = $characterModel->getUser()) ){ + if(is_null($user = $characterModel->getUser())){ // no user found (new character) -> create new user and connect to character /** * @var $user Pathfinder\UserModel @@ -234,7 +234,7 @@ class Sso extends Api\User{ * @var $userCharactersModel Pathfinder\UserCharacterModel */ if( is_null($userCharactersModel = $characterModel->userCharacter) ){ - $userCharactersModel = Pathfinder\AbstractPathfinderModel::getNew('UserCharacterModel'); + $userCharactersModel = $characterModel->rel('userCharacter'); $userCharactersModel->characterId = $characterModel; } diff --git a/app/main/controller/ccp/universe.php b/app/main/controller/ccp/universe.php index 94f1a142..67f1fc68 100644 --- a/app/main/controller/ccp/universe.php +++ b/app/main/controller/ccp/universe.php @@ -53,6 +53,25 @@ class Universe extends Controller { } }*/ + /** + * setup categories + all dependencies (e.g. groups, types) + * id 2 -> Celestial (>100 groups -> >1000 types) + * id 6 -> Ship (46 groups -> 4xx types) + * id 65 -> Structure (10 groups -> 33 types) + * @param array $categoriesWhitelist + * @return array + * @throws \Exception + */ + protected function setupCategories(array $categoriesWhitelist = []) : array { + $info = []; + $categoryIds = Model\Universe\CategoryModel::getUniverseCategories(); + $categoryIds = array_intersect($categoriesWhitelist, $categoryIds); + foreach($categoryIds as $categoryId){ + $info[$categoryId] = $this->setupCategory($categoryId); + } + return $info; + } + /** * setup category + all dependencies (e.g. groups, types) * -> $length = 0 -> setup all groups @@ -63,41 +82,18 @@ class Universe extends Controller { * @throws \Exception */ public function setupCategory(int $categoryId, int $offset = 0, int $length = 0) : array { - $return = []; + $info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset, 'groupTypes' => []]; + if($categoryId){ /** * @var $category Model\Universe\CategoryModel */ $category = Model\Universe\AbstractUniverseModel::getNew('CategoryModel'); $category->loadById($categoryId); - $groupIds = $category->loadGroupsData($offset, $length); - foreach((array)$category->groups as $group){ - // only load types for changed groups (not all) - if(in_array($group->_id, $groupIds)){ - $return[$group->_id] = $group->loadTypesData(); - } - } + $info = $category->loadGroupsData($offset, $length); } - return $return; - } - /** - * setup categories + all dependencies (e.g. groups, types) - * id 2 -> Celestial (>100 groups -> >1000 types) - * id 6 -> Ship (45 groups -> 490 types) - * id 65 -> Structure (10 groups -> 33 types) - * @param array $categoriesWhitelist - * @return array - * @throws \Exception - */ - protected function setupCategories(array $categoriesWhitelist = []) : array { - $return = []; - $categoryIds = $this->getF3()->ccpClient()->getUniverseCategories(); - $categoryIds = array_intersect($categoriesWhitelist, $categoryIds); - foreach($categoryIds as $categoryId){ - $return[$categoryId] = $this->setupCategory($categoryId); - } - return $return; + return $info; } /** @@ -111,19 +107,38 @@ class Universe extends Controller { * @throws \Exception */ protected function setupGroups(array $groupsWhitelist = []) : array { - $return = []; - $groupIds = $this->getF3()->ccpClient()->getUniverseGroups(); + $info = []; + $groupIds = Model\Universe\GroupModel::getUniverseGroups(); $groupIds = array_intersect($groupsWhitelist, $groupIds); - /** - * @var $group Model\Universe\GroupModel - */ - $group = Model\Universe\AbstractUniverseModel::getNew('GroupModel'); foreach($groupIds as $groupId){ - $group->loadById($groupId); - $return[$group->_id] = $group->loadTypesData(); - $group->reset(); + $info[$groupId] = $this->setupGroup($groupId); } - return $return; + return $info; + } + + /** + * setup group + all dependencies (e.g. types) + * @param int $groupId + * @param int $offset + * @param int $length + * @param bool $storeDogmaAttributes + * @return array + * @throws \Exception + */ + public function setupGroup(int $groupId, int $offset = 0, int $length = 0, bool $storeDogmaAttributes = false) : array { + $info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset]; + + if($groupId){ + /** + * @var $group Model\Universe\GroupModel + */ + $group = Model\Universe\AbstractUniverseModel::getNew('GroupModel'); + $group->storeDogmaAttributes = $storeDogmaAttributes; + $group->loadById($groupId); + $info = $group->loadTypesData($offset, $length); + } + + return $info; } // system search index methods ==================================================================================== @@ -146,10 +161,11 @@ class Universe extends Controller { $system = Model\Universe\AbstractUniverseModel::getNew('SystemModel'); $indexData = []; foreach($systemIds as $systemId){ - $system->getById($systemId); + $system->getById($systemId, 0); if($hashKeyId = $system->getHashKey()){ $indexData[$hashKeyId] = $system->getData(); } + $system->reset(); // offset must increase otherwise we get a endless loop // -> see /setup ajax build loop function $offset++; @@ -165,12 +181,14 @@ class Universe extends Controller { /** * get systemIds for all systems + * @param bool $ignoreCache * @return array * @throws \Exception */ - public function getSystemIds() : array { + public function getSystemIds(bool $ignoreCache = false) : array { $f3 = $this->getF3(); - if( !$f3->exists(self::SESSION_KEY_SYSTEM_IDS, $systemIds) ){ + $systemIds = []; + if($ignoreCache || !$f3->exists(self::SESSION_KEY_SYSTEM_IDS, $systemIds)){ /** * @var $system Model\Universe\SystemModel */ @@ -184,7 +202,7 @@ class Universe extends Controller { } } - return (array)$systemIds; + return $systemIds ? : []; } /** @@ -225,20 +243,20 @@ class Universe extends Controller { * @return null|\stdClass * @throws \Exception */ - public function getSystemData(int $systemId){ + public function getSystemData(int $systemId) : ?\stdClass { $data = null; if($systemId){ // ...check index for data $cacheKeyRow = Model\Universe\AbstractUniverseModel::generateHashKeyRow('system', $systemId); - $data = $this->get($cacheKeyRow); - if(!$data){ + if(!$data = $this->get($cacheKeyRow)){ // .. try to build index /** * @var $system Model\Universe\SystemModel */ $system = Model\Universe\AbstractUniverseModel::getNew('SystemModel'); - $system->getById($systemId); - $data = $system->buildIndex(); + if($system->getById($systemId)){ + $data = $system->buildIndex(); + } } } return $data; @@ -249,7 +267,7 @@ class Universe extends Controller { * @param string $cacheKey * @return null|\stdClass */ - private function get(string $cacheKey){ + private function get(string $cacheKey) : ?\stdClass { $data = null; if($this->getF3()->exists($cacheKey,$value)) { if(is_string($value) && strpos($value, Model\Universe\AbstractUniverseModel::CACHE_KEY_PREFIX) === 0) { diff --git a/app/main/controller/controller.php b/app/main/controller/controller.php index ee020a51..7433ec18 100644 --- a/app/main/controller/controller.php +++ b/app/main/controller/controller.php @@ -16,6 +16,7 @@ use lib\db\SQL; use lib\Resource; use lib\Monolog; use lib\Util; +use Model\AbstractModel; use Model\Pathfinder; use DB; @@ -381,13 +382,14 @@ class Controller { } /** - * get current character data from session - * @return array + * get current character from session data + * @param int $ttl + * @return Pathfinder\CharacterModel|null * @throws \Exception */ - public function getSessionCharacterData() : array { - $data = []; - if($user = $this->getUser()){ + protected function getSessionCharacter(int $ttl = AbstractModel::DEFAULT_SQL_TTL) : ?Pathfinder\CharacterModel { + $character = null; + if($user = $this->getUser($ttl)){ $header = self::getRequestHeaders(); $requestedCharacterId = (int)$header['Pf-Character']; if( !$this->getF3()->get('AJAX') ){ @@ -400,10 +402,10 @@ class Controller { } } - $data = $user->getSessionCharacterData($requestedCharacterId); + $character = $user->getSessionCharacter($requestedCharacterId, $ttl); } - return $data; + return $character; } /** @@ -412,24 +414,8 @@ class Controller { * @return Pathfinder\CharacterModel|null * @throws \Exception */ - public function getCharacter(int $ttl = 0) : ?Pathfinder\CharacterModel { - $character = null; - - if(!empty($characterData = $this->getSessionCharacterData())){ - /** - * @var $characterModel Pathfinder\CharacterModel - */ - $characterModel = Pathfinder\AbstractPathfinderModel::getNew('CharacterModel'); - $characterModel->getById((int)$characterData['ID'], $ttl); - if( - !$characterModel->dry() && - $characterModel->hasUserCharacter() - ){ - $character = &$characterModel; - } - } - - return $character; + public function getCharacter(int $ttl = AbstractModel::DEFAULT_SQL_TTL) : ?Pathfinder\CharacterModel { + return $this->getSessionCharacter($ttl); } /** @@ -438,7 +424,7 @@ class Controller { * @return Pathfinder\UserModel|null * @throws \Exception */ - public function getUser($ttl = 0) : ?Pathfinder\UserModel { + public function getUser($ttl = AbstractModel::DEFAULT_SQL_TTL) : ?Pathfinder\UserModel { $user = null; if($this->getF3()->exists(Api\User::SESSION_KEY_USER_ID, $userId)){ @@ -452,7 +438,7 @@ class Controller { !$userModel->dry() && $userModel->hasUserCharacters() ){ - $user = &$userModel; + $user = $userModel; } } @@ -534,10 +520,11 @@ class Controller { * @throws \Exception */ public function getEveServerStatus(\Base $f3){ + $ttl = 60; $esiStatusVersion = 'latest'; $cacheKey = 'eve_server_status'; - if( !$f3->exists($cacheKey, $return) ){ + if(!$exists = $f3->exists($cacheKey, $return)){ $return = (object) []; $return->error = []; @@ -593,10 +580,10 @@ class Controller { // find top status $status = 'OK'; $color = 'green'; - foreach($apiStatus['status'] as $statusData){ + foreach($apiStatus['status'] as &$statusData){ if('red' == $statusData['status']){ $status = 'unstable'; - $color = $statusData['status']; + $color = $statusData['status'] = 'orange'; // red is already in use for fatal API errors (e.g. no response at all, or offline) break; } if('yellow' == $statusData['status']){ @@ -613,11 +600,15 @@ class Controller { } if(empty($return->error)){ - $f3->set($cacheKey, $return, 60); + $f3->set($cacheKey, $return, $ttl); } } } + if(empty($return->error)){ + $f3->expire(Config::ttlLeft($exists, $ttl)); + } + echo json_encode($return); } diff --git a/app/main/controller/logcontroller.php b/app/main/controller/logcontroller.php index ebe47a49..1fc900b3 100644 --- a/app/main/controller/logcontroller.php +++ b/app/main/controller/logcontroller.php @@ -118,13 +118,13 @@ class LogController extends \Prefab { $updateSql = array_map($updateRule, $columnsForUpdate); $sql = "INSERT DELAYED INTO - activity_log (" . implode(', ', $columnsQuoted) . ") values( - " . implode(', ', $placeholder) . " - ) - ON DUPLICATE KEY UPDATE - updated = NOW(), - " . implode(', ', $updateSql) . " - "; + activity_log (" . implode(', ', $columnsQuoted) . ") VALUES( + " . implode(', ', $placeholder) . " + ) + ON DUPLICATE KEY UPDATE + updated = NOW(), + " . implode(', ', $updateSql) . " + "; $db->exec($sql, $args); } diff --git a/app/main/controller/setup.php b/app/main/controller/setup.php index a38eeba4..b7ebb57a 100644 --- a/app/main/controller/setup.php +++ b/app/main/controller/setup.php @@ -67,7 +67,6 @@ class Setup extends Controller { 'Model\Pathfinder\MapTypeModel', 'Model\Pathfinder\SystemTypeModel', 'Model\Pathfinder\SystemStatusModel', - 'Model\Pathfinder\SystemNeighbourModel', 'Model\Pathfinder\RightModel', 'Model\Pathfinder\RoleModel', 'Model\Pathfinder\StructureModel', @@ -105,19 +104,27 @@ class Setup extends Controller { 'UNIVERSE' => [ 'info' => [], 'models' => [ + 'Model\Universe\DogmaAttributeModel', + 'Model\Universe\TypeAttributeModel', 'Model\Universe\TypeModel', 'Model\Universe\GroupModel', 'Model\Universe\CategoryModel', 'Model\Universe\FactionModel', + 'Model\Universe\AllianceModel', + 'Model\Universe\CorporationModel', + 'Model\Universe\RaceModel', + 'Model\Universe\StationModel', 'Model\Universe\StructureModel', - 'Model\Universe\WormholeModel', 'Model\Universe\StargateModel', 'Model\Universe\StarModel', 'Model\Universe\PlanetModel', 'Model\Universe\SystemModel', 'Model\Universe\ConstellationModel', 'Model\Universe\RegionModel', - 'Model\Universe\SystemStaticModel' + 'Model\Universe\SystemNeighbourModel', + 'Model\Universe\SystemStaticModel', + 'Model\Universe\SovereigntyMapModel', + 'Model\Universe\FactionWarSystemModel' ] ] ]; @@ -1665,18 +1672,111 @@ class Setup extends Controller { * @var $categoryUniverseModel Universe\CategoryModel */ $categoryUniverseModel = Universe\AbstractUniverseModel::getNew('CategoryModel'); - $categoryUniverseModel->getById(65, 0); - $structureCount = $categoryUniverseModel->getTypesCount(false); + $categoryUniverseModel->getById(Config::ESI_CATEGORY_STRUCTURE_ID, 0); + $groupsCountStructure = $categoryUniverseModel->getGroupsCount(false); + $typesCountStructure = $categoryUniverseModel->getTypesCount(false); - $categoryUniverseModel->getById(6, 0); - $shipCount = $categoryUniverseModel->getTypesCount(false); + $categoryUniverseModel->getById(Config::ESI_CATEGORY_SHIP_ID, 0); + $groupsCountShip = $categoryUniverseModel->getGroupsCount(false); + $typesCountShip = $categoryUniverseModel->getTypesCount(false); /** - * @var $systemNeighbourModel Pathfinder\SystemNeighbourModel + * @var $groupUniverseModel Universe\GroupModel */ - $systemNeighbourModel = Pathfinder\AbstractPathfinderModel::getNew('SystemNeighbourModel'); + + $groupUniverseModel = Universe\AbstractUniverseModel::getNew('GroupModel'); + $groupUniverseModel->getById(Config::ESI_GROUP_WORMHOLE_ID, 0); + $wormholeCount = $groupUniverseModel->getTypesCount(false); + + /** + * @var $systemNeighbourModel Universe\SystemNeighbourModel + */ + $systemNeighbourModel = Universe\AbstractUniverseModel::getNew('SystemNeighbourModel'); + + /** + * @var $systemStaticModel Universe\SystemStaticModel + */ + $systemStaticModel = Universe\AbstractUniverseModel::getNew('SystemStaticModel'); + + if(empty($systemCountAll = count(($universeController = new UniverseController())->getSystemIds(true)))){ + // no systems found in 'universe' DB. Clear potential existing system cache + $universeController->clearSystemsIndex(); + } + + $sum = function(int $carry, int $value) : int { + return $carry + $value; + }; $indexInfo = [ + 'Wormholes' => [ + 'task' => [ + [ + 'action' => 'buildIndex', + 'label' => 'Import', + 'icon' => 'fa-sync', + 'btn' => 'btn-primary' + ] + ], + 'label' => 'Wormholes data', + 'countBuild' => $wormholeCount, + 'countAll' => count(Universe\GroupModel::getUniverseGroupTypes(Config::ESI_GROUP_WORMHOLE_ID)), + 'tooltip' => 'import all wormhole types (e.g. L031) from ESI. Runtime: ~25s' + ], + 'Structures' => [ + 'task' => [ + [ + 'action' => 'buildIndex', + 'label' => 'Import', + 'icon' => 'fa-sync', + 'btn' => 'btn-primary' + ] + ], + 'label' => 'Structures data', + 'countBuild' => $groupsCountStructure, + 'countAll' => count(Universe\CategoryModel::getUniverseCategoryGroups(Config::ESI_CATEGORY_STRUCTURE_ID)), + 'tooltip' => 'import all structure types (e.g. Citadels) from ESI. Runtime: ~15s', + 'subCount' => [ + 'countBuild' => $typesCountStructure, + 'countAll' => array_reduce(array_map('count', Universe\CategoryModel::getUniverseCategoryTypes(Config::ESI_CATEGORY_STRUCTURE_ID)), $sum, 0), + ] + ], + 'Ships' => [ + 'task' => [ + [ + 'action' => 'buildIndex', + 'label' => 'Import', + 'icon' => 'fa-sync', + 'btn' => 'btn-primary' + ] + ], + 'label' => 'Ships data', + 'countBuild' => $groupsCountShip, + 'countAll' => count(Universe\CategoryModel::getUniverseCategoryGroups(Config::ESI_CATEGORY_SHIP_ID)), + 'tooltip' => 'import all ships from ESI. Runtime: ~2min', + 'subCount' => [ + 'countBuild' => $typesCountShip, + 'countAll' => array_reduce(array_map('count', Universe\CategoryModel::getUniverseCategoryTypes(Config::ESI_CATEGORY_SHIP_ID)), $sum, 0), + ] + ], + 'SystemStatic' => [ + 'task' => [ + [ + 'action' => 'buildIndex', + 'label' => 'Import', + 'icon' => 'fa-sync', + 'btn' => 'btn-primary' + ] + ], + 'label' => 'Wormhole statics data', + 'countBuild' => $systemStaticModel->getRowCount(), + 'countAll' => 3772, + 'tooltip' => 'import all static wormholes for systems. Runtime: ~25s' + ], + [ + 'label' => 'Build search index', + 'icon' => 'fa-search', + 'tooltip' => 'Search indexes are build from static EVE universe data (e.g. systems, stargate connections,…). Re-build if underlying data was updated.' + ], 'Systems' => [ 'task' => [ [ @@ -1691,81 +1791,36 @@ class Setup extends Controller { 'btn' => 'btn-primary' ] ], - 'label' => 'build systems index', - 'countBuild' => count((new UniverseController())->getSystemsIndex()), - 'countAll' => count((new UniverseController())->getSystemIds()), - 'tooltip' => 'build up a static search index over all systems found on DB. Do not refresh page until import is complete (check progress)! Runtime: ~5min' - ], - 'Structures' => [ - 'task' => [ - [ - 'action' => 'buildIndex', - 'label' => 'Import', - 'icon' => 'fa-sync', - 'btn' => 'btn-primary' - ] - ], - 'label' => 'import structures data', - 'countBuild' => $structureCount, - 'countAll' => (int)$f3->get('REQUIREMENTS.DATA.STRUCTURES'), - 'tooltip' => 'import all structure types (e.g. Citadels) from ESI. Runtime: ~15s' - ], - 'Ships' => [ - 'task' => [ - [ - 'action' => 'buildIndex', - 'label' => 'Import', - 'icon' => 'fa-sync', - 'btn' => 'btn-primary' - ] - ], - 'label' => 'import ships data', - 'countBuild' => $shipCount, - 'countAll' => (int)$f3->get('REQUIREMENTS.DATA.SHIPS'), - 'tooltip' => 'import all ships types from ESI. Runtime: ~2min' + 'label' => 'Systems data index', + 'countBuild' => count($universeController->getSystemsIndex()), + 'countAll' => $systemCountAll, + 'tooltip' => 'Build up a static search index over all systems, found on DB. Runtime: ~5min' ], 'SystemNeighbour' => [ 'task' => [ [ + 'action' => 'clearIndex', + 'label' => 'Clear', + 'icon' => 'fa-trash', + 'btn' => 'btn-danger' + ],[ 'action' => 'buildIndex', 'label' => 'Build', 'icon' => 'fa-sync', 'btn' => 'btn-primary' ] ], - 'label' => 'build neighbour index', - 'countBuild' => $f3->DB->getDB('PF')->getRowCount($systemNeighbourModel->getTable()), + 'label' => 'Systems neighbour index', + 'countBuild' => $systemNeighbourModel->getRowCount(), 'countAll' => (int)$f3->get('REQUIREMENTS.DATA.NEIGHBOURS'), - 'tooltip' => 'build up a static search index for route search. This is used as fallback in case ESI is down. Runtime: ~30s' - - ], - // All following rows become deprecated - /* - 'WormholeModel' => [ - 'task' => [ - [ - 'action' => 'exportTable', - 'label' => 'Export', - 'icon' => 'fa-download', - 'btn' => 'btn-default' - ],[ - 'action' => 'importTable', - 'label' => 'Import', - 'icon' => 'fa-upload', - 'btn' => 'btn-primary' - ] - ], - 'label' => 'wormhole', - 'countBuild' => $f3->DB->getDB('PF')->getRowCount($wormholeModel->getTable()), - 'countAll' => 89 + 'tooltip' => 'Build up a static search index for route search. This is used as fallback in case ESI is down. Runtime: ~10s' ] - */ ]; }else{ $indexInfo = [ - 'SystemNeighbour' => [ - 'task' => [], - 'label' => 'Fix database errors first!' + [ + 'label' => 'Fix database errors first!', + 'class' => 'txt-color-danger text-center' ] ]; } diff --git a/app/main/cron/abstractcron.php b/app/main/cron/abstractcron.php index d98c7706..2a758883 100644 --- a/app/main/cron/abstractcron.php +++ b/app/main/cron/abstractcron.php @@ -10,13 +10,21 @@ namespace cron; abstract class AbstractCron { - // default max_execution_time for cronJobs + /** + * default max_execution_time for cronJobs // -> should be less then execution period + */ const DEFAULT_MAX_EXECUTION_TIME = 50; /** - * set max execution time for cronjobs - * -> Default CLI execution time is "0" -> infinite! + * default threshold time in seconds before a running script (e.g. a large loop) should stop + * -> so there is some time or e.g. logging,... left + */ + const DEFAULT_EXECUTION_TIME_THRESHOLD = 3; + + /** + * set max execution time for cronJbs + * -> Default CLI execution time is 0 == infinite! * php.ini settings are ignored! http://php.net/manual/en/info.configuration.php#ini.max-execution-time * @param int $time */ @@ -24,4 +32,33 @@ abstract class AbstractCron { ini_set('max_execution_time', $time); } + /** + * get max execution time + * -> 0 means == infinite! + * @return int + */ + protected function getMaxExecutionTime() : int { + return (int)ini_get('max_execution_time'); + } + + /** + * checks execution time of a "long" running script + * -> returns false if execution time is close to maxExecutionTime + * @param float $timeTotalStart + * @param float|null $timeCheck + * @param int $timeThreshold + * @return bool + */ + protected function isExecutionTimeLeft(float $timeTotalStart, float $timeCheck = null, int $timeThreshold = self::DEFAULT_EXECUTION_TIME_THRESHOLD) : bool { + $timeLeft = true; + if($timeTotalMax = $this->getMaxExecutionTime()){ + $timeTotalMaxThreshold = $timeTotalStart + $timeTotalMax - $timeThreshold; + $timeCheck = $timeCheck ? : microtime(true); + if($timeCheck >= $timeTotalMaxThreshold){ + $timeLeft = false; + } + } + return $timeLeft; + } + } \ No newline at end of file diff --git a/app/main/cron/characterupdate.php b/app/main/cron/characterupdate.php index b1cb2055..6208d0e0 100644 --- a/app/main/cron/characterupdate.php +++ b/app/main/cron/characterupdate.php @@ -65,7 +65,7 @@ class CharacterUpdate extends AbstractCron { */ if(is_object($characterLog->characterId)){ if($accessToken = $characterLog->characterId->getAccessToken()){ - if($this->isOnline($accessToken)){ + if($characterLog->characterId->isOnline($accessToken)){ // force characterLog as "updated" even if no changes were made $characterLog->touch('updated'); $characterLog->save(); diff --git a/app/main/cron/universe.php b/app/main/cron/universe.php index b39d06a7..1dc79fb1 100644 --- a/app/main/cron/universe.php +++ b/app/main/cron/universe.php @@ -13,6 +13,7 @@ use Model; class Universe extends AbstractCron { const LOG_TEXT = '%s type: %s %s/%s peak: %s total: %s msg: %s'; + const LOG_TEXT_SOV_FW = '%s %4s/%-4s checked, %s peak, %s total, %4s updated [%4s sovChanges, %4s fwChanges], msg: %s'; /** @@ -133,10 +134,11 @@ class Universe extends AbstractCron { * @param float $timeTotalStart */ private function echoLoaded(int $importCount, int $id, float $timeLoopStart, float $timeTotalStart){ + $time = microtime(true); echo '[' . date('H:i:s') . '] loaded ' . str_pad('', strlen($importCount), ' ') . ' id: ' . $this->formatIdValue($id) . ' memory: ' . $this->formatMemoryValue(memory_get_usage()) . - ' time: ' . $this->formatSeconds(microtime(true) - $timeLoopStart) . - ' total: ' . $this->formatSeconds(microtime(true) - $timeTotalStart) . PHP_EOL; + ' time: ' . $this->formatSeconds($time - $timeLoopStart) . + ' total: ' . $this->formatSeconds($time - $timeTotalStart) . PHP_EOL; $this->echoFlush(); } @@ -169,8 +171,8 @@ class Universe extends AbstractCron { $msg = ''; $ids = []; + $importCount = 0; $count = 0; - $importCount = []; $modelClass = ''; $setupModel = function(Model\Universe\AbstractUniverseModel &$model, int $id){}; @@ -193,12 +195,51 @@ class Universe extends AbstractCron { $model->loadStargatesData(); }; break; + case 'station': + $ids = $f3->ccpClient()->getUniverseSystems(); + $modelClass = 'SystemModel'; + $setupModel = function(Model\Universe\SystemModel &$model, int $id){ + if($model->getById($id)){ + $model->loadStationsData(); + }else{ + echo 'NOT VALID ' . $id . PHP_EOL; + die(); + } + }; + break; + case 'sovereignty': + // load sovereignty map data. Systems must be present first! + $sovData = $f3->ccpClient()->getSovereigntyMap(); + $ids = !empty($sovData = $sovData['map']) ? array_keys($sovData): []; + $modelClass = 'SystemModel'; + $setupModel = function(Model\Universe\SystemModel &$model, int $id) use ($sovData) { + if($model->getById($id)){ + $model->updateSovereigntyData($sovData[$id]); + }else{ + echo 'NOT VALID ' . $id . PHP_EOL; + die(); + } + }; + break; + case 'faction_war_systems': + $fwSystems = $f3->ccpClient()->getFactionWarSystems(); + $ids = !empty($fwSystems = $fwSystems['systems']) ? array_keys($fwSystems): []; + $modelClass = 'SystemModel'; + $setupModel = function(Model\Universe\SystemModel &$model, int $id) use ($fwSystems) { + if($model->getById($id)){ + $model->updateFactionWarData($fwSystems[$id]); + }else{ + echo 'NOT VALID ' . $id . PHP_EOL; + die(); + } + }; + break; case 'index_system': // setup system index, Systems must be present first! $ids = $f3->ccpClient()->getUniverseSystems(); $modelClass = 'SystemModel'; $setupModel = function(Model\Universe\SystemModel &$model, int $id){ - $model->getById($id); // no loadById() here! would take "forever" when system not exists and build up first... + $model->getById($id); // no loadById() here! would take "forever" when system not exists and must be build up first... $model->buildIndex(); }; break; @@ -216,6 +257,7 @@ class Universe extends AbstractCron { sort($ids, SORT_NUMERIC); $ids = array_slice($ids, $offset, $length); $importCount = count($ids); + $count = 0; $this->echoInfo($total, $offset, $importCount, $ids); $this->echoStart(); @@ -237,9 +279,83 @@ class Universe extends AbstractCron { // Log -------------------------------------------------------------------------------------------------------- $log = new \Log('cron_' . __FUNCTION__ . '.log'); - $log->write( sprintf(self::LOG_TEXT, __FUNCTION__, $type, + $log->write(sprintf(self::LOG_TEXT, __FUNCTION__, $type, $this->formatCounterValue($count), $importCount, $this->formatMemoryValue(memory_get_peak_usage ()), - $this->formatSeconds(microtime(true) - $timeTotalStart), $msg) ); + $this->formatSeconds(microtime(true) - $timeTotalStart), $msg)); + } + + /** + * update Sovereignty system data from ESI + * -> this updates Faction warfare data as well + * >> php index.php "/cron/updateSovereigntyData" + * @param \Base $f3 + * @throws \Exception + */ + function updateSovereigntyData(\Base $f3){ + $this->setMaxExecutionTime(); + $timeTotalStart = microtime(true); + $msg = ''; + + /** + * @var $system Model\Universe\SystemModel + */ + $system = Model\Universe\AbstractUniverseModel::getNew('SystemModel'); + + $sovData = $f3->ccpClient()->getSovereigntyMap(); + $fwSystems = $f3->ccpClient()->getFactionWarSystems(); + $fwSystems = $fwSystems['systems']; + $ids = !empty($sovData = $sovData['map']) ? array_keys($sovData): []; + sort($ids, SORT_NUMERIC); + $importCount = count($ids); + $count = 0; + + $changes = []; + foreach($ids as $id){ + $count++; + + // skip wormhole systems -> can not have sov data + // -> even though they are returned from sovereignty/map endpoint?! + if( + $system->getById($id, 0) && + strpos($system->security, 'C') === false + ){ + if($changedSovData = $system->updateSovereigntyData($sovData[$id])){ + $changes['sovereignty'][] = $id; + } + + $changedFwData = false; + if(is_array($fwSystems[$id])){ + if($changedFwData = $system->updateFactionWarData($fwSystems[$id])){ + $changes['factionWarfare'][] = $id; + } + } + + if($changedSovData || $changedFwData){ + $system->buildIndex(); + } + } + $system->reset(); + + // stop loop if runtime gets close to "max_execution_time" + // -> we need some time for writing *.log file + if(!$this->isExecutionTimeLeft($timeTotalStart)){ + $msg = 'Script execution stopped due to "max_execution_time" limit reached'; + // TODO store current loop index and use it as new "offset" for next call + break; + } + } + + $changedIds = array_reduce($changes, function(array $reducedIds, array $changedIds) : array { + return array_unique(array_merge($reducedIds, $changedIds)); + }, []); + + // Log ------------------------ + $log = new \Log('cron_' . __FUNCTION__ . '.log'); + $log->write(sprintf(self::LOG_TEXT_SOV_FW, __FUNCTION__, + $count, $importCount, $this->formatMemoryValue(memory_get_peak_usage ()), + $this->formatSeconds(microtime(true) - $timeTotalStart), + count($changedIds), count($changes['sovereignty'] ? : []), count($changes['factionWarfare'] ? : []), + $msg)); } /** @@ -251,9 +367,12 @@ class Universe extends AbstractCron { */ function updateUniverseSystems(\Base $f3){ $this->setMaxExecutionTime(); - - $system = Model\Universe\AbstractUniverseModel::getNew('SystemModel'); - $systems = $system->find( null, ['order' => 'updated', 'limit' => 2]); + /** + * @var $systemModel Model\Universe\SystemModel + * @var $system Model\Universe\SystemModel + */ + $systemModel = Model\Universe\AbstractUniverseModel::getNew('SystemModel'); + $systems = $systemModel->find( null, ['order' => 'updated', 'limit' => 2]); if($systems){ foreach ($systems as $system){ $system->updateModel(); diff --git a/app/main/lib/PriorityCacheStore.php b/app/main/lib/PriorityCacheStore.php new file mode 100644 index 00000000..740d0a32 --- /dev/null +++ b/app/main/lib/PriorityCacheStore.php @@ -0,0 +1,124 @@ + truncate store after 10 inserts. Max store entries: + * DEFAULT_ENTRY_LIMIT + DEFAULT_CLEANUP_INTERVAL - 1 + */ + const DEFAULT_CLEANUP_INTERVAL = 10; + + /** + * @var int + */ + protected $entryLimit; + + /** + * @var int + */ + protected $cleanupInterval; + + /** + * @var array + */ + protected $store; + + /** + * @var \SplPriorityQueue + */ + protected $priorityQueue; + + /** + * @var int + */ + protected $priority = 0; + + /** + * PriorityCacheStore constructor. + * @param int $entryLimit + * @param int $cleanupInterval + */ + function __construct(int $entryLimit = self::DEFAULT_ENTRY_LIMIT, int $cleanupInterval = self::DEFAULT_CLEANUP_INTERVAL){ + $this->cleanupInterval = $cleanupInterval; + $this->entryLimit = $entryLimit; + $this->store = []; + $this->priorityQueue = new \SplPriorityQueue (); + $this->priorityQueue->setExtractFlags(\SplPriorityQueue::EXTR_BOTH); + } + + /** + * @param $key + * @param $data + */ + public function set($key, $data){ + if(!$this->exists($key)){ + $this->priorityQueue->insert($key, $this->priority--); + } + + $this->store[$key] = $data; + + // check cleanup interval and cleanup Store + $this->cleanupInterval(); + } + + /** + * @param $key + * @return mixed|null + */ + public function get($key){ + return $this->exists($key) ? $this->store[$key] : null; + } + + /** + * @param $key + * @return bool + */ + public function exists($key){ + return isset($this->store[$key]); + } + + public function cleanupInterval() : void { + if( + !$this->priorityQueue->isEmpty() && $this->cleanupInterval && + ($this->priorityQueue->count() % $this->cleanupInterval === 0) + ){ + $this->cleanup(); + } + } + + public function cleanup(){ + while( + $this->entryLimit < $this->priorityQueue->count() && + $this->priorityQueue->valid() + ){ + if($this->exists($key = $this->priorityQueue->extract()['data'])){ + unset($this->store[$key]); + } + } + } + + public function clear(){ + $limit = $this->entryLimit; + $this->entryLimit = 0; + $this->cleanup(); + // restore entryLimit for next data + $this->entryLimit = $limit; + } + + /** + * @return string + */ + public function __toString(){ + return 'Store count: ' . count($this->store) . ' priorityQueue count: ' . $this->priorityQueue->count(); + } +} \ No newline at end of file diff --git a/app/main/lib/config.php b/app/main/lib/config.php index 1ce9ee9d..c87db59b 100644 --- a/app/main/lib/config.php +++ b/app/main/lib/config.php @@ -82,6 +82,30 @@ class Config extends \Prefab { */ const DOWNTIME_BUFFER = 1; + // ================================================================================================================ + // ESI API id´s + // ================================================================================================================ + + /** + * ESI categoryId or 'Structure's + */ + const ESI_CATEGORY_STRUCTURE_ID = 65; + + /** + * ESI categoryId or 'Ship's + */ + const ESI_CATEGORY_SHIP_ID = 6; + + /** + * ESI groupId or 'Wormhole's + */ + const ESI_GROUP_WORMHOLE_ID = 988; + + /** + * ESI dogmaAttributeId for 'scanWormholeStrength's + */ + const ESI_DOGMA_ATTRIBUTE_SCANWHSTRENGTH_ID = 1908; + /** * error message for missing Composer dependency class */ @@ -166,7 +190,7 @@ class Config extends \Prefab { * @return array|null */ protected function getAllEnvironmentData(\Base $f3){ - if( !$f3->exists(self::HIVE_KEY_ENVIRONMENT, $environmentData) ){ + if(!$f3->exists(self::HIVE_KEY_ENVIRONMENT, $environmentData)){ $environmentData = $this->setAllEnvironmentData($f3); } @@ -320,7 +344,7 @@ class Config extends \Prefab { if($config['SCHEME'] == 'mysql'){ $options[\PDO::MYSQL_ATTR_COMPRESS] = true; $options[\PDO::MYSQL_ATTR_INIT_COMMAND] = implode(',', [ - "SET NAMES " . strtolower(str_replace('-','', $f3->ENCODING)), + "SET NAMES " . self::getRequiredDbVars($f3, $config['SCHEME'])['CHARACTER_SET_CONNECTION'] . " COLLATE " . self::getRequiredDbVars($f3, $config['SCHEME'])['COLLATION_CONNECTION'], "@@session.time_zone = '+00:00'", "@@session.default_storage_engine = " . self::getRequiredDbVars($f3, $config['SCHEME'])['DEFAULT_STORAGE_ENGINE'] ]); @@ -616,4 +640,18 @@ class Config extends \Prefab { return $format; } + static function ttlLeft($fromExists, int $ttlMax) : int { + $ttlMax = max($ttlMax, 0); + if($fromExists){ + // == true || array + if(is_array($fromExists)){ + return max(min((int)ceil(round(array_sum($fromExists) - microtime(true), 4)), $ttlMax), 0); + }else{ + return 0; + } + }else{ + // == false + return $ttlMax; + } + } } \ No newline at end of file diff --git a/app/main/lib/db/Pool.php b/app/main/lib/db/Pool.php index a3cbd545..2a415f92 100644 --- a/app/main/lib/db/Pool.php +++ b/app/main/lib/db/Pool.php @@ -110,8 +110,8 @@ class Pool extends \Prefab { $schema = new Schema($db); if(!in_array($newDbName, $schema->getDatabases())){ $db->exec("CREATE DATABASE IF NOT EXISTS - `" . $newDbName . "` DEFAULT CHARACTER SET utf8 - COLLATE utf8_general_ci;"); + `" . $newDbName . "` DEFAULT CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci;"); $db->exec("USE `" . $newDbName . "`"); // check if DB create was successful diff --git a/app/main/lib/util.php b/app/main/lib/util.php index d0b4b32e..8d031de6 100644 --- a/app/main/lib/util.php +++ b/app/main/lib/util.php @@ -51,12 +51,30 @@ class Util { return $return; } + /** + * transforms array with assoc. arrays as values + * into assoc. array where $key column data is used for its key + * @param array $array + * @param string $key + * @param bool $unsetKey + * @return array + */ + static function arrayGetBy(array $array, string $key, bool $unsetKey = true) : array { + // we can remove $key from nested arrays + return array_map(function($val) use ($key, $unsetKey) : array { + if($unsetKey){ + unset($val[$key]); + } + return $val; + }, array_column($array, null, $key)); + } + /** * checks whether an array is associative or not (sequential) * @param mixed $array * @return bool */ - static function is_assoc($array): bool { + static function is_assoc($array) : bool { $isAssoc = false; if( is_array($array) && @@ -109,7 +127,7 @@ class Util { * @param int $maxHideChars * @return string */ - static function obscureString(string $string, int $maxHideChars = 10): string { + static function obscureString(string $string, int $maxHideChars = 10) : string { $formatted = ''; $length = mb_strlen((string)$string); if($length > 0){ @@ -125,7 +143,7 @@ class Util { * @param array $scopes * @return string */ - static function getHashFromScopes($scopes){ + static function getHashFromScopes($scopes) : string { $scopes = (array)$scopes; sort($scopes); return md5(serialize($scopes)); diff --git a/app/main/model/abstractmodel.php b/app/main/model/abstractmodel.php index 7102f198..e93af259 100644 --- a/app/main/model/abstractmodel.php +++ b/app/main/model/abstractmodel.php @@ -11,6 +11,7 @@ namespace Model; use DB\Cortex; use DB\CortexCollection; use DB\SQL\Schema; +use lib\Util; use lib\logging; use Controller; use Exception\ValidationException; @@ -44,6 +45,14 @@ abstract class AbstractModel extends Cortex { */ protected $addStaticFields = true; + /** + * enables table truncate + * -> see truncate(); + * -> CAUTION! if set to true truncate() will clear ALL rows! + * @var bool + */ + protected $allowTruncate = false; + /** * enables change for "active" column * -> see setActive(); @@ -89,6 +98,23 @@ abstract class AbstractModel extends Cortex { */ const DEFAULT_CACHE_TTL = 120; + /** + * default TTL or temp table data read from *.csv file + * -> used during data import + */ + const DEFAULT_CACHE_CSV_TTL = 120; + + /** + * cache key prefix name for "full table" indexing + * -> used e.g. for a "search" index; or "import" index for *.csv imports + */ + const CACHE_KEY_PREFIX = 'INDEX'; + + /** + * cache key name for temp data import from *.csv files per table + */ + const CACHE_KEY_CSV_PREFIX = 'CSV'; + /** * default TTL for SQL query cache */ @@ -495,7 +521,7 @@ abstract class AbstractModel extends Cortex { * @return bool */ public function getById(int $id, int $ttl = self::DEFAULT_SQL_TTL, bool $isActive = true) : bool { - return $this->getByForeignKey('id', $id, ['limit' => 1], $ttl, $isActive); + return $this->getByForeignKey($this->primary, $id, ['limit' => 1], $ttl, $isActive); } /** @@ -508,10 +534,7 @@ abstract class AbstractModel extends Cortex { * @return bool */ public function getByForeignKey(string $key, $value, array $options = [], int $ttl = 0, bool $isActive = true) : bool { - $filters = []; - if($this->exists($key)){ - $filters[] = [$key . ' = :' . $key, ':' . $key => $value]; - } + $filters = [self::getFilter($key, $value)]; if($isActive && $this->exists('active')){ $filters[] = self::getFilter('active', true); @@ -658,6 +681,24 @@ abstract class AbstractModel extends Cortex { return true; } + /** + * get row count in this table + * @return int + */ + public function getRowCount() : int { + return is_object($this->db) ? $this->db->getRowCount($this->getTable()) : 0; + } + + /** + * truncate all table rows + * -> Use with Caution!!! + */ + public function truncate(){ + if($this->allowTruncate && is_object($this->db)){ + $this->db->exec("TRUNCATE " . $this->getTable()); + } + } + /** * format dateTime column * @param $column @@ -716,43 +757,78 @@ abstract class AbstractModel extends Cortex { } /** - * import table data from a *.csv file - * @return array|bool + * read *.csv file for a $table name + * -> 'group' by $getByKey column name and return array + * @param string $table + * @param string $getByKey + * @return array */ - public function importData(){ - $status = false; + public static function getCSVData(string $table, string $getByKey = 'id') : array { + $hashKeyTableCSV = static::generateHashKeyTable($table, static::CACHE_KEY_PREFIX . '_' . self::CACHE_KEY_CSV_PREFIX); + + if( + !self::getF3()->exists($hashKeyTableCSV, $tableData) && + !empty($tableData = Util::arrayGetBy(self::loadCSV($table), $getByKey, false)) + ){ + self::getF3()->set($hashKeyTableCSV, $tableData, self::DEFAULT_CACHE_CSV_TTL); + } + + return $tableData; + } + + /** + * load data from *.csv file + * @param string $fileName + * @return array + */ + protected static function loadCSV(string $fileName) : array { + $tableData = []; // rtrim(); for arrays (removes empty values) from the end - $rtrim = function($array = [], $lengthMin = false){ + $rtrim = function($array = [], $lengthMin = false) : array { $length = key(array_reverse(array_diff($array, ['']), 1))+1; $length = $length < $lengthMin ? $lengthMin : $length; return array_slice($array, 0, $length); }; - if(static::$enableDataImport){ - $filePath = $this->getF3()->get('EXPORT') . 'csv/' . $this->getTable() . '.csv'; - + if($fileName){ + $filePath = self::getF3()->get('EXPORT') . 'csv/' . $fileName . '.csv'; if(is_file($filePath)){ $handle = @fopen($filePath, 'r'); $keys = array_map('lcfirst', fgetcsv($handle, 0, ';')); $keys = $rtrim($keys); if(count($keys) > 0){ - $tableData = []; while (!feof($handle)) { $tableData[] = array_combine($keys, $rtrim(fgetcsv($handle, 0, ';'), count($keys))); } - // import row data - $status = $this->importStaticData($tableData); - $this->getF3()->status(202); }else{ - $this->getF3()->error(500, 'File could not be read'); + self::getF3()->error(500, 'File could not be read'); } }else{ - $this->getF3()->error(404, 'File not found: ' . $filePath); + self::getF3()->error(404, 'File not found: ' . $filePath); } } + return $tableData; + } + + /** + * import table data from a *.csv file + * @return array|bool + */ + public function importData(){ + $status = false; + + if( + static::$enableDataImport && + !empty($tableData = self::loadCSV($this->getTable())) + ){ + // import row data + $status = $this->importStaticData($tableData); + $this->getF3()->status(202); + } + return $status; } @@ -896,6 +972,15 @@ abstract class AbstractModel extends Cortex { return \Base::instance(); } + /** + * get model data as array + * @param $data + * @return array + */ + public static function toArray($data) : array { + return json_decode(json_encode($data), true); + } + /** * get new filter array representation * -> $suffix can be used fore unique placeholder, @@ -1019,6 +1104,17 @@ abstract class AbstractModel extends Cortex { return $model; } + /** + * generate hashKey for a complete table + * -> should hold hashKeys for multiple rows + * @param string $table + * @param string $prefix + * @return string + */ + public static function generateHashKeyTable(string $table, string $prefix = self::CACHE_KEY_PREFIX ) : string { + return $prefix . '_' . strtolower($table); + } + /** * overwrites parent * @param null $db diff --git a/app/main/model/pathfinder/alliancemapmodel.php b/app/main/model/pathfinder/alliancemapmodel.php index 2634d514..0152b5ff 100644 --- a/app/main/model/pathfinder/alliancemapmodel.php +++ b/app/main/model/pathfinder/alliancemapmodel.php @@ -12,8 +12,14 @@ use DB\SQL\Schema; class AllianceMapModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'alliance_map'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, diff --git a/app/main/model/pathfinder/alliancemodel.php b/app/main/model/pathfinder/alliancemodel.php index 1a792f5d..680dcacd 100644 --- a/app/main/model/pathfinder/alliancemodel.php +++ b/app/main/model/pathfinder/alliancemodel.php @@ -13,8 +13,14 @@ use lib\Config; class AllianceModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'alliance'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -145,7 +151,7 @@ class AllianceModel extends AbstractPathfinderModel { if($this->isOutdated()){ // request alliance data $allianceData = self::getF3()->ccpClient()->getAllianceData($id); - if( !empty($allianceData) ){ + if(!empty($allianceData) && !isset($allianceData['error'])){ $this->copyfrom($allianceData, ['id', 'name', 'ticker']); $this->save(); } diff --git a/app/main/model/pathfinder/characterlogmodel.php b/app/main/model/pathfinder/characterlogmodel.php index 64ce483f..1e4a6b10 100644 --- a/app/main/model/pathfinder/characterlogmodel.php +++ b/app/main/model/pathfinder/characterlogmodel.php @@ -96,6 +96,15 @@ class CharacterLogModel extends AbstractPathfinderModel { 'default' => '', 'activity-log' => true ], + 'structureTypeId' => [ + 'type' => Schema::DT_INT, + 'index' => true + ], + 'structureTypeName' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], 'structureId' => [ 'type' => Schema::DT_BIGINT, 'index' => true, @@ -146,9 +155,13 @@ class CharacterLogModel extends AbstractPathfinderModel { } if( isset($logData['structure']) ){ + $this->structureTypeId = (int)$logData['structure']['type']['id']; + $this->structureTypeName = $logData['structure']['type']['name']; $this->structureId = (int)$logData['structure']['id']; $this->structureName = $logData['structure']['name']; }else{ + $this->structureTypeId = null; + $this->structureTypeName = ''; $this->structureId = null; $this->structureName = ''; } @@ -178,20 +191,15 @@ class CharacterLogModel extends AbstractPathfinderModel { $logData->station->name = $this->stationName; $logData->structure = (object) []; + $logData->structure->type = (object) []; + $logData->structure->type->id = $this->structureTypeId; + $logData->structure->type->name = $this->structureTypeName; $logData->structure->id = (int)$this->structureId; $logData->structure->name = $this->structureName; return $logData; } - /** - * get 'character log' data as array - * @return array - */ - public function getDataAsArray() : array { - return json_decode(json_encode($this->getData()), true); - } - /** * Event "Hook" function * return false will stop any further action diff --git a/app/main/model/pathfinder/charactermodel.php b/app/main/model/pathfinder/charactermodel.php index 6e99a4cb..1b22bbf1 100644 --- a/app/main/model/pathfinder/charactermodel.php +++ b/app/main/model/pathfinder/charactermodel.php @@ -499,14 +499,7 @@ class CharacterModel extends AbstractPathfinderModel { * @return UserModel|null */ public function getUser() : ?UserModel { - $user = null; - if($this->hasUserCharacter()){ - /** - * @var $user UserModel - */ - $user = $this->userCharacter->userId; - } - return $user; + return $this->hasUserCharacter() ? $this->userCharacter->userId : null; } /** @@ -864,23 +857,22 @@ class CharacterModel extends AbstractPathfinderModel { if($this->isOnline($accessToken)){ $locationData = self::getF3()->ccpClient()->getCharacterLocationData($this->_id, $accessToken); - if( !empty($locationData['system']['id']) ){ + if(!empty($locationData['system']['id'])){ // character is currently in-game - // get current $characterLog or get new --------------------------------------------------- - if( !($characterLog = $this->getLog()) ){ + // get current $characterLog or get new ------------------------------------------------------- + if(!$characterLog = $this->getLog()){ // create new log $characterLog = $this->rel('characterLog'); } // get current log data and modify on change - $logData = $characterLog->getDataAsArray(); + $logData = $characterLog::toArray($characterLog->getData()); - // check system and station data for changes ---------------------------------------------- + // check system and station data for changes -------------------------------------------------- // IDs for "systemId", "stationId" that require more data $lookupUniverseIds = []; - if( empty($logData['system']['name']) || $logData['system']['id'] !== $locationData['system']['id'] @@ -889,33 +881,18 @@ class CharacterModel extends AbstractPathfinderModel { $lookupUniverseIds[] = $locationData['system']['id']; } - if( !empty($locationData['station']['id']) ){ - if( - empty($logData['station']['name']) || - $logData['station']['id'] !== $locationData['station']['id'] - ){ - // station changed -> request "station name" for current station - $lookupUniverseIds[] = $locationData['station']['id']; - } - }else{ - unset($logData['station']); - } - $logData = array_replace_recursive($logData, $locationData); - // get "more" data for systemId and/or stationId ----------------------------------------- - if( !empty($lookupUniverseIds) ){ + // get "more" data for systemId --------------------------------------------------------------- + if(!empty($lookupUniverseIds)){ // get "more" information for some Ids (e.g. name) $universeData = self::getF3()->ccpClient()->getUniverseNamesData($lookupUniverseIds); - if( !empty($universeData) && !isset($universeData['error']) ){ + if(!empty($universeData) && !isset($universeData['error'])){ // We expect max ONE system AND/OR station data, not an array of e.g. systems if(!empty($universeData['system'])){ $universeData['system'] = reset($universeData['system']); } - if(!empty($universeData['station'])){ - $universeData['station'] = reset($universeData['station']); - } $logData = array_replace_recursive($logData, $universeData); }else{ @@ -924,32 +901,63 @@ class CharacterModel extends AbstractPathfinderModel { } } - // check structure data for changes ------------------------------------------------------- + // check station data for changes ------------------------------------------------------------- if(!$deleteLog){ + // IDs for "stationId" that require more data + $lookupStationId = 0; + if(!empty($locationData['station']['id'])){ + if( + empty($logData['station']['name']) || + $logData['station']['id'] !== $locationData['station']['id'] + ){ + // station changed -> request station data + $lookupStationId = $locationData['station']['id']; + } + }else{ + unset($logData['station']); + } + // get "more" data for stationId + if($lookupStationId > 0){ + /** + * @var $stationModel Universe\StationModel + */ + $stationModel = Universe\AbstractUniverseModel::getNew('StationModel'); + $stationModel->loadById($lookupStationId, $accessToken, $additionalOptions); + if($stationModel->valid()){ + $stationData['station'] = $stationModel::toArray($stationModel->getData()); + $logData = array_replace_recursive($logData, $stationData); + }else{ + unset($logData['station']); + } + } + } + + // check structure data for changes ----------------------------------------------------------- + if(!$deleteLog){ // IDs for "structureId" that require more data $lookupStructureId = 0; - if( !empty($locationData['structure']['id']) ){ + if(!empty($locationData['structure']['id'])){ if( empty($logData['structure']['name']) || $logData['structure']['id'] !== $locationData['structure']['id'] ){ - // structure changed -> request "structure name" for current station + // structure changed -> request structure data $lookupStructureId = $locationData['structure']['id']; } }else{ unset($logData['structure']); } - // get "more" data for structureId --------------------------------------------------- + // get "more" data for structureId if($lookupStructureId > 0){ /** * @var $structureModel Universe\StructureModel */ $structureModel = Universe\AbstractUniverseModel::getNew('StructureModel'); $structureModel->loadById($lookupStructureId, $accessToken, $additionalOptions); - if(!$structureModel->dry()){ - $structureData['structure'] = (array)$structureModel->getData(); + if($structureModel->valid()){ + $structureData['structure'] = $structureModel::toArray($structureModel->getData()); $logData = array_replace_recursive($logData, $structureData); }else{ unset($logData['structure']); @@ -957,13 +965,13 @@ class CharacterModel extends AbstractPathfinderModel { } } - // check ship data for changes ------------------------------------------------------------ - if( !$deleteLog ){ + // check ship data for changes ---------------------------------------------------------------- + if(!$deleteLog){ $shipData = self::getF3()->ccpClient()->getCharacterShipData($this->_id, $accessToken); // IDs for "shipTypeId" that require more data $lookupShipTypeId = 0; - if( !empty($shipData['ship']['typeId']) ){ + if(!empty($shipData['ship']['typeId'])){ if( empty($logData['ship']['typeName']) || $logData['ship']['typeId'] !== $shipData['ship']['typeId'] @@ -980,7 +988,7 @@ class CharacterModel extends AbstractPathfinderModel { $invalidResponse = true; } - // get "more" data for shipTypeId ---------------------------------------------------- + // get "more" data for shipTypeId if($lookupShipTypeId > 0){ /** * @var $typeModel Universe\TypeModel @@ -997,7 +1005,7 @@ class CharacterModel extends AbstractPathfinderModel { } } - if( !$deleteLog ){ + if(!$deleteLog){ // mark log as "updated" even if no changes were made if($additionalOptions['markUpdated'] === true){ $characterLog->touch('updated'); @@ -1086,7 +1094,7 @@ class CharacterModel extends AbstractPathfinderModel { ){ $task = 'add'; $mapIds = []; - $historyLog = $characterLog->getDataAsArray(); + $historyLog = $characterLog::toArray($characterLog->getData()); if($logHistoryData = $this->getLogsHistory()){ // skip logging if no relevant fields changed diff --git a/app/main/model/pathfinder/connectionmodel.php b/app/main/model/pathfinder/connectionmodel.php index 6f361c6d..9eb3139e 100644 --- a/app/main/model/pathfinder/connectionmodel.php +++ b/app/main/model/pathfinder/connectionmodel.php @@ -14,8 +14,14 @@ use lib\logging; class ConnectionModel extends AbstractMapTrackingModel { + /** + * @var string + */ protected $table = 'connection'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, diff --git a/app/main/model/pathfinder/corporationmapmodel.php b/app/main/model/pathfinder/corporationmapmodel.php index 5cdcc571..8fe73e1c 100644 --- a/app/main/model/pathfinder/corporationmapmodel.php +++ b/app/main/model/pathfinder/corporationmapmodel.php @@ -12,8 +12,14 @@ use DB\SQL\Schema; class CorporationMapModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'corporation_map'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, diff --git a/app/main/model/pathfinder/corporationmodel.php b/app/main/model/pathfinder/corporationmodel.php index d464411d..b2a5cb71 100644 --- a/app/main/model/pathfinder/corporationmodel.php +++ b/app/main/model/pathfinder/corporationmodel.php @@ -13,6 +13,9 @@ use lib\Config; class CorporationModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'corporation'; /** @@ -91,6 +94,9 @@ class CorporationModel extends AbstractPathfinderModel { 'map_export' ]; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -181,17 +187,19 @@ class CorporationModel extends AbstractPathfinderModel { /** * get all maps for this corporation - * @param array $mapIds + * @param int|null $mapId * @param array $options * @return array */ - public function getMaps($mapIds = [], $options = []) : array { + public function getMaps(?int $mapId = null, $options = []) : array { $maps = []; $this->filterRel(); - if(!empty($mapIds)){ - $filters = []; - $filters[] = ['mapId IN (:mapId)', ':mapId' => $mapIds]; + if($mapId){ + $filters = [ + self::getFilter('mapId', $mapId) + ]; + $this->filter('mapCorporations', $this->mergeWithRelFilter('mapCorporations', $this->mergeFilter($filters)), $this->getRelFilterOption('mapCorporations')); } @@ -217,7 +225,7 @@ class CorporationModel extends AbstractPathfinderModel { * @param array $options * @return CharacterModel[] */ - public function getCharacters($characterIds = [], $options = []){ + public function getCharacters($characterIds = [], $options = []) : array { $characters = []; $filter = ['active = ?', 1]; @@ -244,30 +252,28 @@ class CorporationModel extends AbstractPathfinderModel { /** * get all structure data for this corporation - * @param array $systemIds + * @param int $systemId * @return array */ - public function getStructuresData(array $systemIds = []) : array { + public function getStructuresData(int $systemId) : array { $structuresData = []; + $structure = $this->rel('structures'); - $this->filter('corporationStructures', ['active = ?', 1]); - $this->has('corporationStructures.structureId', ['active = ?', 1]); + $filters = [ + self::getFilter('corporationId', $this->id), + self::getFilter('active', true) + ]; - if($systemIds){ - if(count($systemIds) == 1){ - $filterSystems = 'systemId = ?'; - $filterSystemIds = reset($systemIds); - }else{ - $filterSystems = 'systemId IN (?)'; - $filterSystemIds = $systemIds; - } + $structure->has('structureCorporations', $this->mergeFilter($filters)); - $this->has('corporationStructures.structureId', [$filterSystems, $filterSystemIds]); - } + $filters = [ + self::getFilter('systemId', $systemId), + self::getFilter('active', true) + ]; - if($this->corporationStructures) { - foreach($this->corporationStructures as $corporationStructure){ - $structuresData[] = $corporationStructure->structureId->getData(); + if($structures = $structure->find($this->mergeFilter($filters))){ + foreach($structures as $structure){ + $structuresData[] = $structure->getData(); } } @@ -351,7 +357,7 @@ class CorporationModel extends AbstractPathfinderModel { if($this->isOutdated()){ // request corporation data $corporationData = self::getF3()->ccpClient()->getCorporationData($id); - if( !empty($corporationData) ){ + if(!empty($corporationData) && !isset($corporationData['error'])){ // check for NPC corporation $corporationData['isNPC'] = self::getF3()->ccpClient()->isNpcCorporation($id); diff --git a/app/main/model/pathfinder/corporationstructuremodel.php b/app/main/model/pathfinder/corporationstructuremodel.php index 5dcfe47b..a9aab364 100644 --- a/app/main/model/pathfinder/corporationstructuremodel.php +++ b/app/main/model/pathfinder/corporationstructuremodel.php @@ -12,8 +12,14 @@ use DB\SQL\Schema; class CorporationStructureModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'corporation_structure'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, diff --git a/app/main/model/pathfinder/mapmodel.php b/app/main/model/pathfinder/mapmodel.php index e40ce578..2be5ad10 100644 --- a/app/main/model/pathfinder/mapmodel.php +++ b/app/main/model/pathfinder/mapmodel.php @@ -17,6 +17,9 @@ use lib\logging; class MapModel extends AbstractMapTrackingModel { + /** + * @var string + */ protected $table = 'map'; /** @@ -27,6 +30,9 @@ class MapModel extends AbstractMapTrackingModel { const ERROR_SLACK_CHANNEL = 'Invalid #Slack channel column [%s]'; const ERROR_DISCORD_CHANNEL = 'Invalid #Discord channel column [%s]'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -98,6 +104,12 @@ class MapModel extends AbstractMapTrackingModel { 'default' => 1, 'activity-log' => true ], + 'trackAbyssalJumps' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => false, + 'default' => 1, + 'activity-log' => true + ], 'logActivity' => [ 'type' => Schema::DT_BOOL, 'nullable' => false, @@ -223,6 +235,7 @@ class MapModel extends AbstractMapTrackingModel { $mapData->deleteEolConnections = $this->deleteEolConnections; $mapData->persistentAliases = $this->persistentAliases; $mapData->persistentSignatures = $this->persistentSignatures; + $mapData->trackAbyssalJumps = $this->trackAbyssalJumps; // map scope $mapData->scope = (object) []; @@ -563,41 +576,37 @@ class MapModel extends AbstractMapTrackingModel { } /** - * get either all system models in this map - * @return SystemModel[] + * get systems in this map + * @return CortexCollection|array */ protected function getSystems(){ - $systems = []; + $filters = [ + self::getFilter('active', true) + ]; - // orderBy x-Coordinate for smoother frontend animation (left to right) - $this->filter('systems', ['active = 1'], - ['order' => 'posX'] - ); - - if($this->systems){ - $systems = $this->systems; - } - - return $systems; + return $this->relFind('systems', $this->mergeFilter($filters)) ? : []; } /** * get all system data for all systems in this map * @return \stdClass[] - * @throws \Exception */ - public function getSystemsData() : array{ - $systemData = []; - $systems = $this->getSystems(); + public function getSystemsData() : array { + $systemsData = []; - foreach($systems as $system){ + foreach($this->getSystems() as $system){ /** * @var $system SystemModel */ - $systemData[] = $system->getData(); + $systemsData[] = $system->getData(); } - return $systemData; + // orderBy x-Coordinate for smoother frontend animation (left to right) + usort($systemsData, function($sysDataA, $sysDataB){ + return $sysDataA->position->x <=> $sysDataB->position->x; + }); + + return $systemsData; } /** @@ -650,25 +659,24 @@ class MapModel extends AbstractMapTrackingModel { * @return \stdClass[] */ public function getConnectionsData() : array { - $connectionData = []; - $connections = $this->getConnections(); + $connectionsData = []; - foreach($connections as $connection){ + foreach($this->getConnections() as $connection){ /** * @var $connection ConnectionModel */ - $connectionData[] = $connection->getData(true); + $connectionsData[] = $connection->getData(true); } - return $connectionData; + return $connectionsData; } /** * get all structures data for this map - * @param array $systemIds + * @param int $systemId * @return array */ - public function getStructuresData(array $systemIds = []) : array { + public function getStructuresData(int $systemId) : array { $structuresData = []; $corporations = $this->getAllCorporations(); @@ -676,7 +684,7 @@ class MapModel extends AbstractMapTrackingModel { // corporations should be unique if( !isset($structuresData[$corporation->_id]) ){ // get all structures for current corporation - $corporationStructuresData = $corporation->getStructuresData($systemIds); + $corporationStructuresData = $corporation->getStructuresData($systemId); if( !empty($corporationStructuresData) ){ // corporation has structures $structuresData[$corporation->_id] = [ @@ -819,11 +827,6 @@ class MapModel extends AbstractMapTrackingModel { $characters = []; $filter = ['active = ?', 1]; - if( !empty($characterIds) ){ - $filter[0] .= ' AND id IN (?)'; - $filter[] = $characterIds; - } - $this->filter('mapCharacters', $filter); if($this->mapCharacters){ @@ -839,7 +842,7 @@ class MapModel extends AbstractMapTrackingModel { * get corporations that have access to this map * @return CorporationModel[] */ - public function getCorporations() : array { + private function getCorporations() : array { $corporations = []; if($this->isCorporation()){ @@ -847,7 +850,7 @@ class MapModel extends AbstractMapTrackingModel { if($this->mapCorporations){ foreach($this->mapCorporations as $mapCorporation){ - $corporations[] = $mapCorporation->corporationId; + $corporations[$mapCorporation->corporationId->_id] = $mapCorporation->corporationId; } } } diff --git a/app/main/model/pathfinder/rightmodel.php b/app/main/model/pathfinder/rightmodel.php index 71dd0fa3..2c13b877 100644 --- a/app/main/model/pathfinder/rightmodel.php +++ b/app/main/model/pathfinder/rightmodel.php @@ -12,8 +12,14 @@ use DB\SQL\Schema; class RightModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'right'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -24,7 +30,9 @@ class RightModel extends AbstractPathfinderModel { 'name' => [ 'type' => Schema::DT_VARCHAR128, 'nullable' => false, - 'default' => '' + 'default' => '', + 'index' => true, + 'unique' => true ], 'label' => [ 'type' => Schema::DT_VARCHAR128, @@ -41,6 +49,9 @@ class RightModel extends AbstractPathfinderModel { ] ]; + /** + * @var array + */ protected static $tableData = [ [ 'id' => 1, diff --git a/app/main/model/pathfinder/structuremodel.php b/app/main/model/pathfinder/structuremodel.php index 3f3b64d4..f4b8de7c 100644 --- a/app/main/model/pathfinder/structuremodel.php +++ b/app/main/model/pathfinder/structuremodel.php @@ -19,11 +19,6 @@ class StructureModel extends AbstractPathfinderModel { */ protected $table = 'structure'; - /** - * categoryId (from ESI) that holds all "groups" with structure "types" - */ - const CATEGORY_STRUCTURE_ID = 65; - /** * @var array */ @@ -130,15 +125,15 @@ class StructureModel extends AbstractPathfinderModel { /** * set corporationId (owner) for this structure * -> if corporation does not exists in DB -> load from API - * @param int $corporationId + * @param int|null $corporationId * @return int|null */ - public function set_corporationId(int $corporationId) : ?int { + public function set_corporationId(?int $corporationId) : ?int { $oldCorporationId = $this->get('corporationId', true) ? : 0; if($corporationId){ if($corporationId !== $oldCorporationId){ - // make sure there is already corporation data stored for new corporationId + // make sure there is already corporation data available for new corporationId /** * @var CorporationModel $corporation */ @@ -156,26 +151,20 @@ class StructureModel extends AbstractPathfinderModel { } /** * validates systemId + * -> a structure always belongs to the same system * @param string $key * @param string $val * @return bool */ - protected function validate_systemId(string $key, string $val): bool { - $valid = true; - - if( !$this->dry() && $this->systemId !== (int)$val ){ - // structure always belongs to the same system - $valid = false; - } - - return $valid; + protected function validate_systemId(string $key, string $val) : bool { + return !($this->valid() && $this->systemId !== (int)$val); } /** * check whether this model is valid or not * @return bool */ - public function isValid(): bool { + public function isValid() : bool { if($valid = parent::isValid()){ // structure always belongs to a systemId if(!(int)$this->systemId){ @@ -244,7 +233,7 @@ class StructureModel extends AbstractPathfinderModel { * @param string $name * @param int $systemId */ - public function getByName(CorporationModel $corporation, string $name, int $systemId) { + public function getByName(CorporationModel $corporation, string $name, int $systemId){ if( !$corporation->dry() && $name){ $this->has('structureCorporations', ['corporationId = :corporationId', ':corporationId' => $corporation->_id]); $this->load(['name = :name AND systemId = :systemId AND active = :active', diff --git a/app/main/model/pathfinder/structurestatusmodel.php b/app/main/model/pathfinder/structurestatusmodel.php index bb37d6f1..5d7aca0c 100644 --- a/app/main/model/pathfinder/structurestatusmodel.php +++ b/app/main/model/pathfinder/structurestatusmodel.php @@ -13,8 +13,14 @@ use DB\SQL\Schema; class StructureStatusModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'structure_status'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -42,6 +48,9 @@ class StructureStatusModel extends AbstractPathfinderModel { ] ]; + /** + * @var array + */ protected static $tableData = [ [ 'id' => 1, diff --git a/app/main/model/pathfinder/systemmodel.php b/app/main/model/pathfinder/systemmodel.php index 8084ea31..29ec2de9 100644 --- a/app/main/model/pathfinder/systemmodel.php +++ b/app/main/model/pathfinder/systemmodel.php @@ -10,6 +10,7 @@ namespace Model\Pathfinder; use DB\SQL\Schema; use lib\logging; +use lib\PriorityCacheStore; use Controller\Ccp\Universe; class SystemModel extends AbstractMapTrackingModel { @@ -39,16 +40,16 @@ class SystemModel extends AbstractMapTrackingModel { */ const DATA_CACHE_KEY_SIGNATURES_HISTORY = 'HISTORY_SIGNATURES'; + /** + * @var PriorityCacheStore + */ + protected static $priorityCacheStore; + /** * @var string */ protected $table = 'system'; - /** - * @var array - */ - protected $staticSystemDataCache = []; - /** * @var array */ @@ -161,80 +162,79 @@ class SystemModel extends AbstractMapTrackingModel { /** * get map data as object * @return \stdClass - * @throws \Exception */ public function getData(){ - // check if there is cached data - $systemData = $this->getCacheData(); - - if(is_null($systemData)){ - // no cached system data found - - $systemData = (object) []; - $systemData->id = $this->_id; - $systemData->mapId = is_object($this->mapId) ? $this->get('mapId', true) : 0; - $systemData->systemId = $this->systemId; - $systemData->alias = $this->alias; + if(is_null($data = $this->getCacheData())){ + $data = (object) []; + $data->id = $this->_id; + $data->mapId = is_object($this->mapId) ? $this->get('mapId', true) : 0; + $data->systemId = $this->systemId; + $data->alias = $this->alias; if(is_object($this->typeId)){ - $systemData->type = $this->typeId->getData(); + $data->type = $this->typeId->getData(); } if(is_object($this->statusId)){ - $systemData->status = $this->statusId->getData(); + $data->status = $this->statusId->getData(); } - $systemData->locked = $this->locked; - $systemData->rallyUpdated = strtotime($this->rallyUpdated); - $systemData->rallyPoke = $this->rallyPoke; - $systemData->description = $this->description ? : ''; + $data->locked = $this->locked; + $data->drifter = $this->isDrifter(); + $data->rallyUpdated = strtotime($this->rallyUpdated); + $data->rallyPoke = $this->rallyPoke; + $data->description = $this->description ? : ''; - $systemData->position = (object) []; - $systemData->position->x = $this->posX; - $systemData->position->y = $this->posY; + $data->position = (object) []; + $data->position->x = $this->posX; + $data->position->y = $this->posY; - $systemData->created = (object) []; - $systemData->created->created = strtotime($this->created); + $data->created = (object) []; + $data->created->created = strtotime($this->created); if(is_object($this->createdCharacterId)){ - $systemData->created->character = $this->createdCharacterId->getData(); + $data->created->character = $this->createdCharacterId->getData(); } - $systemData->updated = (object) []; - $systemData->updated->updated = strtotime($this->updated); + $data->updated = (object) []; + $data->updated->updated = strtotime($this->updated); if(is_object($this->updatedCharacterId)){ - $systemData->updated->character = $this->updatedCharacterId->getData(); + $data->updated->character = $this->updatedCharacterId->getData(); } // static system data ------------------------------------------------------------------------------------- - $systemData->name = $this->name; - $systemData->security = $this->security; - $systemData->trueSec = $this->trueSec; - $systemData->effect = $this->effect; - $systemData->shattered = $this->shattered; + $data->name = $this->name; + $data->security = $this->security; + $data->trueSec = $this->trueSec; + $data->effect = $this->effect; + $data->shattered = $this->shattered; - $systemData->constellation = (object) []; - $systemData->constellation->id = $this->constellationId; - $systemData->constellation->name = $this->constellation; + $data->constellation = (object) []; + $data->constellation->id = $this->constellationId; + $data->constellation->name = $this->constellation; - $systemData->region = (object) []; - $systemData->region->id = $this->regionId; - $systemData->region->name = $this->region; + $data->region = (object) []; + $data->region->id = $this->regionId; + $data->region->name = $this->region; - $systemData->planets = $this->planets ? : []; - $systemData->statics = $this->statics ? : []; + $data->planets = $this->planets ? : []; + $data->statics = $this->statics ? : []; - if(is_object($this->faction)){ - $systemData->faction = $this->faction; + if(is_object($sovereignty = $this->sovereignty)){ + $data->sovereignty = $sovereignty; + } + + if(is_object($factionWar = $this->factionWar)){ + $data->factionWar = $factionWar; } // max caching time for a system // the cached date has to be cleared manually on any change // this includes system, connection,... changes (all dependencies) - $this->updateCacheData($systemData); + $this->updateCacheData($data); } - return $systemData; + return $data; } /** @@ -244,14 +244,19 @@ class SystemModel extends AbstractMapTrackingModel { */ private function getStaticSystemData(){ $staticData = null; - if( !empty($this->staticSystemDataCache[$this->systemId]) ){ - $staticData = $this->staticSystemDataCache[$this->systemId]; + if(!is_object(self::$priorityCacheStore)){ + self::$priorityCacheStore = new PriorityCacheStore(); + } + + if(self::$priorityCacheStore->exists($this->systemId)){ + $staticData = self::$priorityCacheStore->get($this->systemId); }else{ $staticData = (new Universe())->getSystemData($this->systemId); if($staticData){ - $this->staticSystemDataCache = [$this->systemId => $staticData]; + self::$priorityCacheStore->set($this->systemId, $staticData); } } + return $staticData; } @@ -435,10 +440,6 @@ class SystemModel extends AbstractMapTrackingModel { return ($constellationData && $constellationData->region) ? $constellationData->region->name : null; } - public function get_faction(){ - return $this->getStaticSystemValue('faction'); - } - public function get_security(){ return $this->getStaticSystemValue('security'); } @@ -463,6 +464,18 @@ class SystemModel extends AbstractMapTrackingModel { return $this->getStaticSystemValue('planets'); } + public function get_stations(){ + return $this->getStaticSystemValue('stations'); + } + + public function get_sovereignty(){ + return $this->getStaticSystemValue('sovereignty'); + } + + public function get_factionWar(){ + return $this->getStaticSystemValue('factionWar'); + } + /** * Event "Hook" function * @param self $self @@ -652,7 +665,15 @@ class SystemModel extends AbstractMapTrackingModel { * @return \stdClass[] */ public function getStructuresData() : array { - return $this->getMap()->getStructuresData([$this->systemId]); + return $this->getMap()->getStructuresData($this->systemId); + } + + /** + * get data for all stations in this system + * @return array + */ + public function getStationsData() : array { + return $this->stations ? : []; } /** @@ -679,6 +700,14 @@ class SystemModel extends AbstractMapTrackingModel { return ($this->typeId->id === 3 && $this->security === 'A'); } + /** + * check whether this system is in drifter-space + * @return bool + */ + public function isDrifter() : bool { + return in_array($this->security, ['C14', 'C15', 'C16', 'C17', 'C18']); + } + /** * send rally point poke to various "APIs" * -> send to a Slack channel diff --git a/app/main/model/pathfinder/systemneighbourmodel.php b/app/main/model/pathfinder/systemneighbourmodel.php deleted file mode 100644 index 289ded4f..00000000 --- a/app/main/model/pathfinder/systemneighbourmodel.php +++ /dev/null @@ -1,49 +0,0 @@ - [ - 'type' => Schema::DT_INT, - 'index' => true - ], - 'constellationId' => [ - 'type' => Schema::DT_INT, - 'index' => true - ], - 'systemName' => [ - 'type' => Schema::DT_VARCHAR128, - 'default' => '' - ], - 'systemId' => [ - 'type' => Schema::DT_INT, - 'index' => true - ], - 'jumpNodes' => [ - 'type' => Schema::DT_VARCHAR512, - 'default' => '' - ], - 'trueSec' => [ - 'type' => Schema::DT_DECIMAL, - 'default' => 0 - ] - ]; - - /** - * No static columns added - * @var bool - */ - protected $addStaticFields = false; -} \ No newline at end of file diff --git a/app/main/model/pathfinder/systemsignaturemodel.php b/app/main/model/pathfinder/systemsignaturemodel.php index 1c0f9d60..94ed95d6 100644 --- a/app/main/model/pathfinder/systemsignaturemodel.php +++ b/app/main/model/pathfinder/systemsignaturemodel.php @@ -13,8 +13,14 @@ use lib\logging; class SystemSignatureModel extends AbstractMapTrackingModel { + /** + * @var string + */ protected $table = 'system_signature'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -278,6 +284,13 @@ class SystemSignatureModel extends AbstractMapTrackingModel { */ public function afterEraseEvent($self, $pkeys){ $self->logActivity('signatureDelete'); + + if( + $self->connectionIdDeleteCascade === true && + ($connection = $self->getConnection()) + ){ + $connection->erase(); + } } /** diff --git a/app/main/model/pathfinder/systemtypemodel.php b/app/main/model/pathfinder/systemtypemodel.php index 87e0c5c7..b4c18db5 100644 --- a/app/main/model/pathfinder/systemtypemodel.php +++ b/app/main/model/pathfinder/systemtypemodel.php @@ -12,8 +12,14 @@ use DB\SQL\Schema; class SystemTypeModel extends AbstractPathfinderModel { + /** + * @var string + */ protected $table = 'system_type'; + /** + * @var array + */ protected $fieldConf = [ 'active' => [ 'type' => Schema::DT_BOOL, @@ -28,6 +34,9 @@ class SystemTypeModel extends AbstractPathfinderModel { ] ]; + /** + * @var array + */ protected static $tableData = [ [ 'id' => 1, diff --git a/app/main/model/pathfinder/usermodel.php b/app/main/model/pathfinder/usermodel.php index f067fe1d..6a01c6fa 100644 --- a/app/main/model/pathfinder/usermodel.php +++ b/app/main/model/pathfinder/usermodel.php @@ -202,16 +202,15 @@ class UserModel extends AbstractPathfinderModel { } /** - * get current character data from session + * get current character from session data * -> if $characterId == 0 -> get first character data (random) * @param int $characterId - * @param bool $objectCheck - * @return array + * @param int $ttl + * @return CharacterModel|null * @throws Exception */ - public function getSessionCharacterData($characterId = 0, $objectCheck = true) : array { + public function getSessionCharacter(int $characterId = 0, int $ttl = self::DEFAULT_SQL_TTL) : ?CharacterModel { $data = []; - $characterId = (int)$characterId; $currentSessionUser = (array)$this->getF3()->get(User::SESSION_KEY_USER); if($this->_id === $currentSessionUser['ID']){ @@ -228,28 +227,22 @@ class UserModel extends AbstractPathfinderModel { } } - if( - $objectCheck === true && - !empty($data) - ){ + if($characterId = (int)$data['ID']){ // check if character still exists on DB (e.g. was manually removed in the meantime) // -> This should NEVER happen just for security and "local development" /** * @var $character CharacterModel */ $character = AbstractPathfinderModel::getNew('CharacterModel'); - $character->getById((int)$data['ID']); + $character->getById($characterId, $ttl); - if( - $character->dry() || - !$character->hasUserCharacter() - ){ - // character data is invalid! - $data = []; + if($character->valid() && $character->hasUserCharacter()){ + // character data is valid! + return $character; } } - return $data; + return null; } /** @@ -259,10 +252,9 @@ class UserModel extends AbstractPathfinderModel { */ public function findSessionCharacterData(int $characterId) : array { $data = []; - if($characterId){ - $sessionCharacters = (array)$this->getF3()->get(User::SESSION_KEY_CHARACTERS); + if($characterId && $this->getF3()->exists(User::SESSION_KEY_CHARACTERS, $sessionCharacters)){ // search for specific characterData - foreach($sessionCharacters as $characterData){ + foreach((array)$sessionCharacters as $characterData){ if($characterId === (int)$characterData['ID']){ $data = $characterData; break; diff --git a/app/main/model/universe/abstractuniversemodel.php b/app/main/model/universe/abstractuniversemodel.php index 118b379c..f2b85df8 100644 --- a/app/main/model/universe/abstractuniversemodel.php +++ b/app/main/model/universe/abstractuniversemodel.php @@ -20,7 +20,7 @@ abstract class AbstractUniverseModel extends AbstractModel { /** * */ - const CACHE_KEY_PREFIX = 'index_universe_'; + const CACHE_KEY_PREFIX = parent::CACHE_KEY_PREFIX . '_' . self::DB_ALIAS; /** * cache key for model data -> should "never" expire @@ -36,6 +36,21 @@ abstract class AbstractUniverseModel extends AbstractModel { return null; } + /** + * setter for positions array (x/y/z) + * @param $position + * @return null + */ + public function set_position($position){ + $position = (array)$position; + if(count($position) === 3){ + $this->x = $position['x']; + $this->y = $position['y']; + $this->z = $position['z']; + } + return null; + } + /** * Event "Hook" function * return false will stop any further action @@ -46,7 +61,7 @@ abstract class AbstractUniverseModel extends AbstractModel { public function beforeUpdateEvent($self, $pkeys) : bool { // if model changed, 'update' col needs to be updated as well // -> data no longer "outdated" - $this->touch('updated'); + $self->touch('updated'); return parent::beforeUpdateEvent($self, $pkeys); } @@ -59,7 +74,7 @@ abstract class AbstractUniverseModel extends AbstractModel { */ public function getHashKey(string $column = '_id'){ $key = false; - if( !$this->dry() && $this->exists($column) ){ + if($this->valid() && $this->exists($column)){ $key = self::generateHashKeyRow($this->getTable(), $this->$column); } return $key; @@ -110,21 +125,6 @@ abstract class AbstractUniverseModel extends AbstractModel { return $data; } - /** - * add $rowKeys (hashKeys) to a search index that holds all rowKeys of a table - * @param AbstractUniverseModel $model - * @param array $rowKeys - */ - public static function buildTableIndex(AbstractUniverseModel $model, array $rowKeys = []){ - $hashKeyTable = self::generateHashKeyTable($model->getTable()); - if( !self::getF3()->exists($hashKeyTable, $cachedData) ){ - $cachedData = []; - } - $cachedData = array_unique(array_merge($cachedData, $rowKeys)); - - self::getF3()->set($hashKeyTable, $cachedData, self::CACHE_INDEX_EXPIRE_KEY); - } - /** * get data from "search" index for this model * -> if data not found -> try to build up index for this model @@ -166,6 +166,45 @@ abstract class AbstractUniverseModel extends AbstractModel { */ abstract protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []); + /** + * convert CCPs ids for system security into Pathfinder security label + * -> used e.g. in "Dogma Attributes" (wormholeTargetSystemClass) for wormhole types + * @param int $id + * @return string|null + */ + public static function getSystemSecurityFromId(int $id) : ?string { + $security = null; + if( + ($id >= 1 && $id <= 6) || + ($id >= 12 && $id <= 18) + ){ + $security = 'C' . $id; + }elseif($id == 7){ + $security = 'H'; + }elseif($id == 8){ + $security = 'L'; + }elseif($id == 9){ + $security = '0.0'; + } + + return $security; + } + + /** + * add $rowKeys (hashKeys) to a search index that holds all rowKeys of a table + * @param AbstractUniverseModel $model + * @param array $rowKeys + */ + public static function buildTableIndex(AbstractUniverseModel $model, array $rowKeys = []){ + $hashKeyTable = static::generateHashKeyTable($model->getTable()); + if( !self::getF3()->exists($hashKeyTable, $cachedData) ){ + $cachedData = []; + } + $cachedData = array_unique(array_merge($cachedData, $rowKeys)); + + self::getF3()->set($hashKeyTable, $cachedData, self::CACHE_INDEX_EXPIRE_KEY); + } + /** * generate hashKey for a table row data for search index build * @param string $table @@ -173,16 +212,17 @@ abstract class AbstractUniverseModel extends AbstractModel { * @return string */ public static function generateHashKeyRow(string $table, $value) : string { - return self::generateHashKeyTable($table) . '_' . md5(strtolower((string)$value)); + return static::generateHashKeyTable($table) . '_' . md5(strtolower((string)$value)); } /** * generate hashKey for a complete table * -> should hold hashKeys for multiple rows * @param string $table + * @param string $prefix * @return string */ - public static function generateHashKeyTable(string $table) : string { - return self::CACHE_KEY_PREFIX . strtolower($table); + public static function generateHashKeyTable(string $table, string $prefix = self::CACHE_KEY_PREFIX) : string { + return parent::generateHashKeyTable($table, $prefix); } } \ No newline at end of file diff --git a/app/main/model/universe/alliancemodel.php b/app/main/model/universe/alliancemodel.php new file mode 100644 index 00000000..7a1b3f49 --- /dev/null +++ b/app/main/model/universe/alliancemodel.php @@ -0,0 +1,103 @@ + [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'ticker' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'dateFounded' => [ + 'type' => Schema::DT_DATETIME, + 'default' => null + ], + 'factionId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\FactionModel', + 'constraint' => [ + [ + 'table' => 'faction', + 'on-delete' => 'SET NULL' + ] + ] + ], + 'corporations' => [ + 'has-many' => ['Model\Universe\CorporationModel', 'allianceId'] + ], + 'sovereigntySystems' => [ + 'has-many' => ['Model\Universe\SovereigntyMapModel', 'allianceId'] + ] + ]; + + /** + * get data + * @return \stdClass + */ + public function getData(){ + $data = (object) []; + $data->id = $this->_id; + $data->name = $this->name; + $data->ticker = $this->ticker; + + return $data; + } + + /** + * @param $date + * @return string|null + */ + public function set_dateFounded($date){ + if(is_string($date) && !empty($date)){ + try{ + $dateTime = new \DateTime($date); + $date = $dateTime->format('Y-m-d H:i:s'); + }catch(\Exception $e){ + $date = null; + } + } + return $date; + } + + /** + * load alliance by Id either from DB or load data from API + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ + $data = self::getF3()->ccpClient()->getAllianceData($id); + if(!empty($data) && !isset($data['error'])){ + if($data['factionId']){ + /** + * @var $faction FactionModel + */ + $faction = $this->rel('factionId'); + $faction->loadById($data['factionId'], $accessToken, $additionalOptions); + $data['factionId'] = $faction; + } + + $this->copyfrom($data, ['id', 'name', 'ticker', 'dateFounded', 'factionId']); + $this->save(); + } + } +} \ No newline at end of file diff --git a/app/main/model/universe/categorymodel.php b/app/main/model/universe/categorymodel.php index f8ba5052..3d533143 100644 --- a/app/main/model/universe/categorymodel.php +++ b/app/main/model/universe/categorymodel.php @@ -38,7 +38,7 @@ class CategoryModel extends AbstractUniverseModel { */ public function getData(array $additionalData = []){ $categoryData = (object) []; - $categoryData->id = $this->id; + $categoryData->id = $this->_id; $categoryData->name = $this->name; if($groupsData = $this->getGroupsData($additionalData)){ @@ -77,6 +77,9 @@ class CategoryModel extends AbstractUniverseModel { $groupsData = []; $groups = $this->getGroups(); + /** + * @var $group GroupModel + */ foreach($groups as $group){ $groupsData[] = $group->getData($additionalData); } @@ -84,6 +87,15 @@ class CategoryModel extends AbstractUniverseModel { return $groupsData; } + /** + * get groups count + * @param bool $published + * @return int + */ + public function getGroupsCount(bool $published = true) : int { + return $this->valid() ? count($this->getGroups($published)) : 0; + } + /** * count all types that belong to groups in this category * @param bool $published @@ -91,7 +103,7 @@ class CategoryModel extends AbstractUniverseModel { */ public function getTypesCount(bool $published = true) : int { $count = 0; - if( !$this->dry() ){ + if($this->valid()){ /** * @var $group GroupModel */ @@ -109,8 +121,7 @@ class CategoryModel extends AbstractUniverseModel { * @param array $additionalOptions */ protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ - $data = self::getF3()->ccpClient()->getUniverseCategoryData($id); - if(!empty($data)){ + if(!empty($data = self::getUniverseCategoryData($id))){ $this->copyfrom($data, ['id', 'name', 'published']); $this->save(); } @@ -123,25 +134,71 @@ class CategoryModel extends AbstractUniverseModel { * @return array */ public function loadGroupsData(int $offset = 0, int $length = 0) : array { - $groupIds = []; - if( !$this->dry() ){ - $data = self::getF3()->ccpClient()->getUniverseCategoryData($this->_id); - if(!empty($data)){ - array_multisort($data['groups'], SORT_ASC, SORT_NUMERIC); - if($length){ - $data['groups'] = array_slice($data['groups'], $offset, $length); - } - foreach($data['groups'] as $groupId){ - /** - * @var $group GroupModel - */ - $group = $this->rel('groups'); - $group->loadById($groupId); - $groupIds[] = $groupId; - $group->reset(); - } + $info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset, 'groupTypes' => []]; + + if( + $this->valid() && + !empty($data = self::getUniverseCategoryData($this->_id)) + ){ + $info['countAll'] = count($data['groups']); + + array_multisort($data['groups'], SORT_ASC, SORT_NUMERIC); + if($length){ + $data['groups'] = array_slice($data['groups'], $offset, $length); + } + + $info['countChunk'] = count($data['groups']); + foreach($data['groups'] as $groupId){ + /** + * @var $group GroupModel + */ + $group = $this->rel('groups'); + $group->loadById($groupId); + + $info['groupTypes'][$groupId] = $group->loadTypesData(); + + $group->reset(); + + $info['count']++; + $info['offset']++; } } - return $groupIds; + + return $info; + } + + /** + * @param int $id + * @return array + */ + public static function getUniverseCategoryData(int $id) : array { + return self::getF3()->ccpClient()->getUniverseCategoryData($id); + } + + /** + * @return array + */ + public static function getUniverseCategories() : array { + return self::getF3()->ccpClient()->getUniverseCategories(); + } + + /** + * @param int $id + * @return array + */ + public static function getUniverseCategoryGroups(int $id) : array { + return empty($data = self::getUniverseCategoryData($id)) ? [] : $data['groups']; + } + + /** + * @param int $id + * @return array + */ + public static function getUniverseCategoryTypes(int $id) : array { + $types = []; + foreach($groupIds = self::getUniverseCategoryGroups($id) as $groupId){ + $types[$groupId] = GroupModel::getUniverseGroupTypes($groupId); + } + return $types; } } \ No newline at end of file diff --git a/app/main/model/universe/constellationmodel.php b/app/main/model/universe/constellationmodel.php index 6e78944a..1f061ecf 100644 --- a/app/main/model/universe/constellationmodel.php +++ b/app/main/model/universe/constellationmodel.php @@ -55,6 +55,9 @@ class ConstellationModel extends AbstractUniverseModel { ], 'systems' => [ 'has-many' => ['Model\Universe\SystemModel', 'constellationId'] + ], + 'systemNeighbours' => [ + 'has-many' => ['Model\Universe\SystemNeighbourModel', 'constellationId'] ] ]; @@ -71,21 +74,6 @@ class ConstellationModel extends AbstractUniverseModel { return $constellationData; } - /** - * setter for positions array (x/y/z) - * @param $position - * @return null - */ - public function set_position($position){ - $position = (array)$position; - if(count($position) === 3){ - $this->x = $position['x']; - $this->y = $position['y']; - $this->z = $position['z']; - } - return null; - } - /** * @param int $id * @param string $accessToken diff --git a/app/main/model/universe/corporationmodel.php b/app/main/model/universe/corporationmodel.php new file mode 100644 index 00000000..d672bc13 --- /dev/null +++ b/app/main/model/universe/corporationmodel.php @@ -0,0 +1,135 @@ + [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'ticker' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'dateFounded' => [ + 'type' => Schema::DT_DATETIME, + 'default' => null + ], + 'memberCount' => [ + 'type' => Schema::DT_INT, + 'nullable' => false, + 'default' => 0 + ], + 'isNPC' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => false, + 'default' => 0 + ], + 'factionId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\FactionModel', + 'constraint' => [ + [ + 'table' => 'faction', + 'on-delete' => 'SET NULL' + ] + ] + ], + 'allianceId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\AllianceModel', + 'constraint' => [ + [ + 'table' => 'alliance', + 'on-delete' => 'SET NULL' + ] + ] + ], + 'sovereigntySystems' => [ + 'has-many' => ['Model\Universe\SovereigntyMapModel', 'corporationId'] + ], + 'stations' => [ + 'has-many' => ['Model\Universe\StationModel', 'corporationId'] + ] + ]; + + /** + * get data + * @return \stdClass + */ + public function getData(){ + $data = (object) []; + $data->id = $this->_id; + $data->name = $this->name; + + return $data; + } + + /** + * @param $date + * @return string|null + */ + public function set_dateFounded($date){ + if(is_string($date) && !empty($date)){ + try{ + $dateTime = new \DateTime($date); + $date = $dateTime->format('Y-m-d H:i:s'); + }catch(\Exception $e){ + $date = null; + } + } + return $date; + } + + /** + * load corporation by Id either from DB or load data from API + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ + $data = self::getF3()->ccpClient()->getCorporationData($id); + if(!empty($data) && !isset($data['error'])){ + // check for NPC corporation + $data['isNPC'] = self::getF3()->ccpClient()->isNpcCorporation($id); + + if($data['factionId']){ + /** + * @var $faction FactionModel + */ + $faction = $this->rel('factionId'); + $faction->loadById($data['factionId'], $accessToken, $additionalOptions); + $data['factionId'] = $faction; + } + + if($data['allianceId']){ + /** + * @var $alliance AllianceModel + */ + $alliance = $this->rel('allianceId'); + $alliance->loadById($data['allianceId'], $accessToken, $additionalOptions); + $data['allianceId'] = $alliance; + } + + $this->copyfrom($data, ['id', 'name', 'ticker', 'dateFounded', 'memberCount', 'isNPC', 'factionId', 'allianceId']); + $this->save(); + } + } +} \ No newline at end of file diff --git a/app/main/model/universe/dogmaattributemodel.php b/app/main/model/universe/dogmaattributemodel.php new file mode 100644 index 00000000..7936c2a8 --- /dev/null +++ b/app/main/model/universe/dogmaattributemodel.php @@ -0,0 +1,97 @@ + [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'displayName' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => true, + 'default' => null + ], + 'description' => [ + 'type' => Schema::DT_TEXT + ], + 'published' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => true, + 'default' => null + ], + 'stackable' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => true, + 'default' => null + ], + 'highIsGood' => [ + 'type' => Schema::DT_BOOL, + 'nullable' => true, + 'default' => null + ], + 'defaultValue' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ], + 'iconId' => [ + 'type' => Schema::DT_INT, + 'nullable' => true, + 'default' => null + ], + 'unitId' => [ + 'type' => Schema::DT_INT, + 'nullable' => true, + 'default' => null + ], + 'attributeTypes' => [ + 'has-many' => ['Model\Universe\TypeAttributeModel', 'attributeId'] + ] + ]; + + /** + * get data + * @return \stdClass + */ + public function getData(){ + $attributeData = (object) []; + $attributeData->id = $this->_id; + $attributeData->name = $this->name; + $attributeData->description = $this->description; + + return $attributeData; + } + + /** + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ + $data = self::getF3()->ccpClient()->getDogmaAttributeData($id); + if(!empty($data) && !isset($data['error'])){ + $this->copyfrom($data, ['id', 'name', 'displayName', 'description', 'published', 'stackable', 'highIsGood', 'defaultValue', 'iconId', 'unitId']); + $this->save(); + } + } +} \ No newline at end of file diff --git a/app/main/model/universe/factionmodel.php b/app/main/model/universe/factionmodel.php index 11764aab..bc4623ac 100644 --- a/app/main/model/universe/factionmodel.php +++ b/app/main/model/universe/factionmodel.php @@ -46,8 +46,23 @@ class FactionModel extends AbstractUniverseModel { 'nullable' => false, 'default' => 0 ], - 'systems' => [ - 'has-many' => ['Model\Universe\SystemModel', 'factionId'] + 'race' => [ // faction API endpoint dont have "raceId" data, but race API endpoint has + 'has-one' => ['Model\Universe\RaceModel', 'factionId'] + ], + 'alliances' => [ + 'has-many' => ['Model\Universe\AllianceModel', 'factionId'] + ], + 'corporations' => [ + 'has-many' => ['Model\Universe\CorporationModel', 'factionId'] + ], + 'sovereigntySystems' => [ + 'has-many' => ['Model\Universe\SovereigntyMapModel', 'factionId'] + ], + 'factionWarSystemOwners' => [ + 'has-many' => ['Model\Universe\FactionWarSystemModel', 'ownerFactionId'] + ], + 'factionWarSystemOccupiers' => [ + 'has-many' => ['Model\Universe\FactionWarSystemModel', 'occupierFactionId'] ] ]; @@ -70,7 +85,7 @@ class FactionModel extends AbstractUniverseModel { */ protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ $data = self::getF3()->ccpClient()->getUniverseFactionData($id); - if(!empty($data)){ + if(!empty($data) && !isset($data['error'])){ $this->copyfrom($data, ['id', 'name', 'description', 'sizeFactor', 'stationCount', 'stationSystemCount']); $this->save(); } diff --git a/app/main/model/universe/factionwarsystemmodel.php b/app/main/model/universe/factionwarsystemmodel.php new file mode 100644 index 00000000..943cd09b --- /dev/null +++ b/app/main/model/universe/factionwarsystemmodel.php @@ -0,0 +1,120 @@ + [ + 'type' => Schema::DT_INT, + 'index' => true, + 'unique' => true, + 'belongs-to-one' => 'Model\Universe\SystemModel', + 'constraint' => [ + [ + 'table' => 'system', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'ownerFactionId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\FactionModel', + 'constraint' => [ + [ + 'table' => 'faction', + 'on-delete' => 'CASCADE' + ] + ] + ], + 'occupierFactionId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\FactionModel', + 'constraint' => [ + [ + 'table' => 'faction', + 'on-delete' => 'CASCADE' + ] + ] + ], + 'contested' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'victoryPoints' => [ + 'type' => Schema::DT_INT, + 'nullable' => false, + 'default' => 0 + ], + 'victoryPointsThreshold' => [ + 'type' => Schema::DT_INT, + 'nullable' => false, + 'default' => 0 + ] + ]; + + /** + * No static columns added + * @var bool + */ + protected $addStaticFields = false; + + /** + * get data + * @return \stdClass + */ + public function getData(){ + $data = (object) []; + $data->contested = $this->contested; + + if($this->ownerFactionId){ + $data->ownerFaction = $this->ownerFactionId->getData(); + $data->victoryPercentage = $this->getVictoryPercentage(); + + if( + $this->occupierFactionId && + $this->get('occupierFactionId', true) !== $this->get('ownerFactionId', true) + ){ + $data->occupierFaction = $this->occupierFactionId->getData(); + } + } + + return $data; + } + + /** + * calculate victory progress in percent + * @return int + */ + protected function getVictoryPercentage() : int { + $percent = 0; + + if($this->victoryPoints && $this->victoryPointsThreshold){ + $percent = floor((100 / $this->victoryPointsThreshold) * $this->victoryPoints); + } + + return $percent; + } + + /** + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){} +} \ No newline at end of file diff --git a/app/main/model/universe/groupmodel.php b/app/main/model/universe/groupmodel.php index ec712ab9..1bbf5076 100644 --- a/app/main/model/universe/groupmodel.php +++ b/app/main/model/universe/groupmodel.php @@ -12,8 +12,19 @@ use DB\SQL\Schema; class GroupModel extends AbstractUniverseModel { - protected $table = 'group'; + /** + * @var string + */ + protected $table = 'group'; + /** + * @var bool + */ + public $storeDogmaAttributes = TypeModel::DEFAULT_STORE_DOGMA_ATTRIBUTES; + + /** + * @var array + */ protected $fieldConf = [ 'name' => [ 'type' => Schema::DT_VARCHAR128, @@ -50,7 +61,7 @@ class GroupModel extends AbstractUniverseModel { */ public function getData(array $additionalData = []){ $groupData = (object) []; - $groupData->id = $this->id; + $groupData->id = $this->_id; $groupData->name = $this->name; if($typesData = $this->getTypesData($additionalData)){ @@ -65,7 +76,7 @@ class GroupModel extends AbstractUniverseModel { * @param bool $published * @return array|mixed */ - protected function getTypes(bool $published = true){ + public function getTypes(bool $published = true){ $types = []; if($published){ $this->filter('types', [ @@ -102,7 +113,7 @@ class GroupModel extends AbstractUniverseModel { * @return int */ public function getTypesCount(bool $published = true) : int { - return $this->dry() ? 0 : count($this->getTypes($published)); + return $this->valid() ? count($this->getTypes($published)) : 0; } /** @@ -111,8 +122,7 @@ class GroupModel extends AbstractUniverseModel { * @param array $additionalOptions */ protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ - $data = self::getF3()->ccpClient()->getUniverseGroupData($id); - if(!empty($data)){ + if(!empty($data = self::getUniverseGroupData($id))){ /** * @var $category CategoryModel */ @@ -127,24 +137,62 @@ class GroupModel extends AbstractUniverseModel { /** * load types data for this group - * @return int + * @param int $offset + * @param int $length 0 -> all types + * @return array */ - public function loadTypesData(){ - $count = 0; - if( !$this->dry() ){ - $data = self::getF3()->ccpClient()->getUniverseGroupData($this->_id); - if(!empty($data)){ - foreach((array)$data['types'] as $typeId){ - /** - * @var $type TypeModel - */ - $type = $this->rel('types'); - $type->loadById($typeId); - $type->reset(); - $count++; - } + public function loadTypesData(int $offset = 0, int $length = 0) : array { + $info = ['countAll' => 0, 'countChunk' => 0, 'count' => 0, 'offset' => $offset]; + + if( + $this->valid() && + !empty($data = self::getUniverseGroupData($this->_id)) + ){ + $info['countAll'] = count($data['types']); + + array_multisort($data['types'], SORT_ASC, SORT_NUMERIC); + if($length){ + $data['types'] = array_slice($data['types'], $offset, $length); + } + + $info['countChunk'] = count($data['types']); + foreach($data['types'] as $typeId){ + /** + * @var $type TypeModel + */ + $type = $this->rel('types'); + $type->storeDogmaAttributes = $this->storeDogmaAttributes; + $type->loadById($typeId); + $type->reset(); + + $info['count']++; + $info['offset']++; } } - return $count; + + return $info; + } + + /** + * @param int $id + * @return array + */ + public static function getUniverseGroupData(int $id) : array { + return self::getF3()->ccpClient()->getUniverseGroupData($id); + } + + /** + * @return array + */ + public static function getUniverseGroups() : array { + return self::getF3()->ccpClient()->getUniverseGroups(); + } + + /** + * @param int $id + * @return array + */ + public static function getUniverseGroupTypes(int $id) : array { + return empty($data = self::getUniverseGroupData($id)) ? [] : $data['types']; } } \ No newline at end of file diff --git a/app/main/model/universe/planetmodel.php b/app/main/model/universe/planetmodel.php index 99653034..6b7eefb4 100644 --- a/app/main/model/universe/planetmodel.php +++ b/app/main/model/universe/planetmodel.php @@ -66,28 +66,13 @@ class PlanetModel extends AbstractUniverseModel { * @return \stdClass */ public function getData(){ - $planetData = (object) []; - $planetData->name = $this->name; + $data = (object) []; + $data->name = $this->name; - $planetData->type = (object) []; - $planetData->type->name = $this->typeId->name; + $data->type = (object) []; + $data->type->name = $this->typeId->name; - return $planetData; - } - - /** - * setter for positions array (x/y/z) - * @param $position - * @return null - */ - public function set_position($position){ - $position = (array)$position; - if(count($position) === 3){ - $this->x = $position['x']; - $this->y = $position['y']; - $this->z = $position['z']; - } - return null; + return $data; } /** diff --git a/app/main/model/universe/racemodel.php b/app/main/model/universe/racemodel.php new file mode 100644 index 00000000..deea0ed0 --- /dev/null +++ b/app/main/model/universe/racemodel.php @@ -0,0 +1,82 @@ + [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'description' => [ + 'type' => Schema::DT_TEXT + ], + 'factionId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\FactionModel', + 'constraint' => [ + [ + 'table' => 'faction', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'stations' => [ + 'has-many' => ['Model\Universe\StationModel', 'raceId'] + ] + ]; + + /** + * get data + * @return \stdClass + */ + public function getData(){ + $data = (object) []; + $data->id = $this->_id; + $data->name = $this->name; + $data->faction = $this->factionId->getData(); + + return $data; + } + + /** + * load data from API into $this and save $this + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ + $data = self::getF3()->ccpClient()->getUniverseRaceData($id); + if(!empty($data) && !isset($data['error'])){ + /** + * @var $faction FactionModel + */ + $faction = $this->rel('factionId'); + $faction->loadById($data['factionId'], $accessToken, $additionalOptions); + $data['factionId'] = $faction; + + $this->copyfrom($data, ['id', 'name', 'description', 'factionId']); + $this->save(); + } + } +} \ No newline at end of file diff --git a/app/main/model/universe/regionmodel.php b/app/main/model/universe/regionmodel.php index 46b69b40..77b84fc7 100644 --- a/app/main/model/universe/regionmodel.php +++ b/app/main/model/universe/regionmodel.php @@ -32,6 +32,9 @@ class RegionModel extends AbstractUniverseModel { ], 'constellations' => [ 'has-many' => ['Model\Universe\ConstellationModel', 'regionId'] + ], + 'systemNeighbours' => [ + 'has-many' => ['Model\Universe\SystemNeighbourModel', 'regionId'] ] ]; diff --git a/app/main/model/universe/sovereigntymapmodel.php b/app/main/model/universe/sovereigntymapmodel.php new file mode 100644 index 00000000..59b18b2c --- /dev/null +++ b/app/main/model/universe/sovereigntymapmodel.php @@ -0,0 +1,101 @@ + [ + 'type' => Schema::DT_INT, + 'index' => true, + 'unique' => true, + 'belongs-to-one' => 'Model\Universe\SystemModel', + 'constraint' => [ + [ + 'table' => 'system', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'factionId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\FactionModel', + 'constraint' => [ + [ + 'table' => 'faction', + 'on-delete' => 'CASCADE' + ] + ] + ], + 'allianceId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\AllianceModel', + 'constraint' => [ + [ + 'table' => 'alliance', + 'on-delete' => 'CASCADE' + ] + ] + ], + 'corporationId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\CorporationModel', + 'constraint' => [ + [ + 'table' => 'corporation', + 'on-delete' => 'CASCADE' + ] + ] + ] + ]; + + /** + * No static columns added + * @var bool + */ + protected $addStaticFields = false; + + /** + * get data + * @return \stdClass + */ + public function getData(){ + $data = (object) []; + + if($this->factionId){ + $data->faction = $this->factionId->getData(); + }else{ + if($this->allianceId){ + $data->alliance = $this->allianceId->getData(); + } + + if($this->corporationId){ + $data->corporation = $this->corporationId->getData(); + } + } + + return $data; + } + + /** + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){} +} \ No newline at end of file diff --git a/app/main/model/universe/stargatemodel.php b/app/main/model/universe/stargatemodel.php index d50e5eb6..cbc32a58 100644 --- a/app/main/model/universe/stargatemodel.php +++ b/app/main/model/universe/stargatemodel.php @@ -74,7 +74,6 @@ class StargateModel extends AbstractUniverseModel { ]; public function getData(){ - $stargateData = (object) []; $stargateData->id = $this->_id; $stargateData->type = $this->typeId->name; @@ -83,21 +82,6 @@ class StargateModel extends AbstractUniverseModel { return $stargateData; } - /** - * setter for positions array (x/y/z) - * @param $position - * @return null - */ - public function set_position($position){ - $position = (array)$position; - if(count($position) === 3){ - $this->x = $position['x']; - $this->y = $position['y']; - $this->z = $position['z']; - } - return null; - } - /** * @param int $id * @param string $accessToken diff --git a/app/main/model/universe/starmodel.php b/app/main/model/universe/starmodel.php index 88f8f603..94d21876 100644 --- a/app/main/model/universe/starmodel.php +++ b/app/main/model/universe/starmodel.php @@ -12,8 +12,14 @@ use DB\SQL\Schema; class StarModel extends AbstractUniverseModel { + /** + * @var string + */ protected $table = 'star'; + /** + * @var array + */ protected $fieldConf = [ 'name' => [ 'type' => Schema::DT_VARCHAR128, diff --git a/app/main/model/universe/stationmodel.php b/app/main/model/universe/stationmodel.php new file mode 100644 index 00000000..d890ea59 --- /dev/null +++ b/app/main/model/universe/stationmodel.php @@ -0,0 +1,165 @@ + [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'systemId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\SystemModel', + 'constraint' => [ + [ + 'table' => 'system', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'typeId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\TypeModel', + 'constraint' => [ + [ + 'table' => 'type', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'corporationId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\CorporationModel', + 'constraint' => [ + [ + 'table' => 'corporation', + 'on-delete' => 'CASCADE' + ] + ] + ], + 'raceId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\RaceModel', + 'constraint' => [ + [ + 'table' => 'race', + 'on-delete' => 'CASCADE' + ] + ] + ], + 'services' => [ + 'type' => self::DT_JSON + ], + 'x' => [ + 'type' => Schema::DT_BIGINT, + 'nullable' => false, + 'default' => 0 + ], + 'y' => [ + 'type' => Schema::DT_BIGINT, + 'nullable' => false, + 'default' => 0 + ], + 'z' => [ + 'type' => Schema::DT_BIGINT, + 'nullable' => false, + 'default' => 0 + ] + ]; + + /** + * get data + * @return \stdClass + */ + public function getData(){ + $data = (object) []; + $data->id = $this->_id; + $data->name = $this->name; + $data->type = $this->typeId->getData(); + $data->services = $this->services ? : []; + + // according to ESIs Swagger conf, "raceId" AND "corporationId"(= "owner") are optional + // -> I haven´t seen any imported NPC station data where "raceId" OR "corporationId" not exist + if($this->corporationId){ + $data->corporation = $this->corporationId->getData(); + } + + if($this->raceId){ + $data->race = $this->raceId->getData(); + } + + return $data; + } + + /** + * load data from API into $this and save $this + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ + $data = self::getF3()->ccpClient()->getUniverseStationData($id); + if(!empty($data) && !isset($data['error'])){ + /** + * @var $system SystemModel + */ + $system = $this->rel('systemId'); + $system->loadById($data['systemId'], $accessToken, $additionalOptions); + $data['systemId'] = $system; + + /** + * @var $type TypeModel + */ + $type = $this->rel('typeId'); + $type->loadById($data['typeId'], $accessToken, $additionalOptions); + $data['typeId'] = $type; + + if($data['corporationId']){ + /** + * @var $faction CorporationModel + */ + $corporation = $this->rel('corporationId'); + $corporation->loadById($data['corporationId'], $accessToken, $additionalOptions); + $data['corporationId'] = $corporation; + } + + if($data['raceId']){ + /** + * @var $race RaceModel + */ + $race = $this->rel('raceId'); + $race->loadById($data['raceId'], $accessToken, $additionalOptions); + $data['raceId'] = $race; + } + + $this->copyfrom($data, ['id', 'name', 'systemId', 'typeId', 'corporationId', 'raceId', 'services', 'position']); + $this->save(); + } + } + +} \ No newline at end of file diff --git a/app/main/model/universe/structuremodel.php b/app/main/model/universe/structuremodel.php index 3daee092..a1fc0ae4 100644 --- a/app/main/model/universe/structuremodel.php +++ b/app/main/model/universe/structuremodel.php @@ -13,8 +13,14 @@ use DB\SQL\Schema; class StructureModel extends AbstractUniverseModel { + /** + * @var string + */ protected $table = 'structure'; + /** + * @var array + */ protected $fieldConf = [ 'name' => [ 'type' => Schema::DT_VARCHAR128, @@ -23,9 +29,15 @@ class StructureModel extends AbstractUniverseModel { ], 'systemId' => [ 'type' => Schema::DT_INT, - 'nullable' => false, - 'default' => 0, - 'index' => true + 'index' => true, + 'belongs-to-one' => 'Model\Universe\SystemModel', + 'constraint' => [ + [ + 'table' => 'system', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' ], 'typeId' => [ 'type' => Schema::DT_INT, @@ -63,9 +75,10 @@ class StructureModel extends AbstractUniverseModel { */ public function getData(): \stdClass { $data = (object) []; - if(!$this->dry()){ - $data->id = $this->_id; + if($this->valid()){ + $data->id = $this->_id; $data->name = $this->name; + $data->type = $this->typeId->getData(); } return $data; } @@ -78,7 +91,7 @@ class StructureModel extends AbstractUniverseModel { */ protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ $data = self::getF3()->ccpClient()->getUniverseStructureData($id, $accessToken); - if(!empty($data)){ + if(!empty($data) && !isset($data['error'])){ /** * @var $type TypeModel */ diff --git a/app/main/model/universe/systemmodel.php b/app/main/model/universe/systemmodel.php index b56152d6..39f41672 100644 --- a/app/main/model/universe/systemmodel.php +++ b/app/main/model/universe/systemmodel.php @@ -17,11 +17,6 @@ class SystemModel extends AbstractUniverseModel { */ protected $table = 'system'; - /** - * - */ - const ERROR_INVALID_WORMHOLE = 'Invalid wormhole name "%s" for system: "%s"'; - /** * @var array */ @@ -55,18 +50,6 @@ class SystemModel extends AbstractUniverseModel { ], 'validate' => 'notDry' ], - 'factionId' => [ - 'type' => Schema::DT_INT, - 'index' => true, - 'belongs-to-one' => 'Model\Universe\FactionModel', - 'constraint' => [ - [ - 'table' => 'faction', - 'on-delete' => 'CASCADE' - ] - ], - 'validate' => 'notDry' - ], 'security' => [ 'type' => Schema::DT_VARCHAR128 ], @@ -86,11 +69,6 @@ class SystemModel extends AbstractUniverseModel { 'effect' => [ 'type' => Schema::DT_VARCHAR128 ], - 'shattered' => [ - 'type' => Schema::DT_BOOL, - 'nullable' => false, - 'default' => 0 - ], 'x' => [ 'type' => Schema::DT_BIGINT, 'nullable' => false, @@ -114,6 +92,21 @@ class SystemModel extends AbstractUniverseModel { ], 'stargates' => [ 'has-many' => ['Model\Universe\StargateModel', 'systemId'] + ], + 'stations' => [ + 'has-many' => ['Model\Universe\StationModel', 'systemId'] + ], + 'structures' => [ + 'has-many' => ['Model\Universe\StructureModel', 'systemId'] + ], + 'neighbour' => [ + 'has-one' => ['Model\Universe\SystemNeighbourModel', 'systemId'] + ], + 'sovereignty' => [ + 'has-one' => ['Model\Universe\SovereigntyMapModel', 'systemId'] + ], + 'factionWar' => [ + 'has-one' => ['Model\Universe\FactionWarSystemModel', 'systemId'] ] ]; @@ -123,37 +116,52 @@ class SystemModel extends AbstractUniverseModel { * @return \stdClass */ public function getData(){ - - $systemData = (object) []; - $systemData->id = $this->_id; - $systemData->name = $this->name; - $systemData->constellation = $this->constellationId->getData(); - $systemData->security = $this->security; - $systemData->trueSec = (float)$this->trueSec; - $systemData->effect = $this->effect; - $systemData->shattered = (bool)$this->shattered; + $data = (object) []; + $data->id = $this->_id; + $data->name = $this->name; + $data->constellation = $this->constellationId->getData(); + $data->security = $this->security; + $data->trueSec = (float)$this->trueSec; + $data->effect = $this->effect; + $data->shattered = false; if($this->starId){ - $systemData->star = $this->starId->getData(); + $data->star = $this->starId->getData(); } - if($this->factionId){ - $systemData->faction = $this->factionId->getData(); + if($this->sovereignty){ + $data->sovereignty = $this->sovereignty->getData(); + } + + if($this->factionWar){ + $data->factionWar = $this->factionWar->getData(); } if( !empty($planetsData = $this->getPlanetsData()) ){ - $systemData->planets = $planetsData; + $data->planets = $planetsData; + + // 'Shattered' systems have ONLY planets named with '(shattered)' + // -> system 'Thera' has '(shattered)' AND other planets -> not shattered. + // -> system 'J164104, 'J115422' - the only non-shattered wormholes which have a shattered planet -> not shattered. + $data->shattered = count(array_filter($planetsData, function(object $planetData){ + return property_exists($planetData, 'type') && + (strpos(strtolower($planetData->type->name), '(shattered)') !== false); + })) == count($planetsData); } if( !empty($staticsData = $this->getStaticsData()) ){ - $systemData->statics = $staticsData; + $data->statics = $staticsData; } if( !empty($stargatesData = $this->getStargatesData()) ){ - $systemData->stargates = $stargatesData; + $data->stargates = $stargatesData; } - return $systemData; + if( !empty($stationsData = $this->getStationsData()) ){ + $data->stations = $stationsData; + } + + return $data; } /** @@ -185,11 +193,18 @@ class SystemModel extends AbstractUniverseModel { } $this->trueSec = $trueSec; // set 'security' for NON wormhole systems! -> those get updated from csv import - if(!preg_match('/^j\d+$/i', $this->name)){ - // check for "Abyssal" system - if( - $this->get('constellationId', true) >= 22000001 && - $this->get('constellationId', true) <= 22000025 + // 'J1226-0' is also a wormhole with a '-' in the name! (single system) + if( + !preg_match('/^j(\d{6}|\d{4}-\d)$/i', $this->name) && + $this->name != 'Thera' + ){ + $constellationId = (int)$this->get('constellationId', true); + if($constellationId == 23000001){ + // "Pocket" system + $security = 'P'; + }elseif( + $constellationId >= 22000001 && + $constellationId <= 22000025 ){ // "Abyssal" system $security = 'A'; @@ -220,30 +235,141 @@ class SystemModel extends AbstractUniverseModel { } /** - * setter for positions array (x/y/z) - * @param $position - * @return null + * @param array $sovData + * @return bool true if sovereignty data changed */ - public function set_position($position){ - $position = (array)$position; - if(count($position) === 3){ - $this->x = $position['x']; - $this->y = $position['y']; - $this->z = $position['z']; + public function updateSovereigntyData(array $sovData = []) : bool { + $hasChanged = false; + $systemId = (int)$sovData['systemId']; + $factionId = (int)$sovData['factionId']; + $allianceId = (int)$sovData['allianceId']; + $corporationId = (int)$sovData['corporationId']; + + if($this->valid()){ + if($systemId === $this->_id){ + // sov data belongs to this system + $validSovData = (bool)max($factionId, $allianceId, $corporationId); + if($validSovData){ + // at least one of these Ids must exist for a sovereignty relation + /** + * @var $sovereignty SovereigntyMapModel + */ + if(!$sovereignty = $this->sovereignty){ + // insert new sovereignty data + $sovereignty = $this->rel('sovereignty'); + } + + $sovData['systemId'] = $this; + + if($factionId){ + // HS, L - systems have "faction war" + $sovData['allianceId'] = null; + $sovData['corporationId'] = null; + + /** + * @var $faction FactionModel + */ + $faction = $sovereignty->rel('factionId'); + $faction->loadById($factionId); + $sovData['factionId'] = $faction; + }else{ + // 0.0 - systems have sovereignty data by corp and/or ally + $sovData['factionId'] = null; + + /** + * @var $alliance AllianceModel|null + */ + $alliance = null; + if($allianceId){ + $alliance = $sovereignty->rel('allianceId'); + $alliance->loadById($allianceId); + } + + /** + * @var $corporation CorporationModel|null + */ + $corporation = null; + if($corporationId){ + $corporation = $sovereignty->rel('corporationId'); + $corporation->loadById($corporationId); + } + + $sovData['allianceId'] = $alliance; + $sovData['corporationId'] = $corporation; + } + + $sovereignty->copyfrom($sovData, ['systemId', 'factionId', 'allianceId', 'corporationId']); + + // must be called before save(). Changed fields get reset after save() is called! + if($sovereignty->changed()){ + $hasChanged = true; + } + + $sovereignty->save(); + }elseif($this->sovereignty){ + // delete existing sov data + // -> hint: WH - systems never have sovereignty data + $this->sovereignty->erase(); + $hasChanged = true; + } + } } - return null; + + return $hasChanged; } /** - * setter for static systems (wormholes) - * -> comma separated string or array - * @param $staticNames - * @return null + * @param array $fwData + * @return bool true if faction warfare data changed */ - public function set_staticNames($staticNames){ - $staticNames = array_unique(is_string($staticNames) ? explode(',', $staticNames) : (array)$staticNames); - $this->virtual('staticNames', array_map('strtoupper', $staticNames)); - return null; + public function updateFactionWarData(array $fwData = []) : bool { + $hasChanged = false; + $systemId = (int)$fwData['systemId']; + $ownerFactionId = (int)$fwData['ownerFactionId']; + $occupierFactionId = (int)$fwData['occupierFactionId']; + + if($this->valid()){ + if($systemId === $this->_id){ + /** + * @var $factionWar FactionWarSystemModel + */ + if(!$factionWar = $this->factionWar){ + // insert new faction war data + $factionWar = $this->rel('factionWar'); + } + + $fwData['systemId'] = $this; + + if($ownerFactionId){ + /** + * @var $ownerFaction FactionModel + */ + $ownerFaction = $factionWar->rel('ownerFactionId'); + $ownerFaction->loadById($ownerFactionId); + $fwData['ownerFactionId'] = $ownerFaction; + } + + if($occupierFactionId){ + /** + * @var $occupierFaction FactionModel + */ + $occupierFaction = $factionWar->rel('occupierFactionId'); + $occupierFaction->loadById($occupierFactionId); + $fwData['occupierFactionId'] = $occupierFaction; + } + + $factionWar->copyfrom($fwData, ['systemId', 'ownerFactionId', 'occupierFactionId', 'contested', 'victoryPoints', 'victoryPointsThreshold']); + + // must be called before save(). Changed fields get reset after save() is called! + if($factionWar->changed()){ + $hasChanged = true; + } + + $factionWar->save(); + } + } + + return $hasChanged; } /** @@ -253,38 +379,8 @@ class SystemModel extends AbstractUniverseModel { * @param $pkeys */ public function afterUpdateEvent($self, $pkeys){ - $staticNames = (array)$self->staticNames; - - if( - count($staticNames) > 0 && // make sure statics are set. In case a wh system get updated without statics are set - preg_match('/^c\d+$/i', $self->security) // make sure it is a wormhole - ){ - foreach((array)$self->statics as $static){ - if(in_array($static->wormholeId->name, $staticNames)){ - unset($staticNames[array_search($static->wormholeId->name, $staticNames)]); - }else{ - $static->erase(); - } - } - - // add new statics - foreach($staticNames as $staticName){ - $static = $self->rel('statics'); - /** - * @var $wormhole WormholeModel - */ - $wormhole = $static->rel('wormholeId')->getByForeignKey('name', $staticName, ['limit' => 1]); - if( !$wormhole->dry() ){ - $static->systemId = $self; - $static->wormholeId = $wormhole; - $static->save(); - } - } - } - // build search index $self->buildIndex(); - return parent::afterUpdateEvent($self, $pkeys); } @@ -292,14 +388,14 @@ class SystemModel extends AbstractUniverseModel { * get data from all planets * @return array */ - protected function getPlanetsData(){ + protected function getPlanetsData() : array { $planetsData = []; if($this->planets){ + /** + * @var $planet PlanetModel + */ foreach($this->planets as &$planet){ - /** - * @var $planet PlanetModel - */ $planetsData[] = $planet->getData(); } } @@ -310,14 +406,14 @@ class SystemModel extends AbstractUniverseModel { * get data from all static wormholes * @return array */ - protected function getStaticsData(){ + protected function getStaticsData() : array { $staticsData = []; if($this->statics){ + /** + * @var $static SystemStaticModel + */ foreach($this->statics as &$static){ - /** - * @var $static SystemStaticModel - */ $staticsData[] = $static->getData(); } } @@ -328,25 +424,62 @@ class SystemModel extends AbstractUniverseModel { * get data from all stargates * @return array */ - protected function getStargatesData(){ + protected function getStargatesData() : array { $stargatesData = []; if($this->stargates){ + /** + * @var $stargate StargateModel + */ foreach($this->stargates as &$stargate){ - /** - * @var $stargate StargateModel - */ $stargatesData[] = $stargate->getData(); } } return $stargatesData; } + /** + * get data from all stations + * @return array + */ + protected function getStationsData() : array { + $stationsData = []; + + if($this->stations){ + /** + * @var $station StationModel + */ + foreach($this->stations as &$station){ + $data = $station->getData(); + if(!$data->race){ + // should never happen NPC stations always have a owning race + $data->race = (object) []; + $data->race->id = 0; + $data->race->name = 'unknown'; + $data->race->faction = (object) []; + $data->race->faction->id = 0; + $data->race->faction->name = 'unknown'; + } + + if(!array_key_exists($data->race->faction->id, $stationsData)){ + $stationsData[$data->race->faction->id] = [ + 'id' => $data->race->faction->id, + 'name' => $data->race->name, + 'stations' => [] + ]; + } + + $stationsData[$data->race->faction->id]['stations'][] = $data; + } + } + return $stationsData; + } + /** * update system from ESI */ public function updateModel(){ - if( !$this->dry() ){ + if($this->valid()){ $this->loadData($this->_id); $this->loadPlanetsData(); } @@ -387,7 +520,7 @@ class SystemModel extends AbstractUniverseModel { * load planets data for this system */ public function loadPlanetsData(){ - if( !$this->dry() ){ + if($this->valid()){ $data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id); if($data['planets']){ // planets are optional since ESI v4 (e.g. Abyssal systems) @@ -408,9 +541,9 @@ class SystemModel extends AbstractUniverseModel { * -> stargates to destination system which is not in DB get ignored */ public function loadStargatesData(){ - if( !$this->dry() ){ + if($this->valid()){ $data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id); - if(!empty($data)){ + if($data['stargates']){ foreach((array)$data['stargates'] as $stargateId){ /** * @var $stargate StargateModel @@ -422,4 +555,23 @@ class SystemModel extends AbstractUniverseModel { } } } + + /** + * load NPC owned stations for this system + */ + public function loadStationsData(){ + if($this->valid()){ + $data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id); + if($data['stations']){ + foreach((array)$data['stations'] as $stationId){ + /** + * @var $station SystemModel + */ + $station = $this->rel('stations'); + $station->loadById($stationId); + $station->reset(); + } + } + } + } } \ No newline at end of file diff --git a/app/main/model/universe/systemneighbourmodel.php b/app/main/model/universe/systemneighbourmodel.php new file mode 100644 index 00000000..f598565c --- /dev/null +++ b/app/main/model/universe/systemneighbourmodel.php @@ -0,0 +1,92 @@ + used on /setup page or index build + * @var bool + */ + protected $allowTruncate = true; + + /** + * @var array + */ + protected $fieldConf = [ + 'regionId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\RegionModel', + 'constraint' => [ + [ + 'table' => 'region', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'constellationId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\ConstellationModel', + 'constraint' => [ + [ + 'table' => 'constellation', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'systemId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'unique' => true, + 'belongs-to-one' => 'Model\Universe\SystemModel', + 'constraint' => [ + [ + 'table' => 'system', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'systemName' => [ + 'type' => Schema::DT_VARCHAR128, + 'nullable' => false, + 'default' => '' + ], + 'jumpNodes' => [ + 'type' => Schema::DT_VARCHAR512, + 'nullable' => false, + 'default' => '' + ], + 'trueSec' => [ + 'type' => Schema::DT_DECIMAL, + 'nullable' => false, + 'default' => 0 + ] + ]; + + /** + * No static columns added + * @var bool + */ + protected $addStaticFields = false; + + /** + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){} +} \ No newline at end of file diff --git a/app/main/model/universe/systemstaticmodel.php b/app/main/model/universe/systemstaticmodel.php index f736b781..a072b123 100644 --- a/app/main/model/universe/systemstaticmodel.php +++ b/app/main/model/universe/systemstaticmodel.php @@ -12,8 +12,14 @@ use DB\SQL\Schema; class SystemStaticModel extends AbstractUniverseModel { + /** + * @var string + */ protected $table = 'system_static'; + /** + * @var array + */ protected $fieldConf = [ 'systemId' => [ 'type' => Schema::DT_INT, @@ -27,13 +33,13 @@ class SystemStaticModel extends AbstractUniverseModel { ], 'validate' => 'notDry' ], - 'wormholeId' => [ + 'typeId' => [ 'type' => Schema::DT_INT, 'index' => true, - 'belongs-to-one' => 'Model\Universe\WormholeModel', + 'belongs-to-one' => 'Model\Universe\TypeModel', 'constraint' => [ [ - 'table' => 'wormhole', + 'table' => 'type', 'on-delete' => 'CASCADE' ] ], @@ -52,8 +58,14 @@ class SystemStaticModel extends AbstractUniverseModel { * @return null|string */ public function getData(){ - return $this->wormholeId ? $this->wormholeId->name : null; + return $this->typeId ? $this->typeId->getWormholeName() : null; } + + /** + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){} /** @@ -66,7 +78,7 @@ class SystemStaticModel extends AbstractUniverseModel { */ public static function setup($db = null, $table = null, $fields = null){ if($status = parent::setup($db, $table, $fields)){ - $status = parent::setMultiColumnIndex(['systemId', 'wormholeId'], true); + $status = parent::setMultiColumnIndex(['systemId', 'typeId'], true); } return $status; } diff --git a/app/main/model/universe/typeattributemodel.php b/app/main/model/universe/typeattributemodel.php new file mode 100644 index 00000000..eec9d4bf --- /dev/null +++ b/app/main/model/universe/typeattributemodel.php @@ -0,0 +1,92 @@ + [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\TypeModel', + 'constraint' => [ + [ + 'table' => 'type', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'attributeId' => [ + 'type' => Schema::DT_INT, + 'index' => true, + 'belongs-to-one' => 'Model\Universe\DogmaAttributeModel', + 'constraint' => [ + [ + 'table' => 'dogma_attribute', + 'on-delete' => 'CASCADE' + ] + ], + 'validate' => 'notDry' + ], + 'value' => [ + 'type' => Schema::DT_FLOAT, + 'nullable' => false, + 'default' => 0 + ] + ]; + + /** + * No static columns added + * @var bool + */ + protected $addStaticFields = false; + + /** + * @return \stdClass + */ + public function getData(){ + $typeAttributeData = $this->attributeId->getData(); + $typeAttributeData->value = (float)$this->value; + + return $typeAttributeData; + } + + /** + * @param int $id + * @param string $accessToken + * @param array $additionalOptions + */ + protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){} + + /** + * overwrites parent + * @param null $db + * @param null $table + * @param null $fields + * @return bool + * @throws \Exception + */ + public static function setup($db = null, $table = null, $fields = null){ + if($status = parent::setup($db, $table, $fields)){ + $status = parent::setMultiColumnIndex(['typeId', 'attributeId'], true); + } + return $status; + } +} \ No newline at end of file diff --git a/app/main/model/universe/typemodel.php b/app/main/model/universe/typemodel.php index 5401b4f2..4402d741 100644 --- a/app/main/model/universe/typemodel.php +++ b/app/main/model/universe/typemodel.php @@ -9,11 +9,31 @@ namespace Model\Universe; use DB\SQL\Schema; +use lib\Config; +use lib\Util; class TypeModel extends AbstractUniverseModel { - protected $table = 'type'; + /** + * @var string + */ + protected $table = 'type'; + /** + * default store option for 'dogma' typeAttributes data + * -> set to true will store all typeAttributes from ESI for a type + * -> should be enabled for specific types, where data is used by Pathfinder + */ + const DEFAULT_STORE_DOGMA_ATTRIBUTES = false; + + /** + * @var bool + */ + public $storeDogmaAttributes = self::DEFAULT_STORE_DOGMA_ATTRIBUTES; + + /** + * @var array + */ protected $fieldConf = [ 'name' => [ 'type' => Schema::DT_VARCHAR128, @@ -83,6 +103,9 @@ class TypeModel extends AbstractUniverseModel { 'default' => 0, 'index' => true ], + 'stations' => [ + 'has-many' => ['Model\Universe\StationModel', 'typeId'] + ], 'structures' => [ 'has-many' => ['Model\Universe\StructureModel', 'typeId'] ], @@ -92,11 +115,46 @@ class TypeModel extends AbstractUniverseModel { 'stars' => [ 'has-many' => ['Model\Universe\StarModel', 'typeId'] ], - 'wormholes' => [ - 'has-many' => ['Model\Universe\WormholeModel', 'typeId'] + 'attributes' => [ + 'has-many' => ['Model\Universe\TypeAttributeModel', 'typeId'] + ], + 'stargates' => [ + 'has-many' => ['Model\Universe\StargateModel', 'typeId'] + ], + 'statics' => [ + 'has-many' => ['Model\Universe\SystemStaticModel', 'typeId'] ] ]; + /** + * set 'dogma_attributes' during ESI import process to a virtual field + * -> 'dogma_attributes' get imported after type is saved + * @see loadData() + * @param $dogmaAttributesData + * @return null + */ + public function set_dogma_attributes($dogmaAttributesData){ + $this->virtual('dogmaAttributes', (array)$dogmaAttributesData); + return null; + } + + /** + * special getter for 'wormhole' types + * @return string|null + */ + public function getWormholeName(){ + return self::formatWormholeName($this->name); + } + + /** + * @param bool $mapper + * @return NULL|void + */ + public function reset($mapper = true){ + $this->clearVirtual('dogmaAttributes'); + parent::reset($mapper); + } + /** * get type data * @param array $additionalData @@ -104,24 +162,70 @@ class TypeModel extends AbstractUniverseModel { */ public function getData(array $additionalData = []){ $typeData = (object) []; - $typeData->id = $this->id; + $typeData->id = $this->_id; $typeData->name = $this->name; foreach($additionalData as $key){ - $typeData->$key = $this->$key; + if($key == 'attributes'){ + // add 'dogma' typeAttributes data + $typeData->$key = $this->getAttributesData(); + }elseif($this->exists($key)){ + $typeData->$key = $this->$key; + } } return $typeData; } + /** + * get wormholeData from object + * @return \stdClass + */ + public function getWormholeData() : \stdClass { + $wormholeData = (object) []; + if($this->valid()){ + $wormholeData->name = $this->getWormholeName(); + $wormholeData->static = $this->statics ? (bool)count($this->statics) : false; + $wormholeData->security = ''; + $wormholeData->massTotal = null; + $wormholeData->massIndividual = null; + $wormholeData->maxStableTime = null; + foreach($this->getAttributesData() as $id => $attributesData){ + switch($id){ + case 1381: // 'wormholeTargetSystemClass' -> 'security' + $wormholeData->security = self::getSystemSecurityFromId((int)$attributesData['value']); + break; + case 1383: // 'wormholeMaxStableMass' -> 'massTotal' + $wormholeData->massTotal = $attributesData['value']; + break; + case 1385: // 'wormholeMaxJumpMass' -> 'massIndividual' + $wormholeData->massIndividual = $attributesData['value']; + break; + case 1384: // 'wormholeMassRegeneration' -> 'massRegeneration' + if($attributesData['value']){ + $wormholeData->massRegeneration = $attributesData['value']; + } + break; + case 1382: // 'wormholeMaxStableTime' -> 'maxStableTime' + $wormholeData->maxStableTime = $attributesData['value'] / 60; + break; + case Config::ESI_DOGMA_ATTRIBUTE_SCANWHSTRENGTH_ID: // 'scanWormholeStrength' -> 'scanWormholeStrength' + $wormholeData->scanWormholeStrength = $attributesData['value']; + break; + } + } + } + return $wormholeData; + } + /** * get shipData from object * -> more fields can be added in here if needed * @return \stdClass */ - public function getShipData(): \stdClass { + public function getShipData() : \stdClass { $shipData = (object) []; - if(!$this->dry()){ + if($this->valid()){ $shipData->typeId = $this->_id; $shipData->typeName = $this->name; $shipData->mass = $this->mass; @@ -129,6 +233,118 @@ class TypeModel extends AbstractUniverseModel { return $shipData; } + /** + * @return array + */ + protected function getAttributesData() : array { + $attributesData = []; + + if($this->attributes){ + foreach($this->attributes as $typeAttribute){ + /** + * @var $typeAttribute TypeAttributeModel + */ + $attributesData[] = get_object_vars($typeAttribute->getData()); + } + } + return Util::arrayGetBy($attributesData, 'id'); + } + + /** + * Event "Hook" function + * return false will stop any further action + * @param self $self + * @param $pkeys + */ + public function afterInsertEvent($self, $pkeys){ + $self->syncDogmaAttributes(); + + return parent::afterInsertEvent($self, $pkeys); + } + + /** + * Event "Hook" function + * @param self $self + * @param $pkeys + */ + public function afterUpdateEvent($self, $pkeys){ + $self->syncDogmaAttributes(); + + return parent::afterUpdateEvent($self, $pkeys); + } + + /** + * sync existing 'dogma' typeAttributes data with "new/updated" typeAttributes + * -> $this->dogmaAttributes must be set before calling this method + */ + protected function syncDogmaAttributes(){ + if( + $this->storeDogmaAttributes && + !empty($dogmaAttributesData = (array)$this->dogmaAttributes) + ){ + foreach((array)$this->attributes as $typeAttribute){ + $key = array_search($typeAttribute->get('attributeId', true), array_column($dogmaAttributesData, 'attributeId')); + if($key !== false){ + // attribute still belongs to this 'type' -> update value + $typeAttribute->copyfrom($dogmaAttributesData[$key], ['value']); + $typeAttribute->save(); + + unset($dogmaAttributesData[$key]); + $dogmaAttributesData = array_values($dogmaAttributesData); + }else{ + // attribute no longer belongs to this 'type' + $typeAttribute->erase(); + } + } + + // add new dogmaTypes + foreach($dogmaAttributesData as $dogmaAttributeData){ + /** + * @var $typeAttribute TypeAttributeModel + * @var $dogmaAttribute DogmaAttributeModel + */ + $typeAttribute = $this->rel('attributes'); + $dogmaAttribute = $typeAttribute->rel('attributeId'); + $dogmaAttribute->loadById($dogmaAttributeData['attributeId']); + if($dogmaAttribute->valid()){ + $typeAttribute->typeId = $this; + $typeAttribute->attributeId = $dogmaAttribute; + $typeAttribute->value = $dogmaAttributeData['value']; + $typeAttribute->save(); + } + } + } + } + + /** + * manipulate 'dogma_attributes' array be reference + * -> used to inject custom attributes (not available from ESI) + * @param array $data + */ + private function manipulateDogmaAttributes(array &$data){ + if(!$this->storeDogmaAttributes){ + // attributes should not get saved + unset($data['dogma_attributes']); + }elseif(!empty($data['dogma_attributes'])){ + switch($data['groupId']){ + case Config::ESI_GROUP_WORMHOLE_ID: + if( + !empty($wormholesCSVData = static::getCSVData('wormhole', 'name')) && + !empty($wormholeCSVData = $wormholesCSVData[self::formatWormholeName($data['name'])]) + ){ + // found relevant wormhole data in *.csv for current type + if(!empty($scanWormholeStrength = (float)$wormholeCSVData['scanWormholeStrength'])){ + $data['dogma_attributes'][] = [ + 'attributeId' => Config::ESI_DOGMA_ATTRIBUTE_SCANWHSTRENGTH_ID, + 'value' => $scanWormholeStrength + ]; + } + } + break; + } + } + } + /** * load data from API into $this and save $this * @param int $id @@ -138,6 +354,8 @@ class TypeModel extends AbstractUniverseModel { protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){ $data = self::getF3()->ccpClient()->getUniverseTypesData($id); if(!empty($data)){ + $this->manipulateDogmaAttributes($data); + /** * @var $group GroupModel */ @@ -149,4 +367,12 @@ class TypeModel extends AbstractUniverseModel { $this->save(); } } + + /** + * @param string|null $name + * @return string|null + */ + public static function formatWormholeName(?string $name) : ?string { + return (!empty($name) && !empty($format = @end(explode(' ', $name)))) ? $format : null; + } } \ No newline at end of file diff --git a/app/main/model/universe/wormholemodel.php b/app/main/model/universe/wormholemodel.php deleted file mode 100644 index 93d257a0..00000000 --- a/app/main/model/universe/wormholemodel.php +++ /dev/null @@ -1,192 +0,0 @@ - [ - 'type' => Schema::DT_VARCHAR128, - 'nullable' => false, - 'default' => '', - 'index' => true, - 'unique' => true - ], - 'typeId' => [ - 'type' => Schema::DT_INT, - 'index' => true, - 'belongs-to-one' => 'Model\Universe\TypeModel', - 'constraint' => [ - [ - 'table' => 'type', - 'on-delete' => 'SET NULL' - ] - ], - 'validate' => 'notDry' - ], - 'static' => [ - 'type' => Schema::DT_BOOL, - 'nullable' => false, - 'default' => 0 - ], - 'security' => [ - 'type' => Schema::DT_VARCHAR128, - 'nullable' => false, - 'default' => '' - ], - 'massTotal' => [ - 'type' => Schema::DT_BIGINT, - 'nullable' => true, - 'default' => null - ], - 'massIndividual' => [ - 'type' => Schema::DT_BIGINT, - 'nullable' => true, - 'default' => null - ], - 'massRegeneration' => [ - 'type' => Schema::DT_BIGINT, - 'nullable' => true, - 'default' => null - ], - 'maxStableTime' => [ - 'type' => Schema::DT_TINYINT, - 'nullable' => true, - 'default' => null - ], - 'signatureStrength' => [ - 'type' => Schema::DT_FLOAT, - 'nullable' => true, - 'default' => null - ], - 'systems' => [ - 'has-many' => ['Model\Universe\SystemStaticModel', 'wormholeId'] - ] - ]; - - /** - * get wormhole data - * @return \stdClass - */ - public function getData(){ - - $wormholeData = (object) []; - $wormholeData->name = $this->name; - $wormholeData->static = $this->static; - $wormholeData->security = $this->security; - $wormholeData->massTotal = $this->massTotal; - $wormholeData->massIndividual = $this->massIndividual; - - if($this->massRegeneration){ - $wormholeData->massRegeneration = $this->massRegeneration; - } - - $wormholeData->maxStableTime = $this->maxStableTime; - - // signature strength as defined by http://wiki.eve-inspiracy.com/index.php?title=Wormhole_Signature_Strength_List - if($this->signatureStrength){ - $wormholeData->signatureStrength = $this->signatureStrength; - } - - return $wormholeData; - } - - /** - * setter for typeId - * @param string $typeId - * @return string|int|null - */ - public function set_typeId($typeId){ - if(!is_object($typeId)){ - /** - * @var $type TypeModel - */ - $type = $this->rel('typeId'); - $type->loadById((int)$typeId); - $typeId = $type->dry() ? null : $type->_id; - } - return $typeId; - } - - /** - * setter for massTotal - * @param string $mass - * @return int|null - */ - public function set_massTotal($mass){ - $mass = (int)$mass; - return $mass ? : null; - } - - /** - * setter for massIndividual - * @param string $mass - * @return string|null - */ - public function set_massIndividual($mass){ - $mass = (int)$mass; - return $mass ? : null; - } - - /** - * setter for massRegeneration - * @param $mass - * @return int|null - */ - public function set_massRegeneration($mass){ - $mass = (int)$mass; - return $mass ? : null; - } - - /** - * setter for maxStableTime - * @param string $hours - * @return int|null - */ - public function set_maxStableTime($hours){ - $hours = (int)$hours; - return $hours ? : null; - } - - /** - * setter for signatureStrength - * @param string $strength - * @return float|null - */ - public function set_signatureStrength($strength){ - $strength = (float)$strength; - return $strength ? : null; - } - - /** - * @param array $fields - * @return bool - */ - public function exportData(array $fields = [ - 'id', 'name', 'typeId', 'static', 'security', 'massTotal', 'massIndividual', - 'massRegeneration', 'maxStableTime', 'signatureStrength'] - ) : bool { - return parent::exportData($fields); - } - - /** - * @param int $id - * @param string $accessToken - * @param array $additionalOptions - */ - protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){} - -} \ No newline at end of file diff --git a/app/pathfinder.ini b/app/pathfinder.ini index 569b14e1..8f98fe4f 100644 --- a/app/pathfinder.ini +++ b/app/pathfinder.ini @@ -13,8 +13,8 @@ NAME = Pathfinder ; Version is used for CSS/JS cache busting and is part of the URL for static resources: ; e.g. public/js/vX.X.X/app.js ; Syntax: String (current version) -; Default: v1.5.0 -VERSION = v1.5.3 +; Default: v1.5.4 +VERSION = v1.5.4 ; Contact information [optional] ; Shown on 'licence', 'contact' page. @@ -377,6 +377,9 @@ LOG_LINES = 1000 [PATHFINDER.API] CCP_IMAGE_SERVER = https://image.eveonline.com Z_KILLBOARD = https://zkillboard.com/api +EVEEYE = https://eveeye.com +DOTLAN = http://evemaps.dotlan.net +ANOIK = http://anoik.is ; GitHub Developer API GIT_HUB = https://api.github.com diff --git a/app/requirements.ini b/app/requirements.ini index 35525f68..412b8fea 100644 --- a/app/requirements.ini +++ b/app/requirements.ini @@ -56,12 +56,13 @@ PDO_TIMEOUT = 2 ; MySql variables. Values are auto. set as 'SESSION' vars ; https://dev.mysql.com/doc/refman/5.5/en/show-variables.html DEFAULT_STORAGE_ENGINE = InnoDB -CHARACTER_SET_DATABASE = utf8 -CHARACTER_SET_CLIENT = utf8 -CHARACTER_SET_RESULTS = utf8 -CHARACTER_SET_CONNECTION = utf8 -COLLATION_DATABASE = utf8_general_ci -COLLATION_CONNECTION = utf8_general_ci +CHARACTER_SET_SERVER = utf8mb4 +CHARACTER_SET_DATABASE = utf8mb4 +CHARACTER_SET_CLIENT = utf8mb4 +CHARACTER_SET_RESULTS = utf8mb4 +CHARACTER_SET_CONNECTION = utf8mb4 +COLLATION_DATABASE = utf8mb4_unicode_ci +COLLATION_CONNECTION = utf8mb4_unicode_ci FOREIGN_KEY_CHECKS = ON INNODB_FILE_PER_TABLE = ON WAIT_TIMEOUT = 28800 @@ -80,6 +81,4 @@ NODE = 6.0 NPM = 3.10.0 [REQUIREMENTS.DATA] -STRUCTURES = 33 -SHIPS = 491 NEIGHBOURS = 5201 diff --git a/composer.json b/composer.json index 34056062..40fa6e9b 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "react/socket": "1.2.*", "react/promise-stream": "1.1.*", "clue/ndjson-react": "1.0.*", - "exodus4d/pathfinder_esi": "v1.3.1" + "exodus4d/pathfinder_esi": "v1.3.2" }, "suggest": { "ext-redis": "Redis can be used as cache backend." diff --git a/composer.lock b/composer.lock index 76ff5629..7c1b92cf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ad512ee5d1242b7e46b3620b1b05a2a2", + "content-hash": "cd6689b64084cbe1bdbf74b1165fd472", "packages": [ { "name": "cache/adapter-common", @@ -55,7 +55,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" } ], "description": "Common classes for PSR-6 adapters", @@ -123,7 +123,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" } ], "description": "A PSR-6 cache implementation using a php array. This implementation supports tags", @@ -191,7 +191,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" } ], "description": "A PSR-6 cache implementation using filesystem. This implementation supports tags", @@ -253,7 +253,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" } ], "description": "A helper trait and interface to your PSR-6 cache to support hierarchical keys.", @@ -311,7 +311,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" } ], "description": "A decorator that makes your cache support namespaces", @@ -381,7 +381,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" } ], "description": "A PSR-6 cache implementation using Redis (PhpRedis). This implementation supports tags", @@ -432,7 +432,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" }, { "name": "Nicolas Grekas", @@ -505,7 +505,7 @@ { "name": "Tobias Nyholm", "email": "tobias.nyholm@gmail.com", - "homepage": "https://github.com/nyholm" + "homepage": "https://github.com/Nyholm" } ], "description": "A PSR-6 cache implementation using Void. This implementation supports tags", @@ -629,28 +629,30 @@ }, { "name": "doctrine/lexer", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" + "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", - "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea", + "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.2" }, "require-dev": { - "phpunit/phpunit": "^4.5" + "doctrine/coding-standard": "^6.0", + "phpstan/phpstan": "^0.11.8", + "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -663,14 +665,14 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" @@ -685,20 +687,20 @@ "parser", "php" ], - "time": "2019-06-08T11:03:04+00:00" + "time": "2019-07-30T19:33:28+00:00" }, { "name": "egulias/email-validator", - "version": "2.1.9", + "version": "2.1.11", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25" + "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/128cc721d771ec2c46ce59698f4ca42b73f71b25", - "reference": "128cc721d771ec2c46ce59698f4ca42b73f71b25", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23", + "reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23", "shasum": "" }, "require": { @@ -708,7 +710,8 @@ "require-dev": { "dominicsayers/isemail": "dev-master", "phpunit/phpunit": "^4.8.35||^5.7||^6.0", - "satooshi/php-coveralls": "^1.0.1" + "satooshi/php-coveralls": "^1.0.1", + "symfony/phpunit-bridge": "^4.4@dev" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -716,7 +719,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { @@ -742,7 +745,7 @@ "validation", "validator" ], - "time": "2019-06-23T10:14:27+00:00" + "time": "2019-08-13T17:33:27+00:00" }, { "name": "evenement/evenement", @@ -789,16 +792,16 @@ }, { "name": "exodus4d/pathfinder_esi", - "version": "v1.3.1", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/exodus4d/pathfinder_esi.git", - "reference": "392cb81f1efbd9bf69d7d943f4aefdcf3f0a617c" + "reference": "7eecfe27bb15fab5142aceeee30e07355d660773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/exodus4d/pathfinder_esi/zipball/392cb81f1efbd9bf69d7d943f4aefdcf3f0a617c", - "reference": "392cb81f1efbd9bf69d7d943f4aefdcf3f0a617c", + "url": "https://api.github.com/repos/exodus4d/pathfinder_esi/zipball/7eecfe27bb15fab5142aceeee30e07355d660773", + "reference": "7eecfe27bb15fab5142aceeee30e07355d660773", "shasum": "" }, "require": { @@ -824,9 +827,9 @@ ], "description": "ESI API library for Pathfinder", "support": { - "source": "https://github.com/exodus4d/pathfinder_esi/tree/v1.3.1" + "source": "https://github.com/exodus4d/pathfinder_esi/tree/v1.3.2" }, - "time": "2019-05-07T11:53:44+00:00" + "time": "2019-10-08T10:20:37+00:00" }, { "name": "guzzlehttp/guzzle", @@ -1017,16 +1020,16 @@ }, { "name": "league/flysystem", - "version": "1.0.53", + "version": "1.0.55", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "08e12b7628f035600634a5e76d95b5eb66cea674" + "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/08e12b7628f035600634a5e76d95b5eb66cea674", - "reference": "08e12b7628f035600634a5e76d95b5eb66cea674", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6", + "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6", "shasum": "" }, "require": { @@ -1097,20 +1100,20 @@ "sftp", "storage" ], - "time": "2019-06-18T20:09:29+00:00" + "time": "2019-08-24T11:17:19+00:00" }, { "name": "league/html-to-markdown", - "version": "4.8.1", + "version": "4.8.2", "source": { "type": "git", "url": "https://github.com/thephpleague/html-to-markdown.git", - "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1" + "reference": "e747489191f8e9144a7270eb61f8b9516e99e413" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/250d1bf45f80d15594fb6b316df777d6d4c97ad1", - "reference": "250d1bf45f80d15594fb6b316df777d6d4c97ad1", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/e747489191f8e9144a7270eb61f8b9516e99e413", + "reference": "e747489191f8e9144a7270eb61f8b9516e99e413", "shasum": "" }, "require": { @@ -1142,17 +1145,17 @@ "MIT" ], "authors": [ - { - "name": "Nick Cernis", - "email": "nick@cern.is", - "homepage": "http://modernnerd.net", - "role": "Original Author" - }, { "name": "Colin O'Dell", "email": "colinodell@gmail.com", "homepage": "https://www.colinodell.com", "role": "Lead Developer" + }, + { + "name": "Nick Cernis", + "email": "nick@cern.is", + "homepage": "http://modernnerd.net", + "role": "Original Author" } ], "description": "An HTML-to-markdown conversion helper for PHP", @@ -1161,20 +1164,20 @@ "html", "markdown" ], - "time": "2018-12-24T17:21:44+00:00" + "time": "2019-08-02T11:57:39+00:00" }, { "name": "monolog/monolog", - "version": "1.24.0", + "version": "1.25.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" + "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", - "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/70e65a5470a42cfec1a7da00d30edb6e617e8dcf", + "reference": "70e65a5470a42cfec1a7da00d30edb6e617e8dcf", "shasum": "" }, "require": { @@ -1239,7 +1242,7 @@ "logging", "psr-3" ], - "time": "2018-11-05T09:00:11+00:00" + "time": "2019-09-06T13:49:17+00:00" }, { "name": "psr/cache", @@ -1911,16 +1914,16 @@ }, { "name": "symfony/polyfill-iconv", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7" + "reference": "685968b11e61a347c18bf25db32effa478be610f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/f037ea22acfaee983e271dd9c3b8bb4150bd8ad7", - "reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/685968b11e61a347c18bf25db32effa478be610f", + "reference": "685968b11e61a347c18bf25db32effa478be610f", "shasum": "" }, "require": { @@ -1932,7 +1935,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -1966,20 +1969,20 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af" + "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c766e95bec706cdd89903b1eda8afab7d7a6b7af", - "reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6af626ae6fa37d396dc90a399c0ff08e5cfc45b2", + "reference": "6af626ae6fa37d396dc90a399c0ff08e5cfc45b2", "shasum": "" }, "require": { @@ -1993,7 +1996,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -2009,13 +2012,13 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Laurent Bassin", "email": "laurent@bassin.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", @@ -2028,20 +2031,20 @@ "portable", "shim" ], - "time": "2019-03-04T13:44:35+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { @@ -2053,7 +2056,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -2087,20 +2090,20 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c" + "reference": "04ce3335667451138df4307d6a9b61565560199e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c", - "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/04ce3335667451138df4307d6a9b61565560199e", + "reference": "04ce3335667451138df4307d6a9b61565560199e", "shasum": "" }, "require": { @@ -2109,7 +2112,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -2142,7 +2145,7 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" } ], "packages-dev": [], diff --git a/export/csv/system_static.csv b/export/csv/system_static.csv new file mode 100644 index 00000000..ca445475 --- /dev/null +++ b/export/csv/system_static.csv @@ -0,0 +1,3773 @@ +id;systemId;typeId +1;31002604;34140 +2;31002604;34136 +3;31002587;34138 +4;31002587;34135 +5;31002587;34140 +6;31002599;34138 +7;31002599;34140 +8;31002593;34138 +9;31002593;34137 +10;31002593;34140 +11;31002601;34138 +12;31002601;34140 +13;31002586;34138 +14;31002586;34135 +15;31002586;34140 +16;31002589;34135 +17;31002589;34137 +18;31002589;34140 +19;31002603;34140 +20;31002603;34136 +21;31002585;34137 +22;31002585;34140 +23;31002585;34136 +24;31002590;34140 +25;31002590;34136 +26;31002581;34138 +27;31002581;34140 +28;31002581;34136 +29;31002591;34135 +30;31002591;34140 +31;31002591;34136 +32;31002588;34137 +33;31002588;34140 +34;31002588;34136 +35;31002583;34134 +36;31002583;34140 +37;31002594;34138 +38;31002594;34135 +39;31002594;34140 +40;31002582;34135 +41;31002582;34140 +42;31002584;34134 +43;31002584;34137 +44;31002584;34140 +45;31002600;34139 +46;31002600;34140 +47;31002595;34137 +48;31002595;34140 +49;31002602;34134 +50;31002602;34140 +51;31002597;34137 +52;31002597;34140 +53;31002598;34134 +54;31002598;34140 +55;31002596;34135 +56;31002596;34140 +57;31002580;34138 +58;31002580;34140 +59;31002592;34140 +60;31002592;34136 +61;31002572;30700 +62;31002572;30687 +63;31002518;30677 +64;31002518;30672 +65;31002518;30679 +66;31002543;30693 +67;31002543;30688 +68;31002543;30694 +69;31002528;30695 +70;31002528;30687 +71;31002535;30688 +72;31002535;30687 +73;31002530;30688 +74;31002530;30687 +75;31002557;30702 +76;31002557;30688 +77;31002548;30693 +78;31002548;30688 +79;31002548;30692 +80;31002564;30702 +81;31002564;30687 +82;31002524;30678 +83;31002524;30677 +84;31002524;30671 +85;31002514;30667 +86;31002514;30668 +87;31002542;30695 +88;31002542;30690 +89;31002542;30689 +90;31002538;30691 +91;31002538;30687 +92;31002538;30692 +93;31002511;30666 +94;31002511;30668 +95;31002529;30688 +96;31002529;30687 +97;31002525;30678 +98;31002525;30679 +99;31002525;30675 +100;31002562;30699 +101;31002562;30687 +102;31002563;30700 +103;31002563;30687 +104;31002532;30695 +105;31002532;30687 +106;31002569;30701 +107;31002569;30688 +108;31002558;30702 +109;31002558;30688 +110;31002570;30702 +111;31002570;30687 +112;31002549;30691 +113;31002549;30695 +114;31002549;30690 +115;31002537;30688 +116;31002537;30687 +117;31002513;30667 +118;31002513;30666 +119;31002539;30691 +120;31002539;30687 +121;31002539;30692 +122;31002526;30678 +123;31002526;30679 +124;31002526;30673 +125;31002512;30667 +126;31002512;30666 +127;31002574;30702 +128;31002574;30688 +129;31002561;30702 +130;31002561;30687 +131;31002571;30688 +132;31002571;30703 +133;31002567;30701 +134;31002567;30687 +135;31002546;30691 +136;31002546;30695 +137;31002546;30692 +138;31002555;30691 +139;31002555;30687 +140;31002555;30692 +141;31002552;30691 +142;31002552;30693 +143;31002552;30688 +144;31002515;30678 +145;31002515;30677 +146;31002515;30671 +147;31002568;30687 +148;31002573;30702 +149;31002573;30688 +150;31002506;30666 +151;31002506;30668 +152;31002550;30691 +153;31002550;30689 +154;31002550;30687 +155;31002516;30678 +156;31002516;30679 +157;31002516;30674 +158;31002534;30688 +159;31002534;30687 +160;31002508;30667 +161;31002508;30666 +162;31002547;30693 +163;31002547;30688 +164;31002547;30694 +165;31002523;30677 +166;31002523;30672 +167;31002523;30671 +168;31002541;30690 +169;31002541;30687 +170;31002541;30692 +171;31002576;30712 +172;31002544;30695 +173;31002544;30690 +174;31002544;30689 +175;31002509;30667 +176;31002509;30666 +177;31002556;30688 +178;31002556;30703 +179;31002579;30688 +180;31002579;30711 +181;31002554;30693 +182;31002554;30687 +183;31002554;30692 +184;31002519;30677 +185;31002519;30679 +186;31002519;30674 +187;31002536;30695 +188;31002536;30687 +189;31002553;30691 +190;31002553;30692 +191;31002551;30693 +192;31002551;30687 +193;31002551;30692 +194;31002527;30695 +195;31002527;30687 +196;31002545;30691 +197;31002545;30695 +198;31002545;30689 +199;31002559;30704 +200;31002559;30688 +201;31002559;30700 +202;31002540;30691 +203;31002540;30687 +204;31002540;30692 +205;31002510;30667 +206;31002510;30668 +207;31002507;30666 +208;31002507;30668 +209;31002577;30688 +210;31002577;30711 +211;31002565;30702 +212;31002565;30687 +213;31002522;30678 +214;31002522;30677 +215;31002575;30688 +216;31002575;30711 +217;31002521;30677 +218;31002521;30679 +219;31002521;30673 +220;31002566;30701 +221;31002533;30688 +222;31002533;30687 +223;31002517;30678 +224;31002517;30679 +225;31002517;30676 +226;31002520;30678 +227;31002520;30677 +228;31002520;30672 +229;31002560;30702 +230;31002560;30688 +231;31002531;30695 +232;31002531;30687 +233;31002505;30667 +234;31002505;30668 +235;31002578;30688 +236;31002578;30712 +237;31000001;30672 +238;31001753;30691 +239;31001753;30694 +240;31001040;30687 +241;31001338;30687 +242;31000542;30677 +243;31000542;30674 +244;31000085;30667 +245;31001383;30690 +246;31001383;30692 +247;31000805;30677 +248;31000805;30671 +249;31001684;30691 +250;31001684;30692 +251;31002253;30702 +252;31000995;30687 +253;31001907;30699 +254;31001086;30695 +255;31001638;30689 +256;31001638;30692 +257;31001428;30693 +258;31001428;30690 +259;31001169;30695 +260;31000211;30666 +261;31002076;30703 +262;31000479;30677 +263;31000479;30674 +264;31000068;30667 +265;31002132;30700 +266;31001979;30702 +267;31001334;30687 +268;31001814;30693 +269;31001814;30690 +270;31001823;30693 +271;31001823;30692 +272;31002432;30710 +273;31002052;30702 +274;31000230;30666 +275;31002039;30702 +276;31000474;30677 +277;31000474;30674 +278;31002428;30710 +279;31000678;30678 +280;31000678;30672 +281;31002016;30702 +282;31002233;30702 +283;31002190;30700 +284;31000996;30687 +285;31000460;30677 +286;31000460;30674 +287;31000852;30677 +288;31000852;30671 +289;31000859;30677 +290;31000859;30671 +291;31001892;30698 +292;31001896;30698 +293;31001044;30687 +294;31000008;30668 +295;31002146;30700 +296;31001436;30693 +297;31001436;30694 +298;31000677;30678 +299;31000677;30672 +300;31002479;30702 +301;31001692;30691 +302;31001692;30692 +303;31001794;30693 +304;31001794;30690 +305;31002246;30702 +306;31002024;30702 +307;31002135;30700 +308;31000904;30687 +309;31001683;30691 +310;31001683;30692 +311;31001414;30693 +312;31001414;30690 +313;31001977;30702 +314;31000894;30687 +315;31000173;30666 +316;31002023;30702 +317;31001655;30691 +318;31001655;30692 +319;31001209;30687 +320;31001496;30689 +321;31001496;30692 +322;31001639;30689 +323;31001639;30692 +324;31001817;30693 +325;31001817;30690 +326;31001669;30691 +327;31001669;30693 +328;31000533;30677 +329;31000533;30674 +330;31001249;30688 +331;31002476;30703 +332;31000448;30677 +333;31000448;30673 +334;31001760;30691 +335;31001760;30690 +336;31001279;30688 +337;31001615;30690 +338;31001615;30692 +339;31000076;30667 +340;31002167;30700 +341;31000640;30678 +342;31000640;30672 +343;31001168;30695 +344;31000104;30667 +345;31000663;30678 +346;31000663;30672 +347;31002308;30701 +348;31000847;30677 +349;31000847;30671 +350;31000039;30667 +351;31001118;30695 +352;31001416;30693 +353;31001416;30690 +354;31000358;30677 +355;31000358;30673 +356;31001676;30691 +357;31001676;30690 +358;31001432;30691 +359;31001432;30690 +360;31002400;30707 +361;31002161;30700 +362;31001621;30690 +363;31001621;30692 +364;31001167;30695 +365;31002361;30701 +366;31000844;30677 +367;31000844;30671 +368;31002438;30711 +369;31001440;30693 +370;31001440;30694 +371;31002462;30711 +372;31000875;30677 +373;31000875;30671 +374;31000776;30677 +375;31000776;30671 +376;31000033;30668 +377;31001103;30695 +378;31002456;30711 +379;31000927;30687 +380;31002461;30711 +381;31000067;30667 +382;31000203;30666 +383;31000777;30677 +384;31000777;30671 +385;31001311;30688 +386;31001841;30693 +387;31001841;30689 +388;31000310;30666 +389;31001143;30695 +390;31002184;30700 +391;31001524;30691 +392;31001524;30692 +393;31001859;30691 +394;31001859;30690 +395;31000301;30666 +396;31000831;30677 +397;31000831;30671 +398;31001691;30691 +399;31001691;30692 +400;31002247;30702 +401;31001741;30691 +402;31001741;30693 +403;31000885;30687 +404;31002343;30701 +405;31000952;30687 +406;31000107;30667 +407;31000795;30677 +408;31000795;30671 +409;31000621;30678 +410;31000621;30672 +411;31000915;30687 +412;31001367;30687 +413;31001681;30691 +414;31001681;30690 +415;31002199;30702 +416;31000514;30677 +417;31000514;30674 +418;31000947;30687 +419;31001598;30693 +420;31001598;30692 +421;31000259;30666 +422;31001307;30688 +423;31001544;30694 +424;31001544;30692 +425;31001889;30698 +426;31000668;30678 +427;31000668;30672 +428;31001371;30687 +429;31001595;30693 +430;31001595;30692 +431;31001422;30691 +432;31001422;30690 +433;31000773;30677 +434;31000773;30671 +435;31002060;30702 +436;31000184;30666 +437;31000013;30668 +438;31000279;30666 +439;31002009;30702 +440;31000753;30677 +441;31000753;30671 +442;31000296;30666 +443;31002055;30702 +444;31002492;30711 +445;31000267;30666 +446;31001586;30693 +447;31001586;30692 +448;31000199;30666 +449;31002141;30700 +450;31000910;30687 +451;31000292;30666 +452;31000819;30677 +453;31000819;30671 +454;31001490;30690 +455;31001490;30689 +456;31001201;30687 +457;31001592;30693 +458;31001592;30692 +459;31001439;30693 +460;31001439;30694 +461;31002130;30700 +462;31001803;30691 +463;31001803;30693 +464;31001666;30691 +465;31001666;30693 +466;31000622;30678 +467;31000622;30672 +468;31000093;30667 +469;31001777;30691 +470;31001777;30693 +471;31002347;30701 +472;31000416;30677 +473;31000416;30673 +474;31001716;30691 +475;31001716;30689 +476;31001347;30687 +477;31001534;30691 +478;31001534;30692 +479;31001886;30702 +480;31000262;30666 +481;31001092;30695 +482;31002195;30702 +483;31000390;30677 +484;31000390;30673 +485;31000303;30666 +486;31000594;30678 +487;31000594;30672 +488;31001114;30695 +489;31002422;30709 +490;31000052;30667 +491;31000364;30677 +492;31000364;30673 +493;31000432;30677 +494;31000432;30673 +495;31001810;30693 +496;31001810;30692 +497;31002430;30710 +498;31000466;30677 +499;31000466;30674 +500;31002178;30700 +501;31001976;30702 +502;31002442;30711 +503;31000657;30678 +504;31000657;30672 +505;31001186;30687 +506;31000846;30677 +507;31000846;30671 +508;31001796;30691 +509;31001796;30693 +510;31000307;30666 +511;31002078;30703 +512;31000961;30687 +513;31002261;30702 +514;31000864;30677 +515;31000864;30671 +516;31001719;30691 +517;31001719;30689 +518;31000965;30687 +519;31002481;30702 +520;31001491;30690 +521;31001491;30689 +522;31000459;30677 +523;31000459;30674 +524;31001292;30688 +525;31001920;30699 +526;31001382;30690 +527;31001382;30692 +528;31002383;30711 +529;31002294;30701 +530;31000420;30677 +531;31000420;30673 +532;31000661;30678 +533;31000661;30672 +534;31001842;30693 +535;31001842;30689 +536;31000707;30679 +537;31000707;30675 +538;31000426;30677 +539;31000426;30673 +540;31000940;30687 +541;31001939;30698 +542;31001366;30687 +543;31000387;30677 +544;31000387;30673 +545;31000759;30677 +546;31000759;30671 +547;31002447;30711 +548;31000212;30666 +549;31000493;30677 +550;31000493;30674 +551;31002451;30711 +552;31000113;30667 +553;31000066;30667 +554;31000343;30666 +555;31000491;30677 +556;31000491;30674 +557;31001949;30702 +558;31002164;30700 +559;31000362;30677 +560;31000362;30673 +561;31001429;30691 +562;31001429;30690 +563;31000749;30679 +564;31000749;30676 +565;31000503;30677 +566;31000503;30674 +567;31001332;30687 +568;31000231;30666 +569;31001018;30687 +570;31001643;30689 +571;31001643;30692 +572;31001261;30688 +573;31001331;30687 +574;31000201;30666 +575;31001936;30698 +576;31001006;30687 +577;31001556;30693 +578;31001556;30692 +579;31000729;30679 +580;31000729;30675 +581;31000597;30678 +582;31000597;30672 +583;31002152;30700 +584;31001492;30690 +585;31001492;30689 +586;31000691;30678 +587;31000691;30672 +588;31002501;30703 +589;31001645;30691 +590;31001645;30692 +591;31001198;30687 +592;31000163;30666 +593;31002421;30709 +594;31001135;30695 +595;31001453;30693 +596;31001453;30694 +597;31000897;30687 +598;31002291;30701 +599;31001599;30693 +600;31001599;30692 +601;31000630;30678 +602;31000630;30672 +603;31001724;30691 +604;31001724;30692 +605;31001769;30691 +606;31001769;30693 +607;31001379;30690 +608;31001379;30692 +609;31000676;30678 +610;31000676;30672 +611;31002352;30701 +612;31001312;30688 +613;31000544;30677 +614;31000544;30674 +615;31001264;30688 +616;31001116;30695 +617;31001262;30688 +618;31002116;30703 +619;31000572;30678 +620;31000572;30672 +621;31002444;30711 +622;31002367;30708 +623;31001212;30687 +624;31001046;30687 +625;31002394;30709 +626;31001844;30693 +627;31001844;30689 +628;31000547;30677 +629;31000547;30674 +630;31000888;30687 +631;31000891;30687 +632;31000316;30666 +633;31001061;30687 +634;31002437;30711 +635;31001234;30688 +636;31000070;30667 +637;31002214;30702 +638;31000934;30687 +639;31000779;30677 +640;31000779;30671 +641;31002168;30700 +642;31001355;30687 +643;31000502;30677 +644;31000502;30674 +645;31000280;30666 +646;31000800;30677 +647;31000800;30671 +648;31002257;30702 +649;31000807;30677 +650;31000807;30671 +651;31001865;30691 +652;31001865;30692 +653;31000469;30677 +654;31000469;30674 +655;31000732;30679 +656;31000732;30675 +657;31001450;30693 +658;31001450;30694 +659;31001863;30691 +660;31001863;30690 +661;31000563;30678 +662;31000563;30672 +663;31001130;30695 +664;31001495;30689 +665;31001495;30692 +666;31001434;30691 +667;31001434;30690 +668;31001861;30691 +669;31001861;30690 +670;31000701;30678 +671;31000701;30672 +672;31001928;30699 +673;31000007;30668 +674;31001221;30688 +675;31002089;30703 +676;31000044;30667 +677;31002359;30701 +678;31000591;30678 +679;31000591;30672 +680;31001417;30691 +681;31001417;30690 +682;31001346;30687 +683;31002232;30702 +684;31001762;30691 +685;31001762;30690 +686;31002106;30703 +687;31000144;30666 +688;31000299;30666 +689;31000232;30666 +690;31002373;30708 +691;31000017;30668 +692;31002160;30700 +693;31002330;30701 +694;31001455;30694 +695;31001455;30692 +696;31002436;30711 +697;31000470;30677 +698;31000470;30674 +699;31002113;30703 +700;31001740;30691 +701;31001740;30693 +702;31000228;30666 +703;31002068;30703 +704;31001778;30691 +705;31001778;30693 +706;31002487;30712 +707;31000073;30667 +708;31000128;30667 +709;31000368;30677 +710;31000368;30673 +711;31001498;30689 +712;31001498;30692 +713;31002020;30702 +714;31001335;30687 +715;31000429;30677 +716;31000429;30673 +717;31000194;30666 +718;31000156;30666 +719;31000157;30666 +720;31001978;30702 +721;31001197;30687 +722;31000275;30666 +723;31000002;30672 +724;31000130;30667 +725;31001330;30687 +726;31000506;30677 +727;31000506;30674 +728;31000422;30677 +729;31000422;30673 +730;31001211;30687 +731;31000562;30678 +732;31000562;30672 +733;31001735;30691 +734;31001735;30693 +735;31002401;30707 +736;31001170;30695 +737;31000087;30667 +738;31000901;30687 +739;31002245;30702 +740;31000578;30678 +741;31000578;30672 +742;31000168;30666 +743;31001905;30699 +744;31000688;30678 +745;31000688;30672 +746;31000249;30666 +747;31001982;30702 +748;31002268;30702 +749;31000137;30667 +750;31000439;30677 +751;31000439;30673 +752;31000949;30687 +753;31001526;30691 +754;31001526;30692 +755;31001486;30690 +756;31001486;30689 +757;31001314;30688 +758;31000083;30667 +759;31002354;30701 +760;31002417;30709 +761;31000478;30677 +762;31000478;30674 +763;31002404;30710 +764;31002170;30700 +765;31000151;30666 +766;31000645;30678 +767;31000645;30672 +768;31001766;30691 +769;31001766;30693 +770;31001121;30695 +771;31001975;30702 +772;31002356;30701 +773;31001950;30702 +774;31002320;30701 +775;31001662;30691 +776;31001662;30689 +777;31001733;30691 +778;31001733;30692 +779;31000397;30677 +780;31000397;30673 +781;31001411;30693 +782;31001411;30690 +783;31001024;30687 +784;31000879;30677 +785;31000879;30671 +786;31001912;30699 +787;31000690;30678 +788;31000690;30672 +789;31002074;30703 +790;31000731;30679 +791;31000731;30675 +792;31000975;30687 +793;31000889;30687 +794;31001887;30702 +795;31002262;30702 +796;31002491;30702 +797;31002231;30702 +798;31001012;30687 +799;31000020;30668 +800;31001026;30687 +801;31001479;30689 +802;31001479;30692 +803;31000893;30687 +804;31001449;30693 +805;31001449;30694 +806;31001049;30687 +807;31002470;30711 +808;31002382;30711 +809;31001671;30691 +810;31001671;30690 +811;31000406;30677 +812;31000406;30673 +813;31002248;30702 +814;31002407;30710 +815;31002287;30701 +816;31001466;30690 +817;31001466;30689 +818;31000377;30677 +819;31000377;30673 +820;31001051;30687 +821;31002318;30701 +822;31001813;30693 +823;31001813;30692 +824;31000929;30687 +825;31001727;30691 +826;31001727;30692 +827;31000890;30687 +828;31002316;30701 +829;31001713;30691 +830;31001713;30689 +831;31000102;30667 +832;31000366;30677 +833;31000366;30673 +834;31001770;30691 +835;31001770;30693 +836;31001456;30694 +837;31001456;30692 +838;31000923;30687 +839;31000342;30666 +840;31002169;30700 +841;31001308;30688 +842;31000627;30678 +843;31000627;30672 +844;31000946;30687 +845;31002392;30709 +846;31002075;30703 +847;31002325;30701 +848;31001430;30691 +849;31001430;30690 +850;31001973;30702 +851;31000872;30677 +852;31000872;30671 +853;31002406;30710 +854;31002159;30700 +855;31001568;30694 +856;31001568;30692 +857;31000053;30667 +858;31000244;30666 +859;31000413;30677 +860;31000413;30673 +861;31000419;30677 +862;31000419;30673 +863;31001610;30693 +864;31001610;30692 +865;31001667;30691 +866;31001667;30693 +867;31001963;30702 +868;31000190;30666 +869;31000712;30679 +870;31000712;30675 +871;31001903;30699 +872;31001723;30691 +873;31001723;30692 +874;31000340;30666 +875;31002445;30711 +876;31001351;30687 +877;31001641;30689 +878;31001641;30692 +879;31000752;30677 +880;31000752;30671 +881;31000833;30677 +882;31000833;30671 +883;31000765;30677 +884;31000765;30671 +885;31001926;30699 +886;31000921;30687 +887;31001397;30690 +888;31001397;30689 +889;31000538;30677 +890;31000538;30674 +891;31000817;30677 +892;31000817;30671 +893;31000030;30668 +894;31001540;30694 +895;31001540;30692 +896;31001083;30695 +897;31001323;30687 +898;31000797;30677 +899;31000797;30671 +900;31001876;30691 +901;31001876;30692 +902;31000326;30666 +903;31001361;30687 +904;31000438;30677 +905;31000438;30673 +906;31000427;30677 +907;31000427;30673 +908;31002435;30711 +909;31001989;30702 +910;31000914;30687 +911;31001067;30687 +912;31000958;30687 +913;31000143;30666 +914;31000685;30678 +915;31000685;30672 +916;31002315;30701 +917;31001584;30691 +918;31001584;30692 +919;31000345;30666 +920;31001322;30687 +921;31000993;30687 +922;31001293;30688 +923;31001280;30688 +924;31000675;30678 +925;31000675;30672 +926;31000730;30679 +927;31000730;30675 +928;31001189;30687 +929;31000221;30666 +930;31000357;30677 +931;31000357;30673 +932;31002043;30702 +933;31000486;30677 +934;31000486;30674 +935;31001806;30691 +936;31001806;30693 +937;31001864;30691 +938;31001864;30692 +939;31001166;30695 +940;31001789;30693 +941;31001789;30690 +942;31001751;30691 +943;31001751;30693 +944;31000647;30678 +945;31000647;30672 +946;31000869;30677 +947;31000869;30671 +948;31001646;30691 +949;31001646;30692 +950;31000400;30677 +951;31000400;30673 +952;31000985;30687 +953;31001469;30690 +954;31001469;30689 +955;31000674;30678 +956;31000674;30672 +957;31001126;30695 +958;31001205;30687 +959;31001155;30695 +960;31000976;30687 +961;31001203;30687 +962;31000016;30668 +963;31001828;30691 +964;31001828;30693 +965;31000718;30679 +966;31000718;30675 +967;31000924;30687 +968;31001790;30693 +969;31001790;30690 +970;31001408;30693 +971;31001408;30690 +972;31000154;30666 +973;31001746;30691 +974;31001746;30693 +975;31000960;30687 +976;31001029;30687 +977;31001677;30691 +978;31001677;30690 +979;31000972;30687 +980;31000883;30687 +981;31000318;30666 +982;31002045;30702 +983;31001265;30688 +984;31000081;30667 +985;31002018;30702 +986;31001227;30688 +987;31002225;30702 +988;31000069;30667 +989;31000812;30677 +990;31000812;30671 +991;31001516;30689 +992;31001516;30692 +993;31001413;30693 +994;31001413;30690 +995;31001463;30691 +996;31001463;30689 +997;31002026;30702 +998;31001763;30691 +999;31001763;30690 +1000;31000153;30666 +1001;31002101;30703 +1002;31000333;30666 +1003;31001053;30687 +1004;31001377;30690 +1005;31001377;30692 +1006;31001774;30693 +1007;31001774;30692 +1008;31002386;30711 +1009;31000763;30677 +1010;31000763;30671 +1011;31001245;30688 +1012;31002084;30703 +1013;31001985;30702 +1014;31001458;30694 +1015;31001458;30692 +1016;31001834;30693 +1017;31001834;30690 +1018;31000072;30667 +1019;31002193;30700 +1020;31000519;30677 +1021;31000519;30674 +1022;31000905;30687 +1023;31000496;30677 +1024;31000496;30674 +1025;31002281;30702 +1026;31000209;30666 +1027;31001210;30687 +1028;31001052;30687 +1029;31001700;30691 +1030;31001700;30690 +1031;31002003;30702 +1032;31000887;30687 +1033;31000527;30677 +1034;31000527;30674 +1035;31000766;30677 +1036;31000766;30671 +1037;31000214;30666 +1038;31002302;30701 +1039;31001826;30691 +1040;31001826;30693 +1041;31000970;30687 +1042;31002050;30702 +1043;31000881;30687 +1044;31000380;30677 +1045;31000380;30673 +1046;31001940;30698 +1047;31001532;30691 +1048;31001532;30692 +1049;31001161;30695 +1050;31002006;30702 +1051;31000482;30677 +1052;31000482;30674 +1053;31001721;30691 +1054;31001721;30692 +1055;31000189;30666 +1056;31002441;30711 +1057;31002198;30702 +1058;31002057;30702 +1059;31001231;30688 +1060;31001164;30695 +1061;31002059;30702 +1062;31000593;30678 +1063;31000593;30672 +1064;31001679;30691 +1065;31001679;30690 +1066;31002182;30700 +1067;31000115;30667 +1068;31000164;30666 +1069;31001267;30688 +1070;31001033;30687 +1071;31000899;30687 +1072;31000011;30668 +1073;31000550;30677 +1074;31000550;30674 +1075;31000216;30666 +1076;31001707;30691 +1077;31001707;30693 +1078;31000589;30678 +1079;31000589;30672 +1080;31000494;30677 +1081;31000494;30674 +1082;31002503;30702 +1083;31000374;30677 +1084;31000374;30673 +1085;31002088;30703 +1086;31000120;30667 +1087;31000287;30666 +1088;31001147;30695 +1089;31002434;30710 +1090;31001015;30687 +1091;31001387;30690 +1092;31001387;30692 +1093;31001513;30689 +1094;31001513;30692 +1095;31001824;30693 +1096;31001824;30692 +1097;31002013;30702 +1098;31000131;30667 +1099;31001459;30694 +1100;31001459;30692 +1101;31002312;30701 +1102;31002004;30702 +1103;31001066;30687 +1104;31000205;30666 +1105;31000283;30666 +1106;31000820;30677 +1107;31000820;30671 +1108;31000994;30687 +1109;31001609;30693 +1110;31001609;30692 +1111;31002165;30700 +1112;31000694;30678 +1113;31000694;30672 +1114;31000613;30678 +1115;31000613;30672 +1116;31000750;30679 +1117;31000750;30676 +1118;31000588;30678 +1119;31000588;30672 +1120;31000686;30678 +1121;31000686;30672 +1122;31002238;30702 +1123;31002142;30700 +1124;31002177;30700 +1125;31001358;30687 +1126;31001149;30695 +1127;31001045;30687 +1128;31001242;30688 +1129;31001969;30702 +1130;31002134;30700 +1131;31002064;30703 +1132;31001634;30690 +1133;31001634;30692 +1134;31000624;30678 +1135;31000624;30672 +1136;31000119;30667 +1137;31001404;30693 +1138;31001404;30690 +1139;31000064;30667 +1140;31001481;30689 +1141;31001481;30692 +1142;31001672;30691 +1143;31001672;30690 +1144;31002194;30702 +1145;31000475;30677 +1146;31000475;30674 +1147;31001693;30691 +1148;31001693;30692 +1149;31000734;30679 +1150;31000734;30675 +1151;31000906;30687 +1152;31002311;30701 +1153;31000611;30678 +1154;31000611;30672 +1155;31000192;30666 +1156;31000346;30666 +1157;31000160;30666 +1158;31000325;30666 +1159;31002375;30712 +1160;31002377;30712 +1161;31002280;30702 +1162;31002299;30701 +1163;31002243;30702 +1164;31001374;30687 +1165;31000585;30678 +1166;31000585;30672 +1167;31000918;30687 +1168;31002371;30708 +1169;31002136;30700 +1170;31002396;30709 +1171;31002252;30702 +1172;31002107;30703 +1173;31002419;30709 +1174;31001258;30688 +1175;31001848;30693 +1176;31001848;30694 +1177;31001849;30693 +1178;31001849;30694 +1179;31000603;30678 +1180;31000603;30672 +1181;31000289;30666 +1182;31000425;30677 +1183;31000425;30673 +1184;31000446;30677 +1185;31000446;30673 +1186;31001835;30693 +1187;31001835;30690 +1188;31000200;30666 +1189;31000273;30666 +1190;31002292;30701 +1191;31001142;30695 +1192;31001551;30693 +1193;31001551;30692 +1194;31000306;30666 +1195;31002131;30700 +1196;31000546;30677 +1197;31000546;30674 +1198;31001554;30693 +1199;31001554;30692 +1200;31001022;30687 +1201;31000722;30679 +1202;31000722;30675 +1203;31002389;30709 +1204;31000121;30667 +1205;31002213;30702 +1206;31000497;30677 +1207;31000497;30674 +1208;31000226;30666 +1209;31000794;30677 +1210;31000794;30671 +1211;31001505;30691 +1212;31001505;30689 +1213;31000629;30678 +1214;31000629;30672 +1215;31000370;30677 +1216;31000370;30673 +1217;31000150;30666 +1218;31001345;30687 +1219;31002500;30698 +1220;31001990;30702 +1221;31002092;30703 +1222;31001845;30693 +1223;31001845;30689 +1224;31002322;30701 +1225;31000774;30677 +1226;31000774;30671 +1227;31000500;30677 +1228;31000500;30674 +1229;31001393;30690 +1230;31001393;30692 +1231;31001059;30687 +1232;31001712;30691 +1233;31001712;30693 +1234;31001362;30687 +1235;31001141;30695 +1236;31001593;30693 +1237;31001593;30692 +1238;31000080;30667 +1239;31001958;30702 +1240;31000220;30666 +1241;31001910;30699 +1242;31001150;30695 +1243;31000813;30677 +1244;31000813;30671 +1245;31001501;30691 +1246;31001501;30689 +1247;31000022;30668 +1248;31000554;30677 +1249;31000554;30674 +1250;31002118;30703 +1251;31000152;30666 +1252;31001290;30688 +1253;31002204;30702 +1254;31000989;30687 +1255;31000430;30677 +1256;31000430;30673 +1257;31002197;30702 +1258;31001295;30688 +1259;31000114;30667 +1260;31001642;30689 +1261;31001642;30692 +1262;31001175;30695 +1263;31001528;30691 +1264;31001528;30692 +1265;31001013;30687 +1266;31001959;30702 +1267;31000658;30678 +1268;31000658;30672 +1269;31001349;30687 +1270;31002066;30703 +1271;31001195;30687 +1272;31000861;30677 +1273;31000861;30671 +1274;31000733;30679 +1275;31000733;30675 +1276;31000703;30679 +1277;31000703;30675 +1278;31002223;30702 +1279;31000828;30677 +1280;31000828;30671 +1281;31002051;30702 +1282;31001407;30693 +1283;31001407;30690 +1284;31001703;30691 +1285;31001703;30693 +1286;31001073;30695 +1287;31000347;30666 +1288;31001224;30688 +1289;31000395;30677 +1290;31000395;30673 +1291;31000700;30678 +1292;31000700;30672 +1293;31000315;30666 +1294;31000327;30666 +1295;31000699;30678 +1296;31000699;30672 +1297;31002067;30703 +1298;31001206;30687 +1299;31002062;30703 +1300;31001838;30693 +1301;31001838;30690 +1302;31000252;30666 +1303;31001944;30702 +1304;31000799;30677 +1305;31000799;30671 +1306;31000537;30677 +1307;31000537;30674 +1308;31000757;30677 +1309;31000757;30671 +1310;31001497;30689 +1311;31001497;30692 +1312;31001027;30687 +1313;31001122;30695 +1314;31000350;30666 +1315;31000522;30677 +1316;31000522;30674 +1317;31002424;30709 +1318;31001162;30695 +1319;31000507;30677 +1320;31000507;30674 +1321;31001244;30688 +1322;31002453;30711 +1323;31001802;30691 +1324;31001802;30693 +1325;31001882;30702 +1326;31002306;30701 +1327;31002218;30702 +1328;31000736;30679 +1329;31000736;30675 +1330;31001531;30691 +1331;31001531;30692 +1332;31002069;30703 +1333;31000780;30677 +1334;31000780;30671 +1335;31000723;30679 +1336;31000723;30675 +1337;31001445;30691 +1338;31001445;30694 +1339;31000147;30666 +1340;31001825;30693 +1341;31001825;30692 +1342;31000255;30666 +1343;31000202;30666 +1344;31001918;30702 +1345;31001765;30691 +1346;31001765;30693 +1347;31000823;30677 +1348;31000823;30671 +1349;31002145;30700 +1350;31000984;30687 +1351;31001340;30687 +1352;31002449;30711 +1353;31000394;30677 +1354;31000394;30673 +1355;31000472;30677 +1356;31000472;30674 +1357;31000664;30678 +1358;31000664;30672 +1359;31000250;30666 +1360;31001075;30695 +1361;31000785;30677 +1362;31000785;30671 +1363;31001573;30691 +1364;31001573;30692 +1365;31000134;30667 +1366;31002341;30701 +1367;31000133;30667 +1368;31000907;30687 +1369;31002150;30700 +1370;31001870;30691 +1371;31001870;30692 +1372;31000693;30678 +1373;31000693;30672 +1374;31000909;30687 +1375;31001761;30691 +1376;31001761;30690 +1377;31001682;30691 +1378;31001682;30690 +1379;31001031;30687 +1380;31000711;30679 +1381;31000711;30675 +1382;31002270;30702 +1383;31000574;30678 +1384;31000574;30672 +1385;31002117;30703 +1386;31001289;30688 +1387;31000488;30677 +1388;31000488;30674 +1389;31000951;30687 +1390;31000082;30667 +1391;31001577;30691 +1392;31001577;30692 +1393;31001418;30691 +1394;31001418;30690 +1395;31000796;30677 +1396;31000796;30671 +1397;31002331;30701 +1398;31000709;30679 +1399;31000709;30675 +1400;31002015;30702 +1401;31002097;30703 +1402;31002203;30702 +1403;31000096;30667 +1404;31001398;30690 +1405;31001398;30689 +1406;31001725;30691 +1407;31001725;30692 +1408;31000241;30666 +1409;31001654;30691 +1410;31001654;30692 +1411;31000320;30666 +1412;31000097;30667 +1413;31001054;30687 +1414;31000713;30679 +1415;31000713;30675 +1416;31000605;30678 +1417;31000605;30672 +1418;31002443;30711 +1419;31001694;30691 +1420;31001694;30692 +1421;31000801;30677 +1422;31000801;30671 +1423;31000737;30679 +1424;31000737;30675 +1425;31000451;30677 +1426;31000451;30673 +1427;31000725;30679 +1428;31000725;30675 +1429;31002008;30702 +1430;31000242;30666 +1431;31002269;30702 +1432;31002017;30702 +1433;31001576;30691 +1434;31001576;30692 +1435;31000990;30687 +1436;31002209;30702 +1437;31002459;30711 +1438;31000920;30687 +1439;31001336;30687 +1440;31000265;30666 +1441;31000378;30677 +1442;31000378;30673 +1443;31001775;30693 +1444;31001775;30692 +1445;31001675;30691 +1446;31001675;30690 +1447;31000848;30677 +1448;31000848;30671 +1449;31001041;30687 +1450;31001601;30690 +1451;31001601;30692 +1452;31001152;30695 +1453;31000898;30687 +1454;31001350;30687 +1455;31001992;30702 +1456;31000851;30677 +1457;31000851;30671 +1458;31000295;30666 +1459;31002411;30712 +1460;31000359;30677 +1461;31000359;30673 +1462;31000788;30677 +1463;31000788;30671 +1464;31000616;30678 +1465;31000616;30672 +1466;31002187;30700 +1467;31000866;30677 +1468;31000866;30671 +1469;31000935;30687 +1470;31001313;30688 +1471;31000912;30687 +1472;31001069;30687 +1473;31001057;30687 +1474;31002221;30702 +1475;31001558;30693 +1476;31001558;30692 +1477;31001894;30698 +1478;31001993;30702 +1479;31001363;30687 +1480;31000756;30677 +1481;31000756;30671 +1482;31000886;30687 +1483;31001058;30687 +1484;31001089;30695 +1485;31001399;30690 +1486;31001399;30689 +1487;31000054;30667 +1488;31001603;30690 +1489;31001603;30692 +1490;31001625;30691 +1491;31001625;30692 +1492;31002174;30700 +1493;31001929;30699 +1494;31000127;30667 +1495;31001297;30688 +1496;31001943;30702 +1497;31000863;30677 +1498;31000863;30671 +1499;31001485;30689 +1500;31001485;30692 +1501;31001055;30687 +1502;31002276;30702 +1503;31002460;30711 +1504;31002502;30711 +1505;31001047;30687 +1506;31000140;30666 +1507;31001549;30693 +1508;31001549;30692 +1509;31001165;30695 +1510;31000356;30677 +1511;31000356;30673 +1512;31001163;30695 +1513;31002497;30701 +1514;31001507;30690 +1515;31001507;30692 +1516;31002128;30700 +1517;31001462;30691 +1518;31001462;30689 +1519;31001805;30691 +1520;31001805;30693 +1521;31000874;30677 +1522;31000874;30671 +1523;31001127;30695 +1524;31000035;30667 +1525;31001287;30688 +1526;31002335;30701 +1527;31002149;30700 +1528;31001869;30691 +1529;31001869;30692 +1530;31002244;30702 +1531;31002188;30700 +1532;31000328;30666 +1533;31002504;30700 +1534;31000043;30667 +1535;31002296;30701 +1536;31000511;30677 +1537;31000511;30674 +1538;31000665;30678 +1539;31000665;30672 +1540;31001032;30687 +1541;31000745;30679 +1542;31000745;30676 +1543;31001005;30687 +1544;31000061;30667 +1545;31001955;30702 +1546;31000108;30667 +1547;31000743;30679 +1548;31000743;30676 +1549;31000186;30666 +1550;31000565;30678 +1551;31000565;30672 +1552;31001657;30691 +1553;31001657;30689 +1554;31000982;30687 +1555;31001240;30688 +1556;31000955;30687 +1557;31000367;30677 +1558;31000367;30673 +1559;31002058;30702 +1560;31001816;30693 +1561;31001816;30690 +1562;31001352;30687 +1563;31001924;30699 +1564;31002022;30702 +1565;31002260;30702 +1566;31000240;30666 +1567;31001025;30687 +1568;31000767;30677 +1569;31000767;30671 +1570;31000268;30666 +1571;31001525;30691 +1572;31001525;30692 +1573;31000471;30677 +1574;31000471;30674 +1575;31000467;30677 +1576;31000467;30674 +1577;31001843;30693 +1578;31001843;30689 +1579;31002029;30702 +1580;31001098;30695 +1581;31001392;30690 +1582;31001392;30692 +1583;31000399;30677 +1584;31000399;30673 +1585;31002090;30703 +1586;31002286;30701 +1587;31000217;30666 +1588;31001783;30693 +1589;31001783;30692 +1590;31001530;30691 +1591;31001530;30692 +1592;31002180;30700 +1593;31001582;30691 +1594;31001582;30692 +1595;31002028;30702 +1596;31000963;30687 +1597;31001750;30691 +1598;31001750;30693 +1599;31002206;30702 +1600;31001380;30690 +1601;31001380;30692 +1602;31001011;30687 +1603;31000215;30666 +1604;31001620;30690 +1605;31001620;30692 +1606;31000095;30667 +1607;31001343;30687 +1608;31002098;30703 +1609;31000566;30678 +1610;31000566;30672 +1611;31000706;30679 +1612;31000706;30675 +1613;31001930;30699 +1614;31000058;30667 +1615;31001207;30687 +1616;31001961;30702 +1617;31001268;30688 +1618;31001701;30691 +1619;31001701;30693 +1620;31002488;30702 +1621;31001572;30694 +1622;31001572;30692 +1623;31000716;30679 +1624;31000716;30675 +1625;31001078;30695 +1626;31000050;30667 +1627;31000504;30677 +1628;31000504;30674 +1629;31002380;30711 +1630;31000148;30666 +1631;31001517;30689 +1632;31001517;30692 +1633;31001538;30691 +1634;31001538;30692 +1635;31001074;30695 +1636;31000672;30678 +1637;31000672;30672 +1638;31001589;30693 +1639;31001589;30692 +1640;31001427;30693 +1641;31001427;30690 +1642;31000485;30677 +1643;31000485;30674 +1644;31001624;30690 +1645;31001624;30692 +1646;31000573;30678 +1647;31000573;30672 +1648;31002080;30703 +1649;31002278;30702 +1650;31000442;30677 +1651;31000442;30673 +1652;31001527;30691 +1653;31001527;30692 +1654;31000480;30677 +1655;31000480;30674 +1656;31000900;30687 +1657;31000172;30666 +1658;31000545;30677 +1659;31000545;30674 +1660;31001237;30688 +1661;31001611;30693 +1662;31001611;30692 +1663;31001369;30687 +1664;31000762;30677 +1665;31000762;30671 +1666;31001270;30688 +1667;31001965;30702 +1668;31002275;30702 +1669;31002237;30702 +1670;31002337;30701 +1671;31002321;30701 +1672;31002138;30700 +1673;31000204;30666 +1674;31001467;30690 +1675;31001467;30689 +1676;31001133;30695 +1677;31000697;30678 +1678;31000697;30672 +1679;31001878;30691 +1680;31001878;30689 +1681;31002201;30702 +1682;31001255;30688 +1683;31000351;30666 +1684;31001722;30691 +1685;31001722;30692 +1686;31000654;30678 +1687;31000654;30672 +1688;31001546;30694 +1689;31001546;30692 +1690;31001219;30687 +1691;31002041;30702 +1692;31000742;30679 +1693;31000742;30676 +1694;31002175;30700 +1695;31000646;30678 +1696;31000646;30672 +1697;31000944;30687 +1698;31001100;30695 +1699;31000631;30678 +1700;31000631;30672 +1701;31000839;30677 +1702;31000839;30671 +1703;31000810;30677 +1704;31000810;30671 +1705;31000036;30667 +1706;31001109;30695 +1707;31002418;30709 +1708;31001931;30699 +1709;31000391;30677 +1710;31000391;30673 +1711;31000830;30677 +1712;31000830;30671 +1713;31000530;30677 +1714;31000530;30674 +1715;31001228;30688 +1716;31000634;30678 +1717;31000634;30672 +1718;31000518;30677 +1719;31000518;30674 +1720;31000860;30677 +1721;31000860;30671 +1722;31002466;30711 +1723;31001737;30691 +1724;31001737;30693 +1725;31001476;30693 +1726;31001476;30689 +1727;31001873;30691 +1728;31001873;30692 +1729;31000196;30666 +1730;31001076;30695 +1731;31001299;30688 +1732;31001536;30691 +1733;31001536;30692 +1734;31001953;30702 +1735;31001472;30693 +1736;31001472;30689 +1737;31002473;30701 +1738;31000598;30678 +1739;31000598;30672 +1740;31000434;30677 +1741;31000434;30673 +1742;31002049;30702 +1743;31000618;30678 +1744;31000618;30672 +1745;31000964;30687 +1746;31001833;30693 +1747;31001833;30690 +1748;31002304;30701 +1749;31000401;30677 +1750;31000401;30673 +1751;31000523;30677 +1752;31000523;30674 +1753;31001952;30702 +1754;31002242;30702 +1755;31001420;30691 +1756;31001420;30690 +1757;31000596;30678 +1758;31000596;30672 +1759;31000969;30687 +1760;31000615;30678 +1761;31000615;30672 +1762;31001113;30695 +1763;31001758;30691 +1764;31001758;30694 +1765;31001097;30695 +1766;31001784;30693 +1767;31001784;30692 +1768;31001259;30688 +1769;31001070;30687 +1770;31001431;30691 +1771;31001431;30690 +1772;31000197;30666 +1773;31001932;30699 +1774;31001895;30698 +1775;31001337;30687 +1776;31000501;30677 +1777;31000501;30674 +1778;31002229;30702 +1779;31002496;30702 +1780;31001731;30691 +1781;31001731;30692 +1782;31000557;30677 +1783;31000557;30674 +1784;31002362;30701 +1785;31000091;30667 +1786;31001094;30695 +1787;31000836;30677 +1788;31000836;30671 +1789;31000302;30666 +1790;31000625;30678 +1791;31000625;30672 +1792;31001444;30691 +1793;31001444;30694 +1794;31001893;30698 +1795;31001171;30695 +1796;31000281;30666 +1797;31000853;30677 +1798;31000853;30671 +1799;31001579;30691 +1800;31001579;30692 +1801;31001088;30695 +1802;31000365;30677 +1803;31000365;30673 +1804;31001356;30687 +1805;31002112;30703 +1806;31001004;30687 +1807;31002457;30711 +1808;31000332;30666 +1809;31001341;30687 +1810;31002010;30702 +1811;31000348;30666 +1812;31000185;30666 +1813;31001585;30691 +1814;31001585;30692 +1815;31000755;30677 +1816;31000755;30671 +1817;31000508;30677 +1818;31000508;30674 +1819;31001919;30702 +1820;31001388;30690 +1821;31001388;30692 +1822;31002290;30701 +1823;31001215;30687 +1824;31001971;30702 +1825;31002137;30700 +1826;31001901;30699 +1827;31002176;30700 +1828;31000560;30677 +1829;31000560;30674 +1830;31000174;30666 +1831;31000741;30679 +1832;31000741;30675 +1833;31001437;30693 +1834;31001437;30694 +1835;31000950;30687 +1836;31000161;30666 +1837;31002104;30703 +1838;31001080;30695 +1839;31002376;30712 +1840;31002123;30703 +1841;31002336;30701 +1842;31001785;30693 +1843;31001785;30692 +1844;31001612;30690 +1845;31001612;30692 +1846;31001281;30688 +1847;31001608;30693 +1848;31001608;30692 +1849;31000375;30677 +1850;31000375;30673 +1851;31000986;30687 +1852;31002171;30700 +1853;31001957;30702 +1854;31000942;30687 +1855;31000571;30678 +1856;31000571;30672 +1857;31001272;30688 +1858;31001079;30695 +1859;31000126;30667 +1860;31001125;30695 +1861;31002087;30703 +1862;31000099;30667 +1863;31000198;30666 +1864;31001847;30693 +1865;31001847;30694 +1866;31001885;30702 +1867;31001017;30687 +1868;31000838;30677 +1869;31000838;30671 +1870;31000014;30668 +1871;31000476;30677 +1872;31000476;30674 +1873;31000178;30666 +1874;31001743;30691 +1875;31001743;30693 +1876;31000643;30678 +1877;31000643;30672 +1878;31001687;30691 +1879;31001687;30692 +1880;31000747;30679 +1881;31000747;30676 +1882;31000790;30677 +1883;31000790;30671 +1884;31001415;30693 +1885;31001415;30690 +1886;31001623;30690 +1887;31001623;30692 +1888;31001140;30695 +1889;31002183;30700 +1890;31000088;30667 +1891;31001877;30691 +1892;31001877;30689 +1893;31001578;30691 +1894;31001578;30692 +1895;31000558;30677 +1896;31000558;30674 +1897;31000294;30666 +1898;31000312;30666 +1899;31000516;30677 +1900;31000516;30674 +1901;31001204;30687 +1902;31000125;30667 +1903;31002096;30703 +1904;31000437;30677 +1905;31000437;30673 +1906;31002230;30702 +1907;31001446;30691 +1908;31001446;30694 +1909;31000590;30678 +1910;31000590;30672 +1911;31001587;30693 +1912;31001587;30692 +1913;31000726;30679 +1914;31000726;30675 +1915;31000405;30677 +1916;31000405;30673 +1917;31000330;30666 +1918;31001511;30690 +1919;31001511;30692 +1920;31000012;30668 +1921;31000109;30667 +1922;31000617;30678 +1923;31000617;30672 +1924;31000440;30677 +1925;31000440;30673 +1926;31002463;30711 +1927;31001139;30695 +1928;31000021;30668 +1929;31001987;30702 +1930;31001881;30702 +1931;31001373;30687 +1932;31000680;30678 +1933;31000680;30672 +1934;31000586;30678 +1935;31000586;30672 +1936;31002122;30703 +1937;31001218;30687 +1938;31002440;30711 +1939;31000878;30677 +1940;31000878;30671 +1941;31000103;30667 +1942;31000101;30667 +1943;31002127;30700 +1944;31001184;30687 +1945;31002349;30701 +1946;31001659;30691 +1947;31001659;30689 +1948;31001008;30687 +1949;31002207;30702 +1950;31001256;30688 +1951;31000042;30667 +1952;31000792;30677 +1953;31000792;30671 +1954;31001900;30699 +1955;31002370;30708 +1956;31001489;30690 +1957;31001489;30689 +1958;31000977;30687 +1959;31002313;30701 +1960;31002047;30702 +1961;31001757;30691 +1962;31001757;30694 +1963;31002284;30701 +1964;31001960;30702 +1965;31002391;30709 +1966;31002298;30701 +1967;31000623;30678 +1968;31000623;30672 +1969;31001359;30687 +1970;31002228;30702 +1971;31000513;30677 +1972;31000513;30674 +1973;31002346;30701 +1974;31001354;30687 +1975;31001764;30691 +1976;31001764;30690 +1977;31000489;30677 +1978;31000489;30674 +1979;31002477;30701 +1980;31000653;30678 +1981;31000653;30672 +1982;31001523;30691 +1983;31001523;30692 +1984;31001342;30687 +1985;31001602;30690 +1986;31001602;30692 +1987;31000529;30677 +1988;31000529;30674 +1989;31000455;30677 +1990;31000455;30673 +1991;31000248;30666 +1992;31000943;30687 +1993;31001792;30693 +1994;31001792;30690 +1995;31000510;30677 +1996;31000510;30674 +1997;31000074;30667 +1998;31002358;30701 +1999;31000282;30666 +2000;31001728;30691 +2001;31001728;30692 +2002;31002454;30711 +2003;31001618;30690 +2004;31001618;30692 +2005;31001547;30693 +2006;31001547;30692 +2007;31001696;30691 +2008;31001696;30690 +2009;31002215;30702 +2010;31000525;30677 +2011;31000525;30674 +2012;31001190;30687 +2013;31002033;30702 +2014;31000945;30687 +2015;31002301;30701 +2016;31000758;30677 +2017;31000758;30671 +2018;31002166;30700 +2019;31002154;30700 +2020;31000930;30687 +2021;31002234;30702 +2022;31001552;30693 +2023;31001552;30692 +2024;31002489;30712 +2025;31002011;30702 +2026;31000138;30667 +2027;31001934;30698 +2028;31000526;30677 +2029;31000526;30674 +2030;31001922;30699 +2031;31001854;30691 +2032;31001854;30692 +2033;31000815;30677 +2034;31000815;30671 +2035;31001325;30687 +2036;31000065;30667 +2037;31000728;30679 +2038;31000728;30675 +2039;31000931;30687 +2040;31001705;30691 +2041;31001705;30693 +2042;31000638;30678 +2043;31000638;30672 +2044;31001771;30693 +2045;31001771;30692 +2046;31000141;30666 +2047;31000825;30677 +2048;31000825;30671 +2049;31001569;30694 +2050;31001569;30692 +2051;31002429;30710 +2052;31001742;30691 +2053;31001742;30693 +2054;31001664;30691 +2055;31001664;30693 +2056;31001036;30687 +2057;31000925;30687 +2058;31000639;30678 +2059;31000639;30672 +2060;31000239;30666 +2061;31000106;30667 +2062;31001630;30691 +2063;31001630;30692 +2064;31002348;30701 +2065;31000263;30666 +2066;31002120;30703 +2067;31001247;30688 +2068;31001405;30693 +2069;31001405;30690 +2070;31001706;30691 +2071;31001706;30693 +2072;31001890;30698 +2073;31000235;30666 +2074;31000360;30677 +2075;31000360;30673 +2076;31001964;30702 +2077;31001424;30693 +2078;31001424;30690 +2079;31001640;30689 +2080;31001640;30692 +2081;31000118;30667 +2082;31002105;30703 +2083;31001567;30694 +2084;31001567;30692 +2085;31001788;30693 +2086;31001788;30692 +2087;31002467;30711 +2088;31001521;30691 +2089;31001521;30692 +2090;31000462;30677 +2091;31000462;30674 +2092;31001580;30691 +2093;31001580;30692 +2094;31001702;30691 +2095;31001702;30693 +2096;31002374;30712 +2097;31000704;30679 +2098;31000704;30675 +2099;31001108;30695 +2100;31002433;30710 +2101;31000222;30666 +2102;31001956;30702 +2103;31000811;30677 +2104;31000811;30671 +2105;31002111;30703 +2106;31001561;30693 +2107;31001561;30692 +2108;31000816;30677 +2109;31000816;30671 +2110;31001858;30691 +2111;31001858;30690 +2112;31002189;30700 +2113;31001759;30691 +2114;31001759;30690 +2115;31000352;30666 +2116;31000124;30667 +2117;31001065;30687 +2118;31001365;30687 +2119;31000369;30677 +2120;31000369;30673 +2121;31001539;30691 +2122;31001539;30692 +2123;31000933;30687 +2124;31001023;30687 +2125;31000962;30687 +2126;31000679;30678 +2127;31000679;30672 +2128;31002158;30700 +2129;31000304;30666 +2130;31001734;30691 +2131;31001734;30693 +2132;31000298;30666 +2133;31000293;30666 +2134;31002323;30701 +2135;31001381;30690 +2136;31001381;30692 +2137;31001441;30691 +2138;31001441;30694 +2139;31002044;30702 +2140;31000532;30677 +2141;31000532;30674 +2142;31001426;30693 +2143;31001426;30690 +2144;31002405;30710 +2145;31000411;30677 +2146;31000411;30673 +2147;31000353;30666 +2148;31001739;30691 +2149;31001739;30693 +2150;31002172;30700 +2151;31001500;30691 +2152;31001500;30689 +2153;31001148;30695 +2154;31000916;30687 +2155;31001983;30702 +2156;31001158;30695 +2157;31001820;30693 +2158;31001820;30692 +2159;31001000;30687 +2160;31002379;30712 +2161;31000418;30677 +2162;31000418;30673 +2163;31002366;30701 +2164;31000410;30677 +2165;31000410;30673 +2166;31001179;30687 +2167;31000187;30666 +2168;31002037;30702 +2169;31002095;30703 +2170;31000270;30666 +2171;31002303;30701 +2172;31000059;30667 +2173;31001748;30691 +2174;31001748;30693 +2175;31002475;30701 +2176;31001594;30693 +2177;31001594;30692 +2178;31000567;30678 +2179;31000567;30672 +2180;31000671;30678 +2181;31000671;30672 +2182;31000727;30679 +2183;31000727;30675 +2184;31000666;30678 +2185;31000666;30672 +2186;31002046;30702 +2187;31002277;30702 +2188;31000599;30678 +2189;31000599;30672 +2190;31001457;30694 +2191;31001457;30692 +2192;31000721;30679 +2193;31000721;30675 +2194;31000274;30666 +2195;31001102;30695 +2196;31000256;30666 +2197;31001214;30687 +2198;31002053;30702 +2199;31002081;30703 +2200;31001196;30687 +2201;31001942;30698 +2202;31002196;30702 +2203;31000229;30666 +2204;31001613;30690 +2205;31001613;30692 +2206;31002403;30710 +2207;31000089;30667 +2208;31000032;30668 +2209;31000735;30679 +2210;31000735;30675 +2211;31002220;30702 +2212;31000650;30678 +2213;31000650;30672 +2214;31000579;30678 +2215;31000579;30672 +2216;31001357;30687 +2217;31000473;30677 +2218;31000473;30674 +2219;31000290;30666 +2220;31000436;30677 +2221;31000436;30673 +2222;31000932;30687 +2223;31000689;30678 +2224;31000689;30672 +2225;31000417;30677 +2226;31000417;30673 +2227;31001082;30695 +2228;31000331;30666 +2229;31002295;30701 +2230;31000715;30679 +2231;31000715;30675 +2232;31001815;30693 +2233;31001815;30690 +2234;31002499;30700 +2235;31001317;30687 +2236;31002156;30700 +2237;31000234;30666 +2238;31000407;30677 +2239;31000407;30673 +2240;31000246;30666 +2241;31000695;30678 +2242;31000695;30672 +2243;31001316;30688 +2244;31000659;30678 +2245;31000659;30672 +2246;31002086;30703 +2247;31001968;30702 +2248;31002387;30711 +2249;31002309;30701 +2250;31001468;30690 +2251;31001468;30689 +2252;31002297;30701 +2253;31001786;30693 +2254;31001786;30692 +2255;31000937;30687 +2256;31001134;30695 +2257;31001635;30690 +2258;31001635;30692 +2259;31000739;30679 +2260;31000739;30675 +2261;31001506;30690 +2262;31001506;30692 +2263;31000784;30677 +2264;31000784;30671 +2265;31001339;30687 +2266;31001807;30693 +2267;31001807;30692 +2268;31000606;30678 +2269;31000606;30672 +2270;31001222;30688 +2271;31002032;30702 +2272;31001962;30702 +2273;31001660;30691 +2274;31001660;30689 +2275;31001710;30691 +2276;31001710;30693 +2277;31001698;30691 +2278;31001698;30690 +2279;31000363;30677 +2280;31000363;30673 +2281;31000824;30677 +2282;31000824;30671 +2283;31000373;30677 +2284;31000373;30673 +2285;31000277;30666 +2286;31000079;30667 +2287;31001266;30688 +2288;31000850;30677 +2289;31000850;30671 +2290;31001648;30691 +2291;31001648;30692 +2292;31001533;30691 +2293;31001533;30692 +2294;31001019;30687 +2295;31002384;30711 +2296;31000814;30677 +2297;31000814;30671 +2298;31000535;30677 +2299;31000535;30674 +2300;31002264;30702 +2301;31002414;30712 +2302;31000959;30687 +2303;31000117;30667 +2304;31002353;30701 +2305;31001423;30693 +2306;31001423;30690 +2307;31000041;30667 +2308;31002202;30702 +2309;31000464;30677 +2310;31000464;30674 +2311;31001110;30695 +2312;31000372;30677 +2313;31000372;30673 +2314;31000902;30687 +2315;31000183;30666 +2316;31001154;30695 +2317;31000999;30687 +2318;31000159;30666 +2319;31001090;30695 +2320;31000649;30678 +2321;31000649;30672 +2322;31001508;30690 +2323;31001508;30692 +2324;31000772;30677 +2325;31000772;30671 +2326;31001729;30691 +2327;31001729;30692 +2328;31001409;30693 +2329;31001409;30690 +2330;31001182;30687 +2331;31000354;30666 +2332;31000447;30677 +2333;31000447;30673 +2334;31001791;30693 +2335;31001791;30690 +2336;31000038;30667 +2337;31002448;30711 +2338;31000040;30667 +2339;31000056;30667 +2340;31002258;30702 +2341;31000549;30677 +2342;31000549;30674 +2343;31001038;30687 +2344;31001412;30693 +2345;31001412;30690 +2346;31002319;30701 +2347;31000821;30677 +2348;31000821;30671 +2349;31000481;30677 +2350;31000481;30674 +2351;31001911;30699 +2352;31002485;30702 +2353;31000555;30677 +2354;31000555;30674 +2355;31000581;30678 +2356;31000581;30672 +2357;31001518;30689 +2358;31001518;30692 +2359;31000787;30677 +2360;31000787;30671 +2361;31002327;30701 +2362;31002125;30703 +2363;31000539;30677 +2364;31000539;30674 +2365;31001419;30691 +2366;31001419;30690 +2367;31002129;30700 +2368;31001988;30702 +2369;31001874;30691 +2370;31001874;30692 +2371;31000048;30667 +2372;31002249;30702 +2373;31002071;30703 +2374;31000051;30667 +2375;31002147;30700 +2376;31001840;30693 +2377;31001840;30689 +2378;31000822;30677 +2379;31000822;30671 +2380;31000868;30677 +2381;31000868;30671 +2382;31001512;30690 +2383;31001512;30692 +2384;31002416;30712 +2385;31002103;30703 +2386;31002498;30711 +2387;31001629;30691 +2388;31001629;30692 +2389;31001278;30688 +2390;31001562;30693 +2391;31001562;30692 +2392;31001111;30695 +2393;31000636;30678 +2394;31000636;30672 +2395;31001370;30687 +2396;31000856;30677 +2397;31000856;30671 +2398;31001106;30695 +2399;31001564;30693 +2400;31001564;30692 +2401;31002272;30702 +2402;31000388;30677 +2403;31000388;30673 +2404;31001226;30688 +2405;31002077;30703 +2406;31001720;30691 +2407;31001720;30692 +2408;31001755;30691 +2409;31001755;30694 +2410;31002205;30702 +2411;31001581;30691 +2412;31001581;30692 +2413;31000465;30677 +2414;31000465;30674 +2415;31000384;30677 +2416;31000384;30673 +2417;31002241;30702 +2418;31001484;30689 +2419;31001484;30692 +2420;31002065;30703 +2421;31002250;30702 +2422;31001652;30691 +2423;31001652;30692 +2424;31001096;30695 +2425;31000997;30687 +2426;31000415;30677 +2427;31000415;30673 +2428;31001837;30693 +2429;31001837;30690 +2430;31002390;30709 +2431;31001628;30691 +2432;31001628;30692 +2433;31002378;30712 +2434;31001868;30691 +2435;31001868;30692 +2436;31002001;30702 +2437;31001626;30691 +2438;31001626;30692 +2439;31001202;30687 +2440;31000656;30678 +2441;31000656;30672 +2442;31001452;30693 +2443;31001452;30694 +2444;31000049;30667 +2445;31001302;30688 +2446;31000509;30677 +2447;31000509;30674 +2448;31000344;30666 +2449;31000832;30677 +2450;31000832;30671 +2451;31002038;30702 +2452;31001811;30693 +2453;31001811;30692 +2454;31001622;30690 +2455;31001622;30692 +2456;31000349;30666 +2457;31000536;30677 +2458;31000536;30674 +2459;31000170;30666 +2460;31000867;30677 +2461;31000867;30671 +2462;31001282;30688 +2463;31001948;30702 +2464;31000738;30679 +2465;31000738;30675 +2466;31000271;30666 +2467;31001904;30699 +2468;31001827;30691 +2469;31001827;30693 +2470;31000702;30679 +2471;31000702;30675 +2472;31000238;30666 +2473;31001548;30693 +2474;31001548;30692 +2475;31001529;30691 +2476;31001529;30692 +2477;31001799;30691 +2478;31001799;30693 +2479;31000552;30677 +2480;31000552;30674 +2481;31001002;30687 +2482;31001156;30695 +2483;31000180;30666 +2484;31000818;30677 +2485;31000818;30671 +2486;31001704;30691 +2487;31001704;30693 +2488;31000561;30678 +2489;31000561;30672 +2490;31000188;30666 +2491;31000182;30666 +2492;31000620;30678 +2493;31000620;30672 +2494;31000575;30678 +2495;31000575;30672 +2496;31000520;30677 +2497;31000520;30674 +2498;31000626;30678 +2499;31000626;30672 +2500;31000453;30677 +2501;31000453;30673 +2502;31001745;30691 +2503;31001745;30693 +2504;31001617;30690 +2505;31001617;30692 +2506;31001699;30691 +2507;31001699;30690 +2508;31001925;30699 +2509;31001238;30688 +2510;31000396;30677 +2511;31000396;30673 +2512;31001908;30699 +2513;31000835;30677 +2514;31000835;30671 +2515;31000084;30667 +2516;31000710;30679 +2517;31000710;30675 +2518;31000243;30666 +2519;31002219;30702 +2520;31002151;30700 +2521;31000409;30677 +2522;31000409;30673 +2523;31001276;30688 +2524;31000809;30677 +2525;31000809;30671 +2526;31000461;30677 +2527;31000461;30674 +2528;31002192;30700 +2529;31001651;30691 +2530;31001651;30692 +2531;31000843;30677 +2532;31000843;30671 +2533;31002042;30702 +2534;31002372;30708 +2535;31000337;30666 +2536;31000948;30687 +2537;31000760;30677 +2538;31000760;30671 +2539;31001515;30689 +2540;31001515;30692 +2541;31001616;30690 +2542;31001616;30692 +2543;31001099;30695 +2544;31001793;30693 +2545;31001793;30690 +2546;31000974;30687 +2547;31000857;30677 +2548;31000857;30671 +2549;31001213;30687 +2550;31000670;30678 +2551;31000670;30672 +2552;31001298;30688 +2553;31000254;30666 +2554;31001482;30689 +2555;31001482;30692 +2556;31002217;30702 +2557;31000981;30687 +2558;31000385;30677 +2559;31000385;30673 +2560;31000227;30666 +2561;31001836;30693 +2562;31001836;30690 +2563;31000521;30677 +2564;31000521;30674 +2565;31001400;30690 +2566;31001400;30689 +2567;31001303;30688 +2568;31001945;30702 +2569;31001514;30689 +2570;31001514;30692 +2571;31001200;30687 +2572;31001747;30691 +2573;31001747;30693 +2574;31001233;30688 +2575;31001927;30699 +2576;31002025;30702 +2577;31002458;30711 +2578;31002478;30711 +2579;31000911;30687 +2580;31000681;30678 +2581;31000681;30672 +2582;31002271;30702 +2583;31002397;30707 +2584;31001559;30693 +2585;31001559;30692 +2586;31000176;30666 +2587;31000210;30666 +2588;31000882;30687 +2589;31002148;30700 +2590;31001157;30695 +2591;31002365;30701 +2592;31002115;30703 +2593;31002464;30711 +2594;31000987;30687 +2595;31001050;30687 +2596;31000908;30687 +2597;31001425;30693 +2598;31001425;30690 +2599;31000335;30666 +2600;31000595;30678 +2601;31000595;30672 +2602;31002083;30703 +2603;31001236;30688 +2604;31000305;30666 +2605;31001872;30691 +2606;31001872;30692 +2607;31001619;30690 +2608;31001619;30692 +2609;31000421;30677 +2610;31000421;30673 +2611;31001478;30693 +2612;31001478;30689 +2613;31001856;30691 +2614;31001856;30692 +2615;31000111;30667 +2616;31001230;30688 +2617;31002385;30711 +2618;31000781;30677 +2619;31000781;30671 +2620;31001583;30691 +2621;31001583;30692 +2622;31001644;30691 +2623;31001644;30692 +2624;31001043;30687 +2625;31001557;30693 +2626;31001557;30692 +2627;31001981;30702 +2628;31001830;30691 +2629;31001830;30693 +2630;31001653;30691 +2631;31001653;30692 +2632;31002427;30710 +2633;31000568;30678 +2634;31000568;30672 +2635;31001921;30699 +2636;31001191;30687 +2637;31001251;30688 +2638;31000424;30677 +2639;31000424;30673 +2640;31001880;30702 +2641;31000587;30678 +2642;31000587;30672 +2643;31002279;30702 +2644;31002070;30703 +2645;31000195;30666 +2646;31001132;30695 +2647;31000648;30678 +2648;31000648;30672 +2649;31000628;30678 +2650;31000628;30672 +2651;31000031;30668 +2652;31000778;30677 +2653;31000778;30671 +2654;31000793;30677 +2655;31000793;30671 +2656;31001319;30687 +2657;31001107;30695 +2658;31001984;30702 +2659;31000903;30687 +2660;31002324;30701 +2661;31001832;30691 +2662;31001832;30693 +2663;31001542;30694 +2664;31001542;30692 +2665;31001917;30702 +2666;31001406;30693 +2667;31001406;30690 +2668;31001464;30691 +2669;31001464;30689 +2670;31002474;30711 +2671;31002263;30702 +2672;31000559;30677 +2673;31000559;30674 +2674;31000608;30678 +2675;31000608;30672 +2676;31000284;30666 +2677;31001946;30702 +2678;31001421;30691 +2679;31001421;30690 +2680;31002124;30703 +2681;31002093;30703 +2682;31000913;30687 +2683;31000171;30666 +2684;31000381;30677 +2685;31000381;30673 +2686;31000169;30666 +2687;31001246;30688 +2688;31000404;30677 +2689;31000404;30673 +2690;31001862;30691 +2691;31001862;30690 +2692;31002027;30702 +2693;31002307;30701 +2694;31001283;30688 +2695;31000939;30687 +2696;31002133;30700 +2697;31002423;30709 +2698;31000075;30667 +2699;31001649;30691 +2700;31001649;30692 +2701;31001541;30694 +2702;31001541;30692 +2703;31001085;30695 +2704;31002155;30700 +2705;31000398;30677 +2706;31000398;30673 +2707;31000060;30667 +2708;31001819;30693 +2709;31001819;30690 +2710;31000806;30677 +2711;31000806;30671 +2712;31001768;30691 +2713;31001768;30693 +2714;31000300;30666 +2715;31001084;30695 +2716;31001853;30691 +2717;31001853;30692 +2718;31001274;30688 +2719;31002493;30703 +2720;31000512;30677 +2721;31000512;30674 +2722;31001042;30687 +2723;31000019;30668 +2724;31000534;30677 +2725;31000534;30674 +2726;31001550;30693 +2727;31001550;30692 +2728;31002216;30702 +2729;31000528;30677 +2730;31000528;30674 +2731;31001809;30693 +2732;31001809;30692 +2733;31001451;30693 +2734;31001451;30694 +2735;31001137;30695 +2736;31000966;30687 +2737;31001566;30694 +2738;31001566;30692 +2739;31001590;30693 +2740;31001590;30692 +2741;31002014;30702 +2742;31001916;30702 +2743;31000612;30678 +2744;31000612;30672 +2745;31000770;30677 +2746;31000770;30671 +2747;31001846;30693 +2748;31001846;30694 +2749;31002413;30712 +2750;31001614;30690 +2751;31001614;30692 +2752;31002063;30703 +2753;31001797;30691 +2754;31001797;30693 +2755;31000517;30677 +2756;31000517;30674 +2757;31001062;30687 +2758;31002072;30703 +2759;31001145;30695 +2760;31000751;30679 +2761;31000751;30676 +2762;31001818;30693 +2763;31001818;30690 +2764;31001160;30695 +2765;31000116;30667 +2766;31000029;30668 +2767;31000206;30666 +2768;31000667;30678 +2769;31000667;30672 +2770;31002143;30700 +2771;31001034;30687 +2772;31001087;30695 +2773;31001627;30691 +2774;31001627;30692 +2775;31000314;30666 +2776;31002402;30707 +2777;31001680;30691 +2778;31001680;30690 +2779;31001715;30691 +2780;31001715;30689 +2781;31002350;30701 +2782;31000441;30677 +2783;31000441;30673 +2784;31002399;30707 +2785;31001605;30690 +2786;31001605;30692 +2787;31001475;30693 +2788;31001475;30689 +2789;31001442;30691 +2790;31001442;30694 +2791;31001291;30688 +2792;31001503;30691 +2793;31001503;30689 +2794;31001353;30687 +2795;31000024;30668 +2796;31000062;30667 +2797;31000276;30666 +2798;31001821;30693 +2799;31001821;30692 +2800;31001488;30690 +2801;31001488;30689 +2802;31001060;30687 +2803;31001285;30688 +2804;31000651;30678 +2805;31000651;30672 +2806;31001951;30702 +2807;31000456;30677 +2808;31000456;30673 +2809;31000379;30677 +2810;31000379;30673 +2811;31000826;30677 +2812;31000826;30671 +2813;31002426;30710 +2814;31001509;30690 +2815;31001509;30692 +2816;31001902;30699 +2817;31001301;30688 +2818;31000371;30677 +2819;31000371;30673 +2820;31002048;30702 +2821;31001754;30691 +2822;31001754;30694 +2823;31000336;30666 +2824;31002368;30708 +2825;31001980;30702 +2826;31001072;30687 +2827;31001997;30702 +2828;31002328;30701 +2829;31000191;30666 +2830;31001776;30693 +2831;31001776;30692 +2832;31000477;30677 +2833;31000477;30674 +2834;31001738;30691 +2835;31001738;30693 +2836;31001732;30691 +2837;31001732;30692 +2838;31000077;30667 +2839;31001688;30691 +2840;31001688;30692 +2841;31001248;30688 +2842;31000769;30677 +2843;31000769;30671 +2844;31001744;30691 +2845;31001744;30693 +2846;31001375;30690 +2847;31001375;30692 +2848;31000669;30678 +2849;31000669;30672 +2850;31000257;30666 +2851;31000531;30677 +2852;31000531;30674 +2853;31001401;30690 +2854;31001401;30689 +2855;31000309;30666 +2856;31000177;30666 +2857;31000386;30677 +2858;31000386;30673 +2859;31001395;30690 +2860;31001395;30692 +2861;31001543;30694 +2862;31001543;30692 +2863;31001545;30694 +2864;31001545;30692 +2865;31000037;30667 +2866;31002073;30703 +2867;31002288;30701 +2868;31001372;30687 +2869;31000880;30687 +2870;31000145;30666 +2871;31000414;30677 +2872;31000414;30673 +2873;31002410;30710 +2874;31001093;30695 +2875;31000408;30677 +2876;31000408;30673 +2877;31001474;30693 +2878;31001474;30689 +2879;31001239;30688 +2880;31000146;30666 +2881;31000010;30668 +2882;31000954;30687 +2883;31002398;30707 +2884;31000619;30678 +2885;31000619;30672 +2886;31001831;30691 +2887;31001831;30693 +2888;31000376;30677 +2889;31000376;30673 +2890;31001851;30693 +2891;31001851;30694 +2892;31001915;30702 +2893;31001571;30694 +2894;31001571;30692 +2895;31001187;30687 +2896;31000308;30666 +2897;31000149;30666 +2898;31001448;30693 +2899;31001448;30694 +2900;31000744;30679 +2901;31000744;30676 +2902;31001324;30687 +2903;31002469;30711 +2904;31000564;30678 +2905;31000564;30672 +2906;31000078;30667 +2907;31002179;30700 +2908;31001364;30687 +2909;31002408;30710 +2910;31000895;30687 +2911;31000329;30666 +2912;31001938;30698 +2913;31001772;30693 +2914;31001772;30692 +2915;31000724;30679 +2916;31000724;30675 +2917;31001286;30688 +2918;31001056;30687 +2919;31000237;30666 +2920;31000218;30666 +2921;31002200;30702 +2922;31000837;30677 +2923;31000837;30671 +2924;31002119;30703 +2925;31000334;30666 +2926;31000922;30687 +2927;31002157;30700 +2928;31002040;30702 +2929;31002472;30702 +2930;31001396;30690 +2931;31001396;30689 +2932;31000142;30666 +2933;31001535;30691 +2934;31001535;30692 +2935;31002254;30702 +2936;31000317;30666 +2937;31001243;30688 +2938;31002240;30702 +2939;31002345;30701 +2940;31002109;30703 +2941;31001329;30687 +2942;31000871;30677 +2943;31000871;30671 +2944;31002153;30700 +2945;31000224;30666 +2946;31002282;30702 +2947;31000798;30677 +2948;31000798;30671 +2949;31001773;30693 +2950;31001773;30692 +2951;31001460;30691 +2952;31001460;30689 +2953;31001650;30691 +2954;31001650;30692 +2955;31001553;30693 +2956;31001553;30692 +2957;31001875;30691 +2958;31001875;30692 +2959;31001597;30693 +2960;31001597;30692 +2961;31000873;30677 +2962;31000873;30671 +2963;31002210;30702 +2964;31000499;30677 +2965;31000499;30674 +2966;31001128;30695 +2967;31001064;30687 +2968;31000652;30678 +2969;31000652;30672 +2970;31000829;30677 +2971;31000829;30671 +2972;31000026;30668 +2973;31002340;30701 +2974;31002393;30709 +2975;31001604;30690 +2976;31001604;30692 +2977;31001897;30698 +2978;31000110;30667 +2979;31001678;30691 +2980;31001678;30690 +2981;31000458;30677 +2982;31000458;30673 +2983;31000433;30677 +2984;31000433;30673 +2985;31002126;30700 +2986;31001689;30691 +2987;31001689;30692 +2988;31001537;30691 +2989;31001537;30692 +2990;31001866;30691 +2991;31001866;30692 +2992;31001674;30691 +2993;31001674;30690 +2994;31002351;30701 +2995;31000219;30666 +2996;31000541;30677 +2997;31000541;30674 +2998;31000570;30678 +2999;31000570;30672 +3000;31000272;30666 +3001;31000046;30667 +3002;31001718;30691 +3003;31001718;30689 +3004;31002439;30711 +3005;31000783;30677 +3006;31000783;30671 +3007;31002121;30703 +3008;31001574;30691 +3009;31001574;30692 +3010;31001665;30691 +3011;31001665;30693 +3012;31000637;30678 +3013;31000637;30672 +3014;31000761;30677 +3015;31000761;30671 +3016;31000967;30687 +3017;31001131;30695 +3018;31000253;30666 +3019;31001493;30689 +3020;31001493;30692 +3021;31002012;30702 +3022;31000236;30666 +3023;31001117;30695 +3024;31001822;30693 +3025;31001822;30692 +3026;31000139;30667 +3027;31000600;30678 +3028;31000600;30672 +3029;31001216;30687 +3030;31000714;30679 +3031;31000714;30675 +3032;31002100;30703 +3033;31001717;30691 +3034;31001717;30689 +3035;31001714;30691 +3036;31001714;30689 +3037;31000926;30687 +3038;31001159;30695 +3039;31000978;30687 +3040;31000854;30677 +3041;31000854;30671 +3042;31000505;30677 +3043;31000505;30674 +3044;31001891;30698 +3045;31000018;30668 +3046;31001273;30688 +3047;31000435;30677 +3048;31000435;30673 +3049;31000956;30687 +3050;31002019;30702 +3051;31001178;30687 +3052;31000980;30687 +3053;31000355;30677 +3054;31000355;30673 +3055;31001967;30702 +3056;31000992;30687 +3057;31001181;30687 +3058;31000548;30677 +3059;31000548;30674 +3060;31002021;30702 +3061;31001804;30691 +3062;31001804;30693 +3063;31000123;30667 +3064;31002363;30701 +3065;31002102;30703 +3066;31000683;30678 +3067;31000683;30672 +3068;31002094;30703 +3069;31001756;30691 +3070;31001756;30694 +3071;31002465;30711 +3072;31000003;30672 +3073;31001007;30687 +3074;31001252;30688 +3075;31002236;30702 +3076;31002369;30708 +3077;31001269;30688 +3078;31000285;30666 +3079;31000322;30666 +3080;31000027;30668 +3081;31000614;30678 +3082;31000614;30672 +3083;31001633;30690 +3084;31001633;30692 +3085;31001937;30698 +3086;31002061;30703 +3087;31000841;30677 +3088;31000841;30671 +3089;31000098;30667 +3090;31000553;30677 +3091;31000553;30674 +3092;31000445;30677 +3093;31000445;30673 +3094;31001063;30687 +3095;31000928;30687 +3096;31000845;30677 +3097;31000845;30671 +3098;31000865;30677 +3099;31000865;30671 +3100;31001461;30691 +3101;31001461;30689 +3102;31000181;30666 +3103;31001781;30691 +3104;31001781;30693 +3105;31001787;30693 +3106;31001787;30692 +3107;31001035;30687 +3108;31001123;30695 +3109;31000720;30679 +3110;31000720;30675 +3111;31002305;30701 +3112;31000286;30666 +3113;31002208;30702 +3114;31001327;30687 +3115;31000936;30687 +3116;31001402;30690 +3117;31001402;30689 +3118;31002293;30701 +3119;31001443;30691 +3120;31001443;30694 +3121;31001991;30702 +3122;31000468;30677 +3123;31000468;30674 +3124;31000834;30677 +3125;31000834;30671 +3126;31001487;30690 +3127;31001487;30689 +3128;31002227;30702 +3129;31001570;30694 +3130;31001570;30692 +3131;31001879;30691 +3132;31001879;30689 +3133;31002415;30712 +3134;31001637;30690 +3135;31001637;30692 +3136;31000789;30677 +3137;31000789;30671 +3138;31001565;30693 +3139;31001565;30692 +3140;31000297;30666 +3141;31001995;30702 +3142;31000917;30687 +3143;31000983;30687 +3144;31001021;30687 +3145;31001391;30690 +3146;31001391;30692 +3147;31001385;30690 +3148;31001385;30692 +3149;31001223;30688 +3150;31000175;30666 +3151;31001483;30689 +3152;31001483;30692 +3153;31001172;30695 +3154;31002226;30702 +3155;31001795;30691 +3156;31001795;30693 +3157;31002007;30702 +3158;31002054;30702 +3159;31000896;30687 +3160;31000492;30677 +3161;31000492;30674 +3162;31000319;30666 +3163;31002409;30710 +3164;31001947;30702 +3165;31001839;30693 +3166;31001839;30690 +3167;31001146;30695 +3168;31001328;30687 +3169;31001780;30691 +3170;31001780;30693 +3171;31002483;30699 +3172;31001390;30690 +3173;31001390;30692 +3174;31001966;30702 +3175;31001318;30687 +3176;31002334;30701 +3177;31001690;30691 +3178;31001690;30692 +3179;31002091;30703 +3180;31001333;30687 +3181;31001091;30695 +3182;31002310;30701 +3183;31001673;30691 +3184;31001673;30690 +3185;31001071;30687 +3186;31001888;30702 +3187;31000269;30666 +3188;31001315;30688 +3189;31000543;30677 +3190;31000543;30674 +3191;31000576;30678 +3192;31000576;30672 +3193;31001749;30691 +3194;31001749;30693 +3195;31000092;30667 +3196;31001300;30688 +3197;31000988;30687 +3198;31002333;30701 +3199;31001028;30687 +3200;31002256;30702 +3201;31002035;30702 +3202;31001030;30687 +3203;31002468;30711 +3204;31002259;30702 +3205;31000260;30666 +3206;31001996;30702 +3207;31000213;30666 +3208;31002114;30703 +3209;31000855;30677 +3210;31000855;30671 +3211;31002099;30703 +3212;31001600;30690 +3213;31001600;30692 +3214;31002482;30702 +3215;31000754;30677 +3216;31000754;30671 +3217;31000339;30666 +3218;31001480;30689 +3219;31001480;30692 +3220;31001447;30691 +3221;31001447;30694 +3222;31001403;30693 +3223;31001403;30690 +3224;31002420;30709 +3225;31001253;30688 +3226;31000452;30677 +3227;31000452;30673 +3228;31001767;30691 +3229;31001767;30693 +3230;31000454;30677 +3231;31000454;30673 +3232;31000804;30677 +3233;31000804;30671 +3234;31001193;30687 +3235;31001039;30687 +3236;31001410;30693 +3237;31001410;30690 +3238;31001941;30698 +3239;31000129;30667 +3240;31000162;30666 +3241;31002283;30701 +3242;31000311;30666 +3243;31001081;30695 +3244;31000047;30667 +3245;31001321;30687 +3246;31001477;30693 +3247;31001477;30689 +3248;31000264;30666 +3249;31001310;30688 +3250;31001138;30695 +3251;31002181;30700 +3252;31002266;30702 +3253;31000321;30666 +3254;31000155;30666 +3255;31000635;30678 +3256;31000635;30672 +3257;31000449;30677 +3258;31000449;30673 +3259;31001284;30688 +3260;31002002;30702 +3261;31001779;30691 +3262;31001779;30693 +3263;31001520;30691 +3264;31001520;30692 +3265;31000484;30677 +3266;31000484;30674 +3267;31000487;30677 +3268;31000487;30674 +3269;31001263;30688 +3270;31001048;30687 +3271;31001016;30687 +3272;31001275;30688 +3273;31002274;30702 +3274;31002452;30711 +3275;31001368;30687 +3276;31000122;30667 +3277;31001502;30691 +3278;31001502;30689 +3279;31001808;30693 +3280;31001808;30692 +3281;31002144;30700 +3282;31001883;30702 +3283;31002255;30702 +3284;31000768;30677 +3285;31000768;30671 +3286;31001115;30695 +3287;31000991;30687 +3288;31000786;30677 +3289;31000786;30671 +3290;31000602;30678 +3291;31000602;30672 +3292;31001241;30688 +3293;31002222;30702 +3294;31002326;30701 +3295;31001782;30691 +3296;31001782;30693 +3297;31001994;30702 +3298;31000313;30666 +3299;31001037;30687 +3300;31000132;30667 +3301;31000556;30677 +3302;31000556;30674 +3303;31000006;30672 +3304;31000004;30672 +3305;31001077;30695 +3306;31000842;30677 +3307;31000842;30671 +3308;31001494;30689 +3309;31001494;30692 +3310;31001124;30695 +3311;31001208;30687 +3312;31000644;30678 +3313;31000644;30672 +3314;31001685;30691 +3315;31001685;30692 +3316;31001344;30687 +3317;31002191;30700 +3318;31000166;30666 +3319;31001607;30693 +3320;31001607;30692 +3321;31000609;30678 +3322;31000609;30672 +3323;31001470;30690 +3324;31001470;30689 +3325;31000025;30668 +3326;31001378;30690 +3327;31001378;30692 +3328;31001217;30687 +3329;31001736;30691 +3330;31001736;30693 +3331;31000251;30666 +3332;31000892;30687 +3333;31001194;30687 +3334;31002446;30711 +3335;31001923;30699 +3336;31001095;30695 +3337;31002139;30700 +3338;31000423;30677 +3339;31000423;30673 +3340;31001906;30699 +3341;31001884;30702 +3342;31000015;30668 +3343;31001360;30687 +3344;31001708;30691 +3345;31001708;30693 +3346;31002332;30701 +3347;31001730;30691 +3348;31001730;30692 +3349;31001522;30691 +3350;31001522;30692 +3351;31000862;30677 +3352;31000862;30671 +3353;31000412;30677 +3354;31000412;30673 +3355;31000090;30667 +3356;31001288;30688 +3357;31001376;30690 +3358;31001376;30692 +3359;31002338;30701 +3360;31000808;30677 +3361;31000808;30671 +3362;31000392;30677 +3363;31000392;30673 +3364;31002211;30702 +3365;31000941;30687 +3366;31000708;30679 +3367;31000708;30675 +3368;31001433;30691 +3369;31001433;30690 +3370;31000383;30677 +3371;31000383;30673 +3372;31001304;30688 +3373;31001504;30691 +3374;31001504;30689 +3375;31001606;30693 +3376;31001606;30692 +3377;31002450;30711 +3378;31001812;30693 +3379;31001812;30692 +3380;31002079;30703 +3381;31001309;30688 +3382;31001647;30691 +3383;31001647;30692 +3384;31000551;30677 +3385;31000551;30674 +3386;31000463;30677 +3387;31000463;30674 +3388;31000382;30677 +3389;31000382;30673 +3390;31001225;30688 +3391;31001632;30690 +3392;31001632;30692 +3393;31001560;30693 +3394;31001560;30692 +3395;31001199;30687 +3396;31002173;30700 +3397;31001260;30688 +3398;31001999;30702 +3399;31000641;30678 +3400;31000641;30672 +3401;31002342;30701 +3402;31001136;30695 +3403;31000490;30677 +3404;31000490;30674 +3405;31000957;30687 +3406;31001935;30698 +3407;31000324;30666 +3408;31001499;30689 +3409;31001499;30692 +3410;31000288;30666 +3411;31000719;30679 +3412;31000719;30675 +3413;31002412;30712 +3414;31001798;30691 +3415;31001798;30693 +3416;31000968;30687 +3417;31001180;30687 +3418;31001670;30691 +3419;31001670;30690 +3420;31001563;30693 +3421;31001563;30692 +3422;31000341;30666 +3423;31000775;30677 +3424;31000775;30671 +3425;31001471;30690 +3426;31001471;30689 +3427;31000323;30666 +3428;31001998;30702 +3429;31000498;30677 +3430;31000498;30674 +3431;31001305;30688 +3432;31000791;30677 +3433;31000791;30671 +3434;31002082;30703 +3435;31001591;30693 +3436;31001591;30692 +3437;31002163;30700 +3438;31001909;30699 +3439;31000179;30666 +3440;31002480;30701 +3441;31001173;30695 +3442;31001277;30688 +3443;31001711;30691 +3444;31001711;30693 +3445;31002360;30701 +3446;31001326;30687 +3447;31000158;30666 +3448;31001588;30693 +3449;31001588;30692 +3450;31002031;30702 +3451;31000245;30666 +3452;31001933;30699 +3453;31000443;30677 +3454;31000443;30673 +3455;31002425;30709 +3456;31001465;30691 +3457;31001465;30689 +3458;31001829;30691 +3459;31001829;30693 +3460;31000258;30666 +3461;31002344;30701 +3462;31002185;30700 +3463;31001250;30688 +3464;31002285;30701 +3465;31000979;30687 +3466;31000577;30678 +3467;31000577;30672 +3468;31001510;30690 +3469;31001510;30692 +3470;31001974;30702 +3471;31001697;30691 +3472;31001697;30690 +3473;31000100;30667 +3474;31001235;30688 +3475;31000973;30687 +3476;31002034;30702 +3477;31000662;30678 +3478;31000662;30672 +3479;31000748;30679 +3480;31000748;30676 +3481;31001183;30687 +3482;31001394;30690 +3483;31001394;30692 +3484;31000601;30678 +3485;31000601;30672 +3486;31000655;30678 +3487;31000655;30672 +3488;31000673;30678 +3489;31000673;30672 +3490;31000705;30679 +3491;31000705;30675 +3492;31002239;30702 +3493;31001709;30691 +3494;31001709;30693 +3495;31000105;30667 +3496;31002110;30703 +3497;31000771;30677 +3498;31000771;30671 +3499;31002085;30703 +3500;31002355;30701 +3501;31001348;30687 +3502;31001972;30702 +3503;31000167;30666 +3504;31000233;30666 +3505;31001801;30691 +3506;31001801;30693 +3507;31000483;30677 +3508;31000483;30674 +3509;31002251;30702 +3510;31001871;30691 +3511;31001871;30692 +3512;31000540;30677 +3513;31000540;30674 +3514;31001661;30691 +3515;31001661;30689 +3516;31000266;30666 +3517;31001668;30691 +3518;31001668;30693 +3519;31000136;30667 +3520;31002388;30711 +3521;31001596;30693 +3522;31001596;30692 +3523;31000698;30678 +3524;31000698;30672 +3525;31001555;30693 +3526;31001555;30692 +3527;31001857;30691 +3528;31001857;30692 +3529;31000884;30687 +3530;31001320;30687 +3531;31001153;30695 +3532;31002140;30700 +3533;31000223;30666 +3534;31000953;30687 +3535;31001009;30687 +3536;31002036;30702 +3537;31001020;30687 +3538;31001014;30687 +3539;31000034;30668 +3540;31002056;30702 +3541;31000717;30679 +3542;31000717;30675 +3543;31002490;30700 +3544;31000938;30687 +3545;31001105;30695 +3546;31001954;30702 +3547;31001101;30695 +3548;31001575;30691 +3549;31001575;30692 +3550;31001389;30690 +3551;31001389;30692 +3552;31002381;30711 +3553;31000919;30687 +3554;31001176;30695 +3555;31001855;30691 +3556;31001855;30692 +3557;31000094;30667 +3558;31001068;30687 +3559;31002471;30701 +3560;31002495;30711 +3561;31001192;30687 +3562;31000361;30677 +3563;31000361;30673 +3564;31000692;30678 +3565;31000692;30672 +3566;31001913;30699 +3567;31000023;30668 +3568;31001899;30699 +3569;31000291;30666 +3570;31000876;30677 +3571;31000876;30671 +3572;31001112;30695 +3573;31000998;30687 +3574;31001631;30690 +3575;31001631;30692 +3576;31000607;30678 +3577;31000607;30672 +3578;31000610;30678 +3579;31000610;30672 +3580;31002431;30710 +3581;31000746;30679 +3582;31000746;30676 +3583;31000696;30678 +3584;31000696;30672 +3585;31000802;30677 +3586;31000802;30671 +3587;31001185;30687 +3588;31000278;30666 +3589;31001151;30695 +3590;31000207;30666 +3591;31000086;30667 +3592;31001860;30691 +3593;31001860;30690 +3594;31001656;30691 +3595;31001656;30692 +3596;31000583;30678 +3597;31000583;30672 +3598;31000247;30666 +3599;31001800;30691 +3600;31001800;30693 +3601;31000782;30677 +3602;31000782;30671 +3603;31001435;30693 +3604;31001435;30694 +3605;31000687;30678 +3606;31000687;30672 +3607;31000045;30667 +3608;31001188;30687 +3609;31001229;30688 +3610;31000112;30667 +3611;31001850;30693 +3612;31001850;30694 +3613;31002486;30698 +3614;31001695;30691 +3615;31001695;30690 +3616;31001119;30695 +3617;31000971;30687 +3618;31000009;30668 +3619;31001658;30691 +3620;31001658;30689 +3621;31000740;30679 +3622;31000740;30675 +3623;31001752;30691 +3624;31001752;30693 +3625;31001636;30690 +3626;31001636;30692 +3627;31000569;30678 +3628;31000569;30672 +3629;31001220;30687 +3630;31001726;30691 +3631;31001726;30692 +3632;31001003;30687 +3633;31001852;30691 +3634;31001852;30692 +3635;31000063;30667 +3636;31000684;30678 +3637;31000684;30672 +3638;31001010;30687 +3639;31002162;30700 +3640;31002395;30709 +3641;31000524;30677 +3642;31000524;30674 +3643;31002357;30701 +3644;31001001;30687 +3645;31002273;30702 +3646;31001898;30699 +3647;31001384;30690 +3648;31001384;30692 +3649;31002484;30711 +3650;31001257;30688 +3651;31001271;30688 +3652;31002314;30701 +3653;31002265;30702 +3654;31001120;30695 +3655;31001970;30702 +3656;31000431;30677 +3657;31000431;30673 +3658;31000225;30666 +3659;31002317;30701 +3660;31000582;30678 +3661;31000582;30672 +3662;31001177;30695 +3663;31001232;30688 +3664;31000338;30666 +3665;31001473;30693 +3666;31001473;30689 +3667;31000495;30677 +3668;31000495;30674 +3669;31000135;30667 +3670;31001174;30695 +3671;31001438;30693 +3672;31001438;30694 +3673;31000632;30678 +3674;31000632;30672 +3675;31001663;30691 +3676;31001663;30693 +3677;31000428;30677 +3678;31000428;30673 +3679;31000580;30678 +3680;31000580;30672 +3681;31000633;30678 +3682;31000633;30672 +3683;31000849;30677 +3684;31000849;30671 +3685;31002455;30711 +3686;31000604;30678 +3687;31000604;30672 +3688;31000840;30677 +3689;31000840;30671 +3690;31001914;30702 +3691;31000803;30677 +3692;31000803;30671 +3693;31001254;30688 +3694;31000055;30667 +3695;31000193;30666 +3696;31002267;30702 +3697;31000403;30677 +3698;31000403;30673 +3699;31001144;30695 +3700;31002030;30702 +3701;31002364;30701 +3702;31000592;30678 +3703;31000592;30672 +3704;31001986;30702 +3705;31000028;30668 +3706;31000208;30666 +3707;31001867;30691 +3708;31001867;30692 +3709;31002339;30701 +3710;31000402;30677 +3711;31000402;30673 +3712;31000071;30667 +3713;31000858;30677 +3714;31000858;30671 +3715;31001306;30688 +3716;31002108;30703 +3717;31002005;30702 +3718;31001104;30695 +3719;31000057;30667 +3720;31000764;30677 +3721;31000764;30671 +3722;31001686;30691 +3723;31001686;30692 +3724;31002300;30701 +3725;31002235;30702 +3726;31002329;30701 +3727;31002000;30702 +3728;31001454;30694 +3729;31001454;30692 +3730;31000584;30678 +3731;31000584;30672 +3732;31000457;30677 +3733;31000457;30673 +3734;31002289;30701 +3735;31000877;30677 +3736;31000877;30671 +3737;31000870;30677 +3738;31000870;30671 +3739;31002494;30702 +3740;31000515;30677 +3741;31000515;30674 +3742;31000261;30666 +3743;31002186;30700 +3744;31000444;30677 +3745;31000444;30673 +3746;31001519;30689 +3747;31001519;30692 +3748;31002212;30702 +3749;31000389;30677 +3750;31000389;30673 +3751;31001294;30688 +3752;31000393;30677 +3753;31000393;30673 +3754;31000165;30666 +3755;31000450;30677 +3756;31000450;30673 +3757;31002224;30702 +3758;31000827;30677 +3759;31000827;30671 +3760;31001129;30695 +3761;31001296;30688 +3762;31000682;30678 +3763;31000682;30672 +3764;31000642;30678 +3765;31000642;30672 +3766;31000660;30678 +3767;31000660;30672 +3768;31001386;30690 +3769;31001386;30692 +3770;31000005;34370 +3771;31000005;34368 +3772;31000005;34369 \ No newline at end of file diff --git a/export/csv/wormhole.csv b/export/csv/wormhole.csv index b2274e5a..262c1886 100644 --- a/export/csv/wormhole.csv +++ b/export/csv/wormhole.csv @@ -1,90 +1,90 @@ -"Id";"Name";"Security";"MassTotal";"MassIndividual";"MassRegeneration";"MaxStableTime";"SignatureStrength"; -"1";"A009";"C13";"500000000";"5000000";"3000000000";"16";; -"2";"A239";"L";"2000000000";"300000000";;"24";"5"; -"3";"A641";"H";"2000000000";"1000000000";;"16";"10"; -"4";"A982";"C6";"3000000000";"300000000";;"24";"2.22"; -"5";"B041";"C6";"3000000000";"300000000";"500000000";"48";; -"6";"B274";"H";"2000000000";"300000000";;"24";"10"; -"7";"B449";"H";"2000000000";"1000000000";;"16";"2.5"; -"8";"B520";"H";"3000000000";"300000000";"500000000";"24";; -"9";"C008";"C5";"1000000000";"5000000";"3000000000";"16";; -"10";"C125";"C2";"1000000000";"20000000";;"16";"6.67"; -"11";"C140";"L";"3000000000";"1350000000";;"24";"5"; -"12";"C247";"C3";"2000000000";"300000000";;"16";"10"; -"13";"C248";"0.0";"3000000000";"1350000000";"500000000";"24";; -"14";"C391";"L";"3000000000";"1000000000";"500000000";"24";; -"15";"D364";"C2";"1000000000";"300000000";;"16";"1.25"; -"16";"D382";"C2";"2000000000";"300000000";;"16";"6.67"; -"17";"D792";"H";"3000000000";"1000000000";;"24";"2.5"; -"18";"D845";"H";"5000000000";"300000000";"500000000";"24";"5"; -"19";"E004";"C1";"1000000000";"5000000";"3000000000";"16";; -"20";"E175";"C4";"2000000000";"300000000";;"16";"5"; -"21";"E545";"0.0";"2000000000";"300000000";;"24";"2.5"; -"23";"G024";"C2";"2000000000";"300000000";;"16";"1.25"; -"24";"H121";"C1";"500000000";"20000000";;"16";"10"; -"25";"H296";"C5";"3000000000";"1350000000";;"24";"10"; -"26";"H900";"C5";"3000000000";"300000000";;"24";"2.5"; -"27";"I182";"C2";"2000000000";"300000000";;"16";"4"; -"28";"J244";"L";"1000000000";"20000000";;"24";"5"; -"29";"K329";"0.0";"5000000000";"1800000000";"500000000";"24";; -"30";"K346";"0.0";"3000000000";"300000000";;"24";"2.5"; -"31";"L005";"C2";"1000000000";"5000000";"3000000000";"16";; -"32";"L477";"C3";"2000000000";"300000000";;"16";"5"; -"33";"L614";"C5";"1000000000";"20000000";;"24";"2.5"; -"35";"M267";"C3";"1000000000";"300000000";;"16";"1.25"; -"36";"M555";"C5";"3000000000";"1000000000";;"24";"2.5"; -"37";"M609";"C4";"1000000000";"20000000";;"16";"4"; -"38";"N062";"C5";"3000000000";"300000000";;"24";"2.5"; -"39";"N110";"H";"1000000000";"20000000";;"24";"10"; -"40";"N290";"L";"3000000000";"1350000000";"500000000";"24";; -"41";"N432";"C5";"3000000000";"1350000000";;"24";"10"; -"42";"N766";"C2";"2000000000";"300000000";;"16";"4"; -"43";"N770";"C5";"3000000000";"300000000";;"24";"2.5"; -"44";"N944";"L";"3000000000";"1350000000";;"24";"10"; -"45";"N968";"C3";"2000000000";"300000000";;"16";"10"; -"46";"O128";"C4";"1000000000";"300000000";"100000000";"24";; -"47";"O477";"C3";"2000000000";"300000000";;"16";"5"; -"48";"O883";"C3";"1000000000";"20000000";;"16";"5"; -"49";"P060";"C1";"500000000";"20000000";;"16";"5"; -"50";"Q003";"0.0";"1000000000";"5000000";"3000000000";"16";; -"51";"Q317";"C1";"500000000";"20000000";;"16";"2.5"; -"52";"R051";"L";"3000000000";"1000000000";;"16";"5"; -"53";"R474";"C6";"3000000000";"300000000";;"24";"2.22"; -"54";"R943";"C2";"750000000";"300000000";;"16";"6.67"; -"55";"S047";"H";"3000000000";"300000000";;"24";; -"56";"S199";"0.0";"3000000000";"1350000000";;"24";"10"; -"57";"S804";"C6";"1000000000";"20000000";;"24";"1.25"; -"58";"T405";"C4";"2000000000";"300000000";;"16";"6.67"; -"59";"U210";"L";"3000000000";"300000000";;"24";"10"; -"60";"U319";"C6";"3000000000";"1350000000";"500000000";"48";; -"61";"U574";"C6";"3000000000";"300000000";;"24";"1.25"; -"62";"V283";"0.0";"3000000000";"1000000000";;"24";"2.5"; -"63";"V301";"C1";"500000000";"20000000";;"16";"5"; -"64";"V753";"C6";"3000000000";"1350000000";;"24";"6.67"; -"65";"V911";"C5";"3000000000";"1350000000";;"24";"10"; -"66";"W237";"C6";"3000000000";"1350000000";;"24";"6.67"; -"67";"X702";"C3";"1000000000";"300000000";;"24";"10"; -"68";"X877";"C4";"2000000000";"300000000";;"16";"6.67"; -"69";"Y683";"C4";"2000000000";"300000000";;"16";"4"; -"70";"Y790";"C1";"500000000";"20000000";;"16";"2.5"; -"71";"Z006";"C3";"1000000000";"5000000";"3000000000";"16";; -"72";"Z060";"0.0";"1000000000";"20000000";;"24";"2.5"; -"73";"Z142";"0.0";"3000000000";"1350000000";;"24";"10"; -"74";"Z457";"C4";"2000000000";"300000000";;"16";"4"; -"75";"Z647";"C1";"500000000";"20000000";;"16";"10"; -"76";"Z971";"C1";"100000000";"20000000";;"16";"10"; -"80";"M001";"C4";"1000000000";"5000000";"3000000000";"16";; -"81";"E587";"0.0";"3000000000";"1000000000";;"16";; -"82";"V898";"L";"2000000000";"300000000";;"16";; -"83";"Q063";"H";"500000000";"20000000";;"16";; -"84";"G008";"C6";"1000000000";"5000000";"3000000000";"16";; -"85";"F353";"C12";"100000000";"20000000";;"16";; -"86";"F135";"C12";"750000000";"300000000";;"16";; -"87";"T458";"C12";"500000000";"20000000";;"16";; -"88";"M164";"C12";"2000000000";"300000000";;"16";; -"89";"L031";"C12";"3000000000";"1000000000";;"16";; -"90";"S877";"C14";"750000000";"300000000";;"16";; -"91";"B735";"C15";"750000000";"300000000";;"16";; -"92";"V928";"C16";"750000000";"300000000";;"16";; -"93";"C414";"C17";"750000000";"300000000";;"16";; -"94";"R259";"C18";"750000000";"300000000";;"16";; \ No newline at end of file +Id;Name;scanWormholeStrength +1;A009; +2;A239;5 +3;A641;10 +4;A982;2.22 +5;B041; +6;B274;10 +7;B449;2.5 +8;B520; +9;C008; +10;C125;6.67 +11;C140;5 +12;C247;10 +13;C248; +14;C391; +15;D364;1.25 +16;D382;6.67 +17;D792;2.5 +18;D845;5 +19;E004; +20;E175;5 +21;E545;2.5 +23;G024;1.25 +24;H121;10 +25;H296;10 +26;H900;2.5 +27;I182;4 +28;J244;5 +29;K329; +30;K346;2.5 +31;L005; +32;L477;5 +33;L614;2.5 +35;M267;1.25 +36;M555;2.5 +37;M609;4 +38;N062;2.5 +39;N110;10 +40;N290; +41;N432;10 +42;N766;4 +43;N770;2.5 +44;N944;10 +45;N968;10 +46;O128; +47;O477;5 +48;O883;5 +49;P060;5 +50;Q003; +51;Q317;2.5 +52;R051;5 +53;R474;2.22 +54;R943;6.67 +55;S047; +56;S199;10 +57;S804;1.25 +58;T405;6.67 +59;U210;10 +60;U319; +61;U574;1.25 +62;V283;2.5 +63;V301;5 +64;V753;6.67 +65;V911;10 +66;W237;6.67 +67;X702;10 +68;X877;6.67 +69;Y683;4 +70;Y790;2.5 +71;Z006; +72;Z060;2.5 +73;Z142;10 +74;Z457;4 +75;Z647;10 +76;Z971;10 +80;M001; +81;E587; +82;V898; +83;Q063; +84;G008; +85;F353; +86;F135; +87;T458; +88;M164; +89;L031; +90;S877; +91;B735; +92;V928; +93;C414; +94;R259; \ No newline at end of file diff --git a/export/sql/eve_universe.sql.zip b/export/sql/eve_universe.sql.zip index f88481dc..ba4d3bb7 100644 Binary files a/export/sql/eve_universe.sql.zip and b/export/sql/eve_universe.sql.zip differ diff --git a/js/app.js b/js/app.js index ec82a8b4..12379702 100644 --- a/js/app.js +++ b/js/app.js @@ -27,7 +27,7 @@ requirejs.config({ admin: './app/admin', // initial start "admin page" view notification: './app/notification', // "notification" view - jquery: 'lib/jquery-3.3.1.min', // v3.3.1 jQuery + jquery: 'lib/jquery-3.4.1.min', // v3.4.1 jQuery bootstrap: 'lib/bootstrap.min', // v3.3.0 Bootstrap js code - http://getbootstrap.com/javascript text: 'lib/requirejs/text', // v2.0.12 A RequireJS/AMD loader plugin for loading text resources. mustache: 'lib/mustache.min', // v3.0.1 Javascript template engine - http://mustache.github.io @@ -42,11 +42,11 @@ requirejs.config({ xEditable: 'lib/bootstrap-editable.min', // v1.5.1 X-editable - in placed editing morris: 'lib/morris.min', // v0.5.1 Morris.js - graphs and charts raphael: 'lib/raphael.min', // v2.2.8 Raphaël - required for morris - https://dmitrybaranovskiy.github.io/raphael - bootbox: 'lib/bootbox.min', // v4.4.0 Bootbox.js - custom dialogs - http://bootboxjs.com + bootbox: 'lib/bootbox.min', // v5.2.0 Bootbox.js - custom dialogs - http://bootboxjs.com easyPieChart: 'lib/jquery.easypiechart.min', // v2.1.6 Easy Pie Chart - HTML 5 pie charts - http://rendro.github.io/easy-pie-chart peityInlineChart: 'lib/jquery.peity.min', // v3.2.1 Inline Chart - http://benpickles.github.io/peity/ dragToSelect: 'lib/jquery.dragToSelect', // v1.1 Drag to Select - http://andreaslagerkvist.com/jquery/drag-to-select - hoverIntent: 'lib/jquery.hoverIntent.min', // v1.9.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html + hoverIntent: 'lib/jquery.hoverIntent.min', // v1.10.0 Hover intention - http://cherne.net/brian/resources/jquery.hoverIntent.html select2: 'lib/select2.min', // v4.0.3 Drop Down customization - https://select2.github.io validator: 'lib/validator.min', // v0.10.1 Validator for Bootstrap 3 - https://github.com/1000hz/bootstrap-validator lazylinepainter: 'lib/jquery.lazylinepainter-1.5.1.min', // v1.5.1 SVG line animation plugin - http://lazylinepainter.info diff --git a/js/app/conf/signature_type.js b/js/app/conf/signature_type.js index 0e0727f5..108da68b 100644 --- a/js/app/conf/signature_type.js +++ b/js/app/conf/signature_type.js @@ -383,6 +383,51 @@ define(['jquery'], ($) => { 30: 'K346 - 0.0', 31: 'Z060 - 0.0' } + }, + 14: { // Drifter Sentinel WH + 1: { // Combat + 1: 'Monolith', + 2: 'Wormhole in Rock Circle', + 3: 'Opposing Spatial Rifts', + 4: 'Sleeper Enclave Debris', + 5: 'Crystal Resource' + } + }, + 15: { // Drifter Barbican WH + 1: { // Combat + 1: 'Wrecked Ships', + 2: 'Unstable Wormhole', + 3: 'Spatial Rift', + 4: 'Heavily Guarded Spatial Rift', + 5: 'Crystals' + } + }, + 16: { // Drifter Vidette WH + 1: { // Combat + 1: 'Ship Graveyard', + 2: 'Sleeper Engineering Station', + 3: 'Spatial Rift', + 4: 'Sleeper Enclave in Coral Rock', + 5: 'Crystals and Stone Circle' + } + }, + 17: { // Drifter Conflux WH + 1: { // Combat + 1: 'Monolith', + 2: 'Caged Wormhole', + 3: 'Rock Formation and Wormhole', + 4: 'Particle Acceleration Array', + 5: 'Guarded Asteroid Station' + } + }, + 18: { // Drifter Redoubt WH + 1: { // Combat + 1: 'Ship Graveyard', + 2: 'Caged Wormhole', + 3: 'Spatial Rift Generator', + 4: 'Sleeper Enclave', + 5: 'Hollow Asteroid' + } } }, // system type (k-space) 2: { diff --git a/js/app/conf/system_effect.js b/js/app/conf/system_effect.js index 44215ade..2fc3f8a1 100644 --- a/js/app/conf/system_effect.js +++ b/js/app/conf/system_effect.js @@ -4,730 +4,820 @@ */ -define([], function(){ +define([], () => { 'use strict'; - // system effects - let systemEffects = { - wh: { - magnetar: { - 1: [ - { - effect: 'Damage', - value: '+30%' - },{ - effect: 'Missile exp. radius', - value: '+15%' - },{ - effect: 'Drone tracking', - value: '-15%' - },{ - effect: 'Targeting range', - value: '-15%' - },{ - effect: 'Tracking speed', - value: '-15%' - },{ - effect: 'Target Painter strength', - value: '-15%' - } - ], - 2: [ - { - effect: 'Damage', - value: '+44%' - },{ - effect: 'Missile exp. radius', - value: '+22%' - },{ - effect: 'Drone tracking', - value: '-22%' - },{ - effect: 'Targeting range', - value: '-22%' - },{ - effect: 'Tracking speed', - value: '-22%' - },{ - effect: 'Target Painter strength', - value: '-22%' - } - ], - 3: [ - { - effect: 'Damage', - value: '+58%' - },{ - effect: 'Missile exp. radius', - value: '+29%' - },{ - effect: 'Drone tracking', - value: '-29%' - },{ - effect: 'Targeting range', - value: '-29%' - },{ - effect: 'Tracking speed', - value: '-29%' - },{ - effect: 'Target Painter strength', - value: '-29%' - } - ], - 4: [ - { - effect: 'Damage', - value: '+72%' - },{ - effect: 'Missile exp. radius', - value: '+36%' - },{ - effect: 'Drone tracking', - value: '-36%' - },{ - effect: 'Targeting range', - value: '-36%' - },{ - effect: 'Tracking speed', - value: '-36%' - },{ - effect: 'Target Painter strength', - value: '-36%' - } - ], - 5: [ - { - effect: 'Damage', - value: '+86%' - },{ - effect: 'Missile exp. radius', - value: '+43%' - },{ - effect: 'Drone tracking', - value: '-43%' - },{ - effect: 'Targeting range', - value: '-43%' - },{ - effect: 'Tracking speed', - value: '-43%' - },{ - effect: 'Target Painter strength', - value: '-43%' - } - ], - 6: [ - { - effect: 'Damage', - value: '+100%' - },{ - effect: 'Missile exp. radius', - value: '+50%' - },{ - effect: 'Drone tracking', - value: '-50%' - },{ - effect: 'Targeting range', - value: '-50%' - },{ - effect: 'Tracking speed', - value: '-50%' - },{ - effect: 'Target Painter strength', - value: '-50%' - } - ] - }, - redGiant: { - 1: [ - { - effect: 'Heat damage', - value: '+15%' - },{ - effect: 'Overload bonus', - value: '+30%' - },{ - effect: 'Smart Bomb range', - value: '+30%' - },{ - effect: 'Smart Bomb damage', - value: '+30%' - },{ - effect: 'Bomb damage', - value: '+30%' - } - ], - 2: [ - { - effect: 'Heat damage', - value: '+22%' - },{ - effect: 'Overload bonus', - value: '+44%' - },{ - effect: 'Smart Bomb range', - value: '+44%' - },{ - effect: 'Smart Bomb damage', - value: '+44%' - },{ - effect: 'Bomb damage', - value: '+44%' - } - ], - 3: [ - { - effect: 'Heat damage', - value: '+29%' - },{ - effect: 'Overload bonus', - value: '+58%' - },{ - effect: 'Smart Bomb range', - value: '+58%' - },{ - effect: 'Smart Bomb damage', - value: '+58%' - },{ - effect: 'Bomb damage', - value: '+58%' - } - ], - 4: [ - { - effect: 'Heat damage', - value: '+36%' - },{ - effect: 'Overload bonus', - value: '+72%' - },{ - effect: 'Smart Bomb range', - value: '+72%' - },{ - effect: 'Smart Bomb damage', - value: '+72%' - },{ - effect: 'Bomb damage', - value: '+72%' - } - ], - 5: [ - { - effect: 'Heat damage', - value: '+43%' - },{ - effect: 'Overload bonus', - value: '+86%' - },{ - effect: 'Smart Bomb range', - value: '+86%' - },{ - effect: 'Smart Bomb damage', - value: '+86%' - },{ - effect: 'Bomb damage', - value: '+86%' - } - ], - 6: [ - { - effect: 'Heat damage', - value: '+50%' - },{ - effect: 'Overload bonus', - value: '+100%' - },{ - effect: 'Smart Bomb range', - value: '+100%' - },{ - effect: 'Smart Bomb damage', - value: '+100%' - },{ - effect: 'Bomb damage', - value: '+100%' - } - ] - }, - pulsar: { - 1: [ - { - effect: 'Shield HP', - value: '+30%' - },{ - effect: 'Armor resist', - value: '-15%' - },{ - effect: 'Capacitor recharge', - value: '-15%' - },{ - effect: 'Signature', - value: '+30%' - },{ - effect: 'NOS/Neut drain', - value: '+30%' - } - ], - 2: [ - { - effect: 'Shield HP', - value: '+44%' - },{ - effect: 'Armor resist', - value: '-22%' - },{ - effect: 'Capacitor recharge', - value: '-22%' - },{ - effect: 'Signature', - value: '+44%' - },{ - effect: 'NOS/Neut drain', - value: '+44%' - } - ], - 3: [ - { - effect: 'Shield HP', - value: '+58%' - },{ - effect: 'Armor resist', - value: '-29%' - },{ - effect: 'Capacitor recharge', - value: '-29%' - },{ - effect: 'Signature', - value: '+58%' - },{ - effect: 'NOS/Neut drain', - value: '+58%' - } - ], - 4: [ - { - effect: 'Shield HP', - value: '+72%' - },{ - effect: 'Armor resist', - value: '-36%' - },{ - effect: 'Capacitor recharge', - value: '-36%' - },{ - effect: 'Signature', - value: '+72%' - },{ - effect: 'NOS/Neut drain', - value: '+72%' - } - ], - 5: [ - { - effect: 'Shield HP', - value: '+86%' - },{ - effect: 'Armor resist', - value: '-43%' - },{ - effect: 'Capacitor recharge', - value: '-43%' - },{ - effect: 'Signature', - value: '+86%' - },{ - effect: 'NOS/Neut drain', - value: '+86%' - } - ], - 6: [ - { - effect: 'Shield HP', - value: '+100%' - },{ - effect: 'Armor resist', - value: '-50%' - },{ - effect: 'Capacitor recharge', - value: '-50%' - },{ - effect: 'Signature', - value: '+100%' - },{ - effect: 'NOS/Neut drain', - value: '+100%' - } - ] - }, - wolfRayet: { - 1: [ - { - effect: 'Armor HP', - value: '+30%' - },{ - effect: 'Shield resist', - value: '-15%' - },{ - effect: 'Small Weapon damage', - value: '+60%' - },{ - effect: 'Signature size', - value: '-15%' - } - ], - 2: [ - { - effect: 'Armor HP', - value: '+44%' - },{ - effect: 'Shield resist', - value: '-22%' - },{ - effect: 'Small Weapon damage', - value: '+88%' - },{ - effect: 'Signature size', - value: '-22%' - } - ], - 3: [ - { - effect: 'Armor HP', - value: '+58%' - },{ - effect: 'Shield resist', - value: '-29%' - },{ - effect: 'Small Weapon damage', - value: '+116%' - },{ - effect: 'Signature size', - value: '-29%' - } - ], - 4: [ - { - effect: 'Armor HP', - value: '+72%' - },{ - effect: 'Shield resist', - value: '-36%' - },{ - effect: 'Small Weapon damage', - value: '+144%' - },{ - effect: 'Signature size', - value: '-36%' - } - ], - 5: [ - { - effect: 'Armor HP', - value: '+86%' - },{ - effect: 'Shield resist', - value: '-43%' - },{ - effect: 'Small Weapon damage', - value: '+172%' - },{ - effect: 'Signature size', - value: '-43%' - } - ], - 6: [ - { - effect: 'Armor HP', - value: '+100%' - },{ - effect: 'Shield resist', - value: '-50%' - },{ - effect: 'Small Weapon damage', - value: '+200%' - },{ - effect: 'Signature size', - value: '-50%' - } - ] - }, - cataclysmic: { - 1: [ - { - effect: 'Local armor repair amount', - value: '-15%' - },{ - effect: 'Local shield boost amount', - value: '-15%' - },{ - effect: 'Shield transfer amount', - value: '+30%' - },{ - effect: 'Remote repair amount', - value: '+30%' - },{ - effect: 'Capacitor capacity', - value: '+30%' - },{ - effect: 'Capacitor recharge time', - value: '+15%' - },{ - effect: 'Remote Capacitor Transmitter amount', - value: '-15%' - } - ], - 2: [ - { - effect: 'Local armor repair amount', - value: '-22%' - },{ - effect: 'Local shield boost amount', - value: '-22%' - },{ - effect: 'Shield transfer amount', - value: '+44%' - },{ - effect: 'Remote repair amount', - value: '+44%' - },{ - effect: 'Capacitor capacity', - value: '+44%' - },{ - effect: 'Capacitor recharge time', - value: '+22%' - },{ - effect: 'Remote Capacitor Transmitter amount', - value: '-22%' - } - ], - 3: [ - { - effect: 'Local armor repair amount', - value: '-29%' - },{ - effect: 'Local shield boost amount', - value: '-29%' - },{ - effect: 'Shield transfer amount', - value: '+58%' - },{ - effect: 'Remote repair amount', - value: '+58%' - },{ - effect: 'Capacitor capacity', - value: '+58%' - },{ - effect: 'Capacitor recharge time', - value: '+29%' - },{ - effect: 'Remote Capacitor Transmitter amount', - value: '-29%' - } - ], - 4: [ - { - effect: 'Local armor repair amount', - value: '-36%' - },{ - effect: 'Local shield boost amount', - value: '-36%' - },{ - effect: 'Shield transfer amount', - value: '+72%' - },{ - effect: 'Remote repair amount', - value: '+72%' - },{ - effect: 'Capacitor capacity', - value: '+72%' - },{ - effect: 'Capacitor recharge time', - value: '+36%' - },{ - effect: 'Remote Capacitor Transmitter amount', - value: '-36%' - } - ], - 5: [ - { - effect: 'Local armor repair amount', - value: '-43%' - },{ - effect: 'Local shield boost amount', - value: '-43%' - },{ - effect: 'Shield transfer amount', - value: '+86%' - },{ - effect: 'Remote repair amount', - value: '+86%' - },{ - effect: 'Capacitor capacity', - value: '+86%' - },{ - effect: 'Capacitor recharge time', - value: '+43%' - },{ - effect: 'Remote Capacitor Transmitter amount', - value: '-43%' - } - ], - 6: [ - { - effect: 'Local armor repair amount', - value: '-50%' - },{ - effect: 'Local shield boost amount', - value: '-50%' - },{ - effect: 'Shield transfer amount', - value: '+100%' - },{ - effect: 'Remote repair amount', - value: '+100%' - },{ - effect: 'Capacitor capacity', - value: '+100%' - },{ - effect: 'Capacitor recharge time', - value: '+50%' - },{ - effect: 'Remote Capacitor Transmitter amount', - value: '-50%' - } - ] - }, - blackHole: { - 1: [ - { - effect: 'Missile velocity', - value: '+15%' - },{ - effect: 'Missile exp. velocity', - value: '+30%' - },{ - effect: 'Ship velocity', - value: '+30%' - },{ - effect: 'Stasis Webifier strength', - value: '-15%' - },{ - effect: 'Inertia', - value: '+15%' - },{ - effect: 'Targeting range', - value: '+30%' - } - ], - 2: [ - { - effect: 'Missile velocity', - value: '+22%' - },{ - effect: 'Missile exp. velocity', - value: '+44%' - },{ - effect: 'Ship velocity', - value: '+44%' - },{ - effect: 'Stasis Webifier strength', - value: '-22%' - },{ - effect: 'Inertia', - value: '+22%' - },{ - effect: 'Targeting range', - value: '+44%' - } - ], - 3: [ - { - effect: 'Missile velocity', - value: '+29%' - },{ - effect: 'Missile exp. velocity', - value: '+58%' - },{ - effect: 'Ship velocity', - value: '+58%' - },{ - effect: 'Stasis Webifier strength', - value: '-29%' - },{ - effect: 'Inertia', - value: '+29%' - },{ - effect: 'Targeting range', - value: '+58%' - } - ], - 4: [ - { - effect: 'Missile velocity', - value: '+36%' - },{ - effect: 'Missile exp. velocity', - value: '+72%' - },{ - effect: 'Ship velocity', - value: '+72%' - },{ - effect: 'Stasis Webifier strength', - value: '-36%' - },{ - effect: 'Inertia', - value: '+36%' - },{ - effect: 'Targeting range', - value: '+72%' - } - ], - 5: [ - { - effect: 'Missile velocity', - value: '+43%' - },{ - effect: 'Missile exp. velocity', - value: '+86%' - },{ - effect: 'Ship velocity', - value: '+86%' - },{ - effect: 'Stasis Webifier strength', - value: '-43%' - },{ - effect: 'Inertia', - value: '+43%' - },{ - effect: 'Targeting range', - value: '+86%' - } - ], - 6: [ - { - effect: 'Missile velocity', - value: '+50%' - },{ - effect: 'Missile exp. velocity', - value: '+100%' - },{ - effect: 'Ship velocity', - value: '+100%' - },{ - effect: 'Stasis Webifier strength', - value: '-50%' - },{ - effect: 'Inertia', - value: '+50%' - },{ - effect: 'Targeting range', - value: '+100%' - } - ] - } + /** + * get system effect multiplier + * @param areaId + * @returns {number} + */ + let getMultiplierByAreaId = areaId => { + let multiply = 0; + switch(areaId){ + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + multiply = areaId; // C1-C6 holes + break; + case 13: + multiply = 6; // Shattered frigate holes + break; + case 14: + case 15: + case 16: + case 17: + case 18: + multiply = 2; // Drifter space + break; + } + + return multiply; + }; + + + let magnetar = { + 1: [ + { + effect: 'Damage', + value: '+30%' + }, { + effect: 'Missile exp. radius', + value: '+15%' + }, { + effect: 'Drone tracking', + value: '-15%' + }, { + effect: 'Targeting range', + value: '-15%' + }, { + effect: 'Tracking speed', + value: '-15%' + }, { + effect: 'Target Painter strength', + value: '-15%' } - }; + ], + 2: [ + { + effect: 'Damage', + value: '+44%' + }, { + effect: 'Missile exp. radius', + value: '+22%' + }, { + effect: 'Drone tracking', + value: '-22%' + }, { + effect: 'Targeting range', + value: '-22%' + }, { + effect: 'Tracking speed', + value: '-22%' + }, { + effect: 'Target Painter strength', + value: '-22%' + } + ], + 3: [ + { + effect: 'Damage', + value: '+58%' + }, { + effect: 'Missile exp. radius', + value: '+29%' + }, { + effect: 'Drone tracking', + value: '-29%' + }, { + effect: 'Targeting range', + value: '-29%' + }, { + effect: 'Tracking speed', + value: '-29%' + }, { + effect: 'Target Painter strength', + value: '-29%' + } + ], + 4: [ + { + effect: 'Damage', + value: '+72%' + }, { + effect: 'Missile exp. radius', + value: '+36%' + }, { + effect: 'Drone tracking', + value: '-36%' + }, { + effect: 'Targeting range', + value: '-36%' + }, { + effect: 'Tracking speed', + value: '-36%' + }, { + effect: 'Target Painter strength', + value: '-36%' + } + ], + 5: [ + { + effect: 'Damage', + value: '+86%' + }, { + effect: 'Missile exp. radius', + value: '+43%' + }, { + effect: 'Drone tracking', + value: '-43%' + }, { + effect: 'Targeting range', + value: '-43%' + }, { + effect: 'Tracking speed', + value: '-43%' + }, { + effect: 'Target Painter strength', + value: '-43%' + } + ], + 6: [ + { + effect: 'Damage', + value: '+100%' + }, { + effect: 'Missile exp. radius', + value: '+50%' + }, { + effect: 'Drone tracking', + value: '-50%' + }, { + effect: 'Targeting range', + value: '-50%' + }, { + effect: 'Tracking speed', + value: '-50%' + }, { + effect: 'Target Painter strength', + value: '-50%' + } + ] + }; + let redGiant = { + 1: [ + { + effect: 'Heat damage', + value: '+15%' + }, { + effect: 'Overload bonus', + value: '+30%' + }, { + effect: 'Smart Bomb range', + value: '+30%' + }, { + effect: 'Smart Bomb damage', + value: '+30%' + }, { + effect: 'Bomb damage', + value: '+30%' + } + ], + 2: [ + { + effect: 'Heat damage', + value: '+22%' + }, { + effect: 'Overload bonus', + value: '+44%' + }, { + effect: 'Smart Bomb range', + value: '+44%' + }, { + effect: 'Smart Bomb damage', + value: '+44%' + }, { + effect: 'Bomb damage', + value: '+44%' + } + ], + 3: [ + { + effect: 'Heat damage', + value: '+29%' + }, { + effect: 'Overload bonus', + value: '+58%' + }, { + effect: 'Smart Bomb range', + value: '+58%' + }, { + effect: 'Smart Bomb damage', + value: '+58%' + }, { + effect: 'Bomb damage', + value: '+58%' + } + ], + 4: [ + { + effect: 'Heat damage', + value: '+36%' + }, { + effect: 'Overload bonus', + value: '+72%' + }, { + effect: 'Smart Bomb range', + value: '+72%' + }, { + effect: 'Smart Bomb damage', + value: '+72%' + }, { + effect: 'Bomb damage', + value: '+72%' + } + ], + 5: [ + { + effect: 'Heat damage', + value: '+43%' + }, { + effect: 'Overload bonus', + value: '+86%' + }, { + effect: 'Smart Bomb range', + value: '+86%' + }, { + effect: 'Smart Bomb damage', + value: '+86%' + }, { + effect: 'Bomb damage', + value: '+86%' + } + ], + 6: [ + { + effect: 'Heat damage', + value: '+50%' + }, { + effect: 'Overload bonus', + value: '+100%' + }, { + effect: 'Smart Bomb range', + value: '+100%' + }, { + effect: 'Smart Bomb damage', + value: '+100%' + }, { + effect: 'Bomb damage', + value: '+100%' + } + ] + }; - return systemEffects; + let pulsar = { + 1: [ + { + effect: 'Shield HP', + value: '+30%' + }, { + effect: 'Armor resist', + value: '-15%' + }, { + effect: 'Capacitor recharge', + value: '-15%' + }, { + effect: 'Signature', + value: '+30%' + }, { + effect: 'NOS/Neut drain', + value: '+30%' + } + ], + 2: [ + { + effect: 'Shield HP', + value: '+44%' + }, { + effect: 'Armor resist', + value: '-22%' + }, { + effect: 'Capacitor recharge', + value: '-22%' + }, { + effect: 'Signature', + value: '+44%' + }, { + effect: 'NOS/Neut drain', + value: '+44%' + } + ], + 3: [ + { + effect: 'Shield HP', + value: '+58%' + }, { + effect: 'Armor resist', + value: '-29%' + }, { + effect: 'Capacitor recharge', + value: '-29%' + }, { + effect: 'Signature', + value: '+58%' + }, { + effect: 'NOS/Neut drain', + value: '+58%' + } + ], + 4: [ + { + effect: 'Shield HP', + value: '+72%' + }, { + effect: 'Armor resist', + value: '-36%' + }, { + effect: 'Capacitor recharge', + value: '-36%' + }, { + effect: 'Signature', + value: '+72%' + }, { + effect: 'NOS/Neut drain', + value: '+72%' + } + ], + 5: [ + { + effect: 'Shield HP', + value: '+86%' + }, { + effect: 'Armor resist', + value: '-43%' + }, { + effect: 'Capacitor recharge', + value: '-43%' + }, { + effect: 'Signature', + value: '+86%' + }, { + effect: 'NOS/Neut drain', + value: '+86%' + } + ], + 6: [ + { + effect: 'Shield HP', + value: '+100%' + }, { + effect: 'Armor resist', + value: '-50%' + }, { + effect: 'Capacitor recharge', + value: '-50%' + }, { + effect: 'Signature', + value: '+100%' + }, { + effect: 'NOS/Neut drain', + value: '+100%' + } + ] + }; + + let wolfRayet = { + 1: [ + { + effect: 'Armor HP', + value: '+30%' + }, { + effect: 'Shield resist', + value: '-15%' + }, { + effect: 'Small Weapon damage', + value: '+60%' + }, { + effect: 'Signature size', + value: '-15%' + } + ], + 2: [ + { + effect: 'Armor HP', + value: '+44%' + }, { + effect: 'Shield resist', + value: '-22%' + }, { + effect: 'Small Weapon damage', + value: '+88%' + }, { + effect: 'Signature size', + value: '-22%' + } + ], + 3: [ + { + effect: 'Armor HP', + value: '+58%' + }, { + effect: 'Shield resist', + value: '-29%' + }, { + effect: 'Small Weapon damage', + value: '+116%' + }, { + effect: 'Signature size', + value: '-29%' + } + ], + 4: [ + { + effect: 'Armor HP', + value: '+72%' + }, { + effect: 'Shield resist', + value: '-36%' + }, { + effect: 'Small Weapon damage', + value: '+144%' + }, { + effect: 'Signature size', + value: '-36%' + } + ], + 5: [ + { + effect: 'Armor HP', + value: '+86%' + }, { + effect: 'Shield resist', + value: '-43%' + }, { + effect: 'Small Weapon damage', + value: '+172%' + }, { + effect: 'Signature size', + value: '-43%' + } + ], + 6: [ + { + effect: 'Armor HP', + value: '+100%' + }, { + effect: 'Shield resist', + value: '-50%' + }, { + effect: 'Small Weapon damage', + value: '+200%' + }, { + effect: 'Signature size', + value: '-50%' + } + ] + }; + + let cataclysmic = { + 1: [ + { + effect: 'Local armor repair amount', + value: '-15%' + }, { + effect: 'Local shield boost amount', + value: '-15%' + }, { + effect: 'Shield transfer amount', + value: '+30%' + }, { + effect: 'Remote repair amount', + value: '+30%' + }, { + effect: 'Capacitor capacity', + value: '+30%' + }, { + effect: 'Capacitor recharge time', + value: '+15%' + }, { + effect: 'Remote Capacitor Transmitter amount', + value: '-15%' + } + ], + 2: [ + { + effect: 'Local armor repair amount', + value: '-22%' + }, { + effect: 'Local shield boost amount', + value: '-22%' + }, { + effect: 'Shield transfer amount', + value: '+44%' + }, { + effect: 'Remote repair amount', + value: '+44%' + }, { + effect: 'Capacitor capacity', + value: '+44%' + }, { + effect: 'Capacitor recharge time', + value: '+22%' + }, { + effect: 'Remote Capacitor Transmitter amount', + value: '-22%' + } + ], + 3: [ + { + effect: 'Local armor repair amount', + value: '-29%' + }, { + effect: 'Local shield boost amount', + value: '-29%' + }, { + effect: 'Shield transfer amount', + value: '+58%' + }, { + effect: 'Remote repair amount', + value: '+58%' + }, { + effect: 'Capacitor capacity', + value: '+58%' + }, { + effect: 'Capacitor recharge time', + value: '+29%' + }, { + effect: 'Remote Capacitor Transmitter amount', + value: '-29%' + } + ], + 4: [ + { + effect: 'Local armor repair amount', + value: '-36%' + }, { + effect: 'Local shield boost amount', + value: '-36%' + }, { + effect: 'Shield transfer amount', + value: '+72%' + }, { + effect: 'Remote repair amount', + value: '+72%' + }, { + effect: 'Capacitor capacity', + value: '+72%' + }, { + effect: 'Capacitor recharge time', + value: '+36%' + }, { + effect: 'Remote Capacitor Transmitter amount', + value: '-36%' + } + ], + 5: [ + { + effect: 'Local armor repair amount', + value: '-43%' + }, { + effect: 'Local shield boost amount', + value: '-43%' + }, { + effect: 'Shield transfer amount', + value: '+86%' + }, { + effect: 'Remote repair amount', + value: '+86%' + }, { + effect: 'Capacitor capacity', + value: '+86%' + }, { + effect: 'Capacitor recharge time', + value: '+43%' + }, { + effect: 'Remote Capacitor Transmitter amount', + value: '-43%' + } + ], + 6: [ + { + effect: 'Local armor repair amount', + value: '-50%' + }, { + effect: 'Local shield boost amount', + value: '-50%' + }, { + effect: 'Shield transfer amount', + value: '+100%' + }, { + effect: 'Remote repair amount', + value: '+100%' + }, { + effect: 'Capacitor capacity', + value: '+100%' + }, { + effect: 'Capacitor recharge time', + value: '+50%' + }, { + effect: 'Remote Capacitor Transmitter amount', + value: '-50%' + } + ] + }; + + let blackHole = { + 1: [ + { + effect: 'Missile velocity', + value: '+15%' + }, { + effect: 'Missile exp. velocity', + value: '+30%' + }, { + effect: 'Ship velocity', + value: '+30%' + }, { + effect: 'Stasis Webifier strength', + value: '-15%' + }, { + effect: 'Inertia', + value: '+15%' + }, { + effect: 'Targeting range', + value: '+30%' + } + ], + 2: [ + { + effect: 'Missile velocity', + value: '+22%' + }, { + effect: 'Missile exp. velocity', + value: '+44%' + }, { + effect: 'Ship velocity', + value: '+44%' + }, { + effect: 'Stasis Webifier strength', + value: '-22%' + }, { + effect: 'Inertia', + value: '+22%' + }, { + effect: 'Targeting range', + value: '+44%' + } + ], + 3: [ + { + effect: 'Missile velocity', + value: '+29%' + }, { + effect: 'Missile exp. velocity', + value: '+58%' + }, { + effect: 'Ship velocity', + value: '+58%' + }, { + effect: 'Stasis Webifier strength', + value: '-29%' + }, { + effect: 'Inertia', + value: '+29%' + }, { + effect: 'Targeting range', + value: '+58%' + } + ], + 4: [ + { + effect: 'Missile velocity', + value: '+36%' + }, { + effect: 'Missile exp. velocity', + value: '+72%' + }, { + effect: 'Ship velocity', + value: '+72%' + }, { + effect: 'Stasis Webifier strength', + value: '-36%' + }, { + effect: 'Inertia', + value: '+36%' + }, { + effect: 'Targeting range', + value: '+72%' + } + ], + 5: [ + { + effect: 'Missile velocity', + value: '+43%' + }, { + effect: 'Missile exp. velocity', + value: '+86%' + }, { + effect: 'Ship velocity', + value: '+86%' + }, { + effect: 'Stasis Webifier strength', + value: '-43%' + }, { + effect: 'Inertia', + value: '+43%' + }, { + effect: 'Targeting range', + value: '+86%' + } + ], + 6: [ + { + effect: 'Missile velocity', + value: '+50%' + }, { + effect: 'Missile exp. velocity', + value: '+100%' + }, { + effect: 'Ship velocity', + value: '+100%' + }, { + effect: 'Stasis Webifier strength', + value: '-50%' + }, { + effect: 'Inertia', + value: '+50%' + }, { + effect: 'Targeting range', + value: '+100%' + } + ] + }; + + // system effects + return { + getMultiplierByAreaId: getMultiplierByAreaId, + wh: { + magnetar: { + 1: magnetar[getMultiplierByAreaId(1)], + 2: magnetar[getMultiplierByAreaId(2)], + 3: magnetar[getMultiplierByAreaId(3)], + 4: magnetar[getMultiplierByAreaId(4)], + 5: magnetar[getMultiplierByAreaId(5)], + 6: magnetar[getMultiplierByAreaId(6)], + 16: magnetar[getMultiplierByAreaId(16)] + }, + redGiant: { + 1: redGiant[getMultiplierByAreaId(1)], + 2: redGiant[getMultiplierByAreaId(2)], + 3: redGiant[getMultiplierByAreaId(3)], + 4: redGiant[getMultiplierByAreaId(4)], + 5: redGiant[getMultiplierByAreaId(5)], + 6: redGiant[getMultiplierByAreaId(6)], + 14: redGiant[getMultiplierByAreaId(14)] + }, + pulsar: { + 1: pulsar[getMultiplierByAreaId(1)], + 2: pulsar[getMultiplierByAreaId(2)], + 3: pulsar[getMultiplierByAreaId(3)], + 4: pulsar[getMultiplierByAreaId(4)], + 5: pulsar[getMultiplierByAreaId(5)], + 6: pulsar[getMultiplierByAreaId(6)], + 17: pulsar[getMultiplierByAreaId(17)] + }, + wolfRayet: { + 1: wolfRayet[getMultiplierByAreaId(1)], + 2: wolfRayet[getMultiplierByAreaId(2)], + 3: wolfRayet[getMultiplierByAreaId(3)], + 4: wolfRayet[getMultiplierByAreaId(4)], + 5: wolfRayet[getMultiplierByAreaId(5)], + 6: wolfRayet[getMultiplierByAreaId(6)], + 13: wolfRayet[getMultiplierByAreaId(13)], + 18: wolfRayet[getMultiplierByAreaId(18)] + }, + cataclysmic: { + 1: cataclysmic[getMultiplierByAreaId(1)], + 2: cataclysmic[getMultiplierByAreaId(2)], + 3: cataclysmic[getMultiplierByAreaId(3)], + 4: cataclysmic[getMultiplierByAreaId(4)], + 5: cataclysmic[getMultiplierByAreaId(5)], + 6: cataclysmic[getMultiplierByAreaId(6)], + 15: cataclysmic[getMultiplierByAreaId(15)] + }, + blackHole: { + 1: blackHole[getMultiplierByAreaId(1)], + 2: blackHole[getMultiplierByAreaId(2)], + 3: blackHole[getMultiplierByAreaId(3)], + 4: blackHole[getMultiplierByAreaId(4)], + 5: blackHole[getMultiplierByAreaId(5)], + 6: blackHole[getMultiplierByAreaId(6)] + } + } + }; }); \ No newline at end of file diff --git a/js/app/counter.js b/js/app/counter.js index 134d6abc..672893ae 100644 --- a/js/app/counter.js +++ b/js/app/counter.js @@ -137,7 +137,8 @@ define([ // mark as init tableElement.attr('data-counter', 'init'); - let refreshIntervalId = window.setInterval(() => { + + let updateTableCount = () => { tableApi.cells(null, columnSelector).every(function(rowIndex, colIndex, tableLoopCount, cellLoopCount){ let cell = this; let node = cell.node(); @@ -148,7 +149,9 @@ define([ updateDateDiff( cell.nodes().to$(), date, round); } }); - }, 500); + }; + + let refreshIntervalId = window.setInterval(updateTableCount, 500); tableElement.data('interval', refreshIntervalId); }; diff --git a/js/app/datatables.loader.js b/js/app/datatables.loader.js index 38751434..b702148f 100644 --- a/js/app/datatables.loader.js +++ b/js/app/datatables.loader.js @@ -45,6 +45,7 @@ define([ table.destroyTimestampCounter(true); }); + // Status Plugin ============================================================================================== let StatusTable = function(settings){ let me = this; me.statusContainer = $('
';
+ value = '