From faa7988bc42e17ddfe0b22debeb3ae8517b1e9e8 Mon Sep 17 00:00:00 2001 From: tomaszrudowski <tomasz@innit.no> Date: Wed, 20 Oct 2021 15:23:04 +0200 Subject: [PATCH] Rework --- app/Election.php | 4 + app/ExtensionDelegationElection.php | 4 +- app/Http/Controllers/APIController.php | 341 ++++- app/Http/Controllers/HomeController.php | 5 + app/Population.php | 59 +- ..._02_18_093626_create_populations_table.php | 7 + .../2021_02_18_093642_create_voters_table.php | 1 + ...e_extension_delegation_elections_table.php | 3 +- ..._161037_add_reputation_to_voters_table.php | 32 - public/js/app.js | 1290 +++++++++++++---- resources/js/app.js | 1 + resources/js/components/population-create.vue | 4 +- resources/js/components/population-index.vue | 71 +- resources/js/components/population-show.vue | 139 +- .../components/population-template-show.vue | 187 +++ .../population-template-details.blade.php | 5 + routes/api_internal.php | 40 +- routes/web.php | 4 +- 18 files changed, 1758 insertions(+), 439 deletions(-) delete mode 100644 database/migrations/2021_03_24_161037_add_reputation_to_voters_table.php create mode 100644 resources/js/components/population-template-show.vue create mode 100644 resources/views/population-template-details.blade.php diff --git a/app/Election.php b/app/Election.php index 0fcc117..0032ebd 100644 --- a/app/Election.php +++ b/app/Election.php @@ -10,4 +10,8 @@ class Election extends Model 'population_id', 'type' ]; + + public function extension() { + return $this->hasOne(ExtensionDelegationElection::class, 'election_id', 'id'); + } } diff --git a/app/ExtensionDelegationElection.php b/app/ExtensionDelegationElection.php index 45d8941..5947ff0 100644 --- a/app/ExtensionDelegationElection.php +++ b/app/ExtensionDelegationElection.php @@ -14,6 +14,8 @@ class ExtensionDelegationElection extends Model 'initial_incorrect', 'as_delegate', 'as_follower', - 'as_independent' + 'as_independent', + 'weight_a', + 'weight_b' ]; } diff --git a/app/Http/Controllers/APIController.php b/app/Http/Controllers/APIController.php index 1e5d550..016877f 100644 --- a/app/Http/Controllers/APIController.php +++ b/app/Http/Controllers/APIController.php @@ -5,7 +5,6 @@ namespace App\Http\Controllers; use App\DelegationOneVote; use App\Election; use App\ExtensionDelegationElection; -use App\MajorityElection; use App\MajorityVote; use App\Population; use App\Voter; @@ -16,7 +15,7 @@ use Illuminate\Validation\ValidationException; class APIController extends Controller { - public function generatePopulation(Request $request) + public function generatePopulationTemplate(Request $request) { try { $attributes = $request->validate([ @@ -50,7 +49,7 @@ class APIController extends Controller $population = new Population(); $population->save(); - $population->name = isset($attributes['name']) ? $attributes['name'] : 'Population ' . $population->id; + $population->name = isset($attributes['name']) ? $attributes['name'] : 'Population Template ' . $population->id; $population->update(); $minValue = 1; @@ -178,23 +177,92 @@ class APIController extends Controller return response()->json($data, Response::HTTP_OK); } - public function getPopulations(Request $request) - { + public function getChildPopulations(Population $population) { $data = Population::with('voters', 'elections') - ->orderBy('id', 'desc') + ->where('parent_id', '=', $population->id) + ->orderBy('id', 'asc') ->get() ->makeHidden(['elections', 'voters']); $data->each(function($item) { - $item->append(['elections_stats', 'voters_stats']); + $item->append(['elections_stats', 'voters_stats']); }); return response()->json($data, Response::HTTP_OK); } - public function getPopulation($population, Request $request) + public function generateChildPopulation(Population $template, Request $request) { + try { + $attributes = $request->validate([ + 'election_type' => 'required|string|in:m,d1,d2,d3' + ]); + } catch (ValidationException $e) { + return response()->json([ + 'message' => 'Incorrect payload', + 'val_errors' => $e->errors() + ], Response::HTTP_UNPROCESSABLE_ENTITY); + } + $nextChildId = $template->childPopulations()->where('election_type', '=', $attributes['election_type'])->count() + 1; + + $newPopulation = new Population(); + if (!isset($template->parent_id)) { + $newPopulationName = "[". $template->name . "].(" . $attributes['election_type'] . "-" . $nextChildId . ")"; + } else { + $newPopulationName = $template->name . ".(" . $attributes['election_type'] . "-" . $nextChildId . ")"; + } + $newPopulation->election_type = $attributes['election_type']; + $newPopulation->name = $newPopulationName; + $newPopulation->parent_id = $template->id; + $newPopulation->stage = $newPopulation->election_type === "d1" ? 'p' : 'l'; // 'performance'/'learning' default stage for child population + $newPopulation->save(); + + foreach ($template->voters as $parentPopulationVoter) { + Voter::create([ + 'population_id' => $newPopulation->id, + 'expertise' => $parentPopulationVoter->expertise, + 'confidence' => $parentPopulationVoter->confidence, + 'following' => $parentPopulationVoter->following, + 'leadership' => $parentPopulationVoter->leadership, + 'reputation' => $parentPopulationVoter->reputation, + 'group' => $parentPopulationVoter->group + ]); + } + } + + public function getPopulationTemplates(Request $request) + { + $data = Population::with('voters', 'elections', 'childPopulations') + ->whereNull('parent_id') + ->orderBy('id', 'desc') + ->get() + ->makeHidden(['elections', 'voters', 'childPopulations']); + + $data->each(function($item) { + $item->append(['elections_stats', 'voters_stats', 'children_count', 'child_populations_stats']); + }); + + return response()->json($data, Response::HTTP_OK); + } + + public function getPopulationTemplate(int $template, Request $request) { + $data = Population::where('id', '=', $template) + ->whereNull('parent_id') + ->with('elections', 'voters', 'childPopulations') + ->firstOrFail() + ->append(['elections_stats', 'voters_stats', 'children_count', 'child_populations_stats']) + ->makeHidden('elections', 'voters'); + + $data->childPopulations->each(function($item) { + $item->append(['elections_stats']); + }); + + return response()->json($data, Response::HTTP_OK); + } + + public function getPopulation(int $template, int $population, Request $request) { $data = Population::where('id', '=', $population) + ->where('parent_id', '=', $template) ->with('elections', 'voters') ->firstOrFail() ->append(['elections_stats', 'voters_stats']) @@ -203,7 +271,22 @@ class APIController extends Controller return response()->json($data, Response::HTTP_OK); } - public function getMajorityElectionsDistribution($population, Request $request) + public function changeToPerformanceStage(int $template, int $population, Request $request) + { + $childPopulation = Population::where('id', '=', $population) + ->where('parent_id', '=', $template) + ->firstOrFail(); + + $childPopulation->stage = 'p'; + + if ($childPopulation->save()) { + return response()->json(null, Response::HTTP_NO_CONTENT); + } + + return response()->json(['error' => 'Stage not saved'], Response::HTTP_INTERNAL_SERVER_ERROR); + } + + public function getMajorityElectionsDistribution(int $template, int $population, Request $request) { $metadata = new \stdClass(); $startTime = microtime(true); @@ -245,7 +328,7 @@ class APIController extends Controller ], Response::HTTP_OK); } - public function getVoters($population, Request $request) + public function getVoters(int $template, int $population, Request $request) { $data = Population::where('id', '=', $population) ->with('voters') @@ -262,7 +345,7 @@ class APIController extends Controller * @return \Illuminate\Http\JsonResponse * @throws \Exception */ - public function runMajorityElections($population, Request $request) { + public function runMajorityElections(int $template, int $population, Request $request) { $data = new \stdClass(); $data->elections_stats = array(); @@ -348,7 +431,7 @@ class APIController extends Controller return $electionStats; } - public function runElections(int $population, Request $request) { + public function runElections(int $template, int $population, Request $request) { $data = new \stdClass(); $data->elections_stats = array(); @@ -376,6 +459,7 @@ class APIController extends Controller } $existingPopulation = Population::where('id', '=', $population) + ->where('parent_id', '=', $template) ->with('voters') ->firstOrFail(); @@ -404,6 +488,42 @@ class APIController extends Controller return response()->json($data, Response::HTTP_OK); } + /** + * Performance stage + * No changes to voters' attributes + * + * @param Population $population + * @return \stdClass + * @throws \Exception + */ + private function runSingleDelegationElectionVersion1(Population $population) { + return $this->runSingleDelegationElection($population, false, 'd1', false); + } + + /** + * Learning stage. + * Modify Reputation only after each election + * + * @param Population $population + * @return \stdClass + * @throws \Exception + */ + private function runSingleDelegationElectionVersion2 (Population $population) { + return $this->runSingleDelegationElection($population, true, 'd2', false); + } + + /** + * Learning stage. + * Modify Reputation, Following and Leadership after each election + * + * @param Population $population + * @return \stdClass + * @throws \Exception + */ + private function runSingleDelegationElectionVersion3 (Population $population) { + return $this->runSingleDelegationElection($population, true, 'd3', true); + } + /* private function runSingleDelegationElectionVersion1(Population $population) { $startTime = microtime(true); $election = Election::create([ @@ -561,11 +681,20 @@ class APIController extends Controller return $electionStats; } - - private function runSingleDelegationElectionVersion2( + */ + /** + * Run delegation election, default in performance stage (d1) + * @param Population $population + * @param bool $modifyReputation + * @param string $type + * @param bool $modifyAttributes + * @return \stdClass + * @throws \Exception + */ + private function runSingleDelegationElection( Population $population, - $useReputationBalance = true, - $type = 'd2', + $modifyReputation = false, + $type = 'd1', $modifyAttributes = false ) { $startTime = microtime(true); @@ -596,10 +725,19 @@ class APIController extends Controller $followersVotes = []; + $votingWeightA = 0; + $votingWeightB = 0; + foreach ($population->voters as $voter) { + $voterWeight = $voter->reputation > 0 ? $voter->reputation : 1; + if ($voter->group == "A") { + $votingWeightA += $voterWeight; + } else { + $votingWeightB += $voterWeight; + } $tresholdFollowing = $voter->following; - $tresholdIndependent = 100; - $tresholdLeadership = 100 + $voter->leadership; + $tresholdIndependent = $tresholdFollowing + $voter->confidence; + $tresholdLeadership = $tresholdIndependent + $voter->leadership; $randomBehaviour = random_int(0, $tresholdLeadership); @@ -676,57 +814,59 @@ class APIController extends Controller } - // adjust attributes of all voters (skip leadership/following adjustments) - // todo: extend data table to store initial/current leadership/following if to be adjustable over time as reputation - foreach ($population->voters as $voter) { - $previousReputation = $voter->reputation; - $voterID = $voter->id; - if (isset($delegatesSavedVotes[$voterID])) { - // is delegate - $noOfFollowers = $followersDistribution[$delegateID]; - if ($noOfFollowers > 0) { - // save weight adjustments for delegate's vote - $delegatesSavedVotes[$voterID]->save(); - // adjust voter reputation - if ($delegatesSavedVotes[$voterID]->vote_final > 0) { - $voter->reputation += (2 * $noOfFollowers); - if($modifyAttributes && $voter->leadership < 100) - $voter->leadership++; + // Adjust attributes of all voters (skip leadership/following adjustments) + // if in learning stage + if ($population->stage === 'l') { + foreach ($population->voters as $voter) { + $previousReputation = $voter->reputation; + $voterID = $voter->id; + if (isset($delegatesSavedVotes[$voterID])) { + // is delegate + $noOfFollowers = $followersDistribution[$delegateID]; + if ($noOfFollowers > 0) { + // save weight adjustments for delegate's vote + $delegatesSavedVotes[$voterID]->save(); + // adjust voter reputation + if ($delegatesSavedVotes[$voterID]->vote_final > 0) { + $voter->reputation += (2 * $noOfFollowers); + if($modifyAttributes && $voter->leadership < 100) + $voter->leadership++; + } else { + $voter->reputation -= (2 * $noOfFollowers); + if($modifyAttributes && $voter->leadership > 1) + $voter->leadership--; + } } else { - $voter->reputation -= (2 * $noOfFollowers); if($modifyAttributes && $voter->leadership > 1) $voter->leadership--; } - } else { - if($modifyAttributes && $voter->leadership > 1) - $voter->leadership--; - } - } elseif ($modifyAttributes && isset($followersVotes[$voterID])) { - // is follower - if ($followersVotes[$voterID]['vote_final']) { - if (!$followersVotes[$voterID]['vote_direct']) { - if($voter->following < 100) - $voter->following++; + } elseif ($modifyAttributes && isset($followersVotes[$voterID])) { + // is follower + if ($followersVotes[$voterID]['vote_final']) { + if (!$followersVotes[$voterID]['vote_direct']) { + if($voter->following < 100) + $voter->following++; + } + } elseif ($followersVotes[$voterID]['vote_direct']) { + if($voter->following > 1) + $voter->following--; } - } elseif ($followersVotes[$voterID]['vote_direct']) { - if($voter->following > 1) - $voter->following--; } - } - // balance voter reputation over time between elections (todo: move to another db call?) - if ($useReputationBalance) { + // balance voter reputation over time between elections if ($voter->reputation < 0) { $voter->reputation++; } elseif ($voter->reputation > 0) { $voter->reputation--; } - } - if ($previousReputation != $voter->reputation) { - $voter->save(); + + if ($previousReputation != $voter->reputation) { + $voter->save(); + } } } + $election->total_correct = $correctChoices; $election->total_incorrect = $incorrectChoices; @@ -751,7 +891,9 @@ class APIController extends Controller 'initial_incorrect' => $electionStats->initial_incorrect, 'as_delegate' => $asDelegate, 'as_follower' => $asFollower, - 'as_independent' => $asIndependent + 'as_independent' => $asIndependent, + 'weight_a' => $votingWeightA, + 'weight_b' => $votingWeightB ]); DelegationOneVote::insert($followersVotes); @@ -773,12 +915,7 @@ class APIController extends Controller return $electionStats; } - /** - * Modify Following and Leadership after each election - */ - private function runSingleDelegationElectionVersion3 (Population $population) { - return $this->runSingleDelegationElectionVersion2($population, true, 'd3', true); - } + private function findDelegateID(array $weightedDelegatesIDs, int $randomDelegation) { foreach ($weightedDelegatesIDs as $id => $cumulativeReputation) { @@ -789,7 +926,7 @@ class APIController extends Controller return null; } - public function getVoterStats($population, $voter) { + public function getVoterStats(int $template, int $population, $voter) { $data = Voter::where('id', '=', $voter) ->with('delegationOneVotes.parentDelegationOneVote') ->firstOrFail() @@ -799,7 +936,7 @@ class APIController extends Controller return response()->json($data,200); } - public function getVotersStats($population) { + public function getVotersStats(int $template, int $population) { $population = Population::where('id', '=', $population) ->with('voters.delegationOneVotes') @@ -810,7 +947,7 @@ class APIController extends Controller return response()->json($population->voters,200); } - public function getElectionsTimeline (Population $population, Request $request) { + public function getElectionsTimeline (int $template, Population $population, Request $request) { $data = new \stdClass(); try { @@ -861,4 +998,80 @@ class APIController extends Controller return response()->json($data, Response::HTTP_OK); } + public function getWeightsTimeline (int $template, Population $population, Request $request) { + $data = new \stdClass(); + + try { + $attributes = $request->validate([ + 'type' => 'required|string|in:d2,d3', + 'moving_average' => 'nullable|integer' + ]); + } catch (ValidationException $e) { + return response()->json(['error' => 'Unknown election type'], Response::HTTP_UNPROCESSABLE_ENTITY); + } + + $movingAverage = isset($attributes['moving_average']) ? $attributes['moving_average'] : 0; + + $data->elections_type = $attributes['type']; + $data->no_of_voters = $population->noOfVoters; + + if ($data->no_of_voters < 1) { + return response()->json(['error' => 'No voters in population'], Response::HTTP_NOT_FOUND); + } + + $countA = $population->voters()->where('group', '=', 'A')->count(); + $countB = $population->voters()->where('group', '=', 'B')->count(); + + $elections = Election::where('population_id', '=', $population->id) + ->where('type', '=', $attributes['type']) + ->with('extension')->get(); + + $data->no_of_elections = $elections->count(); + $data->moving_average = $movingAverage; + + $weightsTimeline = array(); + foreach ($elections as $election) { + $newWeight = new \stdClass(); + if ($countA > 0) { + + $newWeight->weight_a = $election->extension->weight_a; + $newWeight->avg_weight_a = $election->extension->weight_a / $countA; + } else { + + $newWeight->weight_a = 0; + $newWeight->avg_weight_a = 0; + } + + if ($countB > 0) { + $newWeight->weight_b = $election->extension->weight_b; + $newWeight->avg_weight_b = $election->extension->weight_b / $countB; + } else { + $newWeight->weight_b = 0; + $newWeight->avg_weight_b = 0; + } + array_push($weightsTimeline, $newWeight); + } + $data->weigths = $weightsTimeline; +/* + if($movingAverage > 0) { + $flattenData = []; + if ($data->no_of_elections >= $movingAverage) { + $asArray = $elections->toArray(); + for ($i = $movingAverage - 1; $i < $data->no_of_elections; $i++) { + $sumOfValues = 0; + for ($j = 0; $j < $movingAverage; $j++) { + $sumOfValues += $asArray[$i - $j]; + } + $flattenData[] = $sumOfValues / $movingAverage; + } + } + $data->elections = $flattenData; + } else { + $data->elections = $elections; + }*/ + + return response()->json($data, Response::HTTP_OK); + } + + } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 4fbe348..7996e66 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -26,6 +26,11 @@ class HomeController extends Controller return view('home'); } + public function showPopulationTemplate() + { + return view('population-template-details'); + } + public function showPopulation() { return view('population-details'); diff --git a/app/Population.php b/app/Population.php index 6b16c96..1073db1 100644 --- a/app/Population.php +++ b/app/Population.php @@ -7,7 +7,10 @@ use Illuminate\Database\Eloquent\Model; class Population extends Model { protected $fillable =[ - 'name' + 'name', + 'parent_id', + 'stage', + 'election_type' ]; protected $appends = [ @@ -18,6 +21,60 @@ class Population extends Model return $this->hasMany(Voter::class, 'population_id', 'id'); } + public function childPopulations() { + return $this->hasMany(Population::class, 'parent_id', 'id'); + } + + public function getChildrenCountAttribute() { + return $this->childPopulations()->count(); + } + + public function getChildPopulationsStatsAttribute() { + $data = [ + 'm' => [ + 'info' => 'Majority', + 'count' => 0, + 'learning_stage_count' => 0, + 'performance_stage_count' => 0 + ], + 'd1' => [ + 'info' => 'Delegation Performance (d1)', + 'count' => 0, + 'learning_stage_count' => 0, + 'performance_stage_count' => 0 + ], + 'd2' => [ + 'info' => 'Delegation Learning (d2)', + 'count' => 0, + 'learning_stage_count' => 0, + 'performance_stage_count' => 0 + ], + 'd3' => [ + 'info' => 'Delegation Learning (d3)', + 'count' => 0, + 'learning_stage_count' => 0, + 'performance_stage_count' => 0 + ] + ]; + foreach ($this->childPopulations as $childPopulation) { + if (isset($childPopulation->election_type) && isset($data[$childPopulation->election_type])) { + $data[$childPopulation->election_type]['count']++; + + switch($childPopulation->stage) { + case 'l': + $data[$childPopulation->election_type]['learning_stage_count']++; + break; + case 'p': + $data[$childPopulation->election_type]['performance_stage_count']++; + break; + default: + break; + } + } + } + return $data; + } + public function getNoOfVotersAttribute() { return $this->voters()->count(); } diff --git a/database/migrations/2021_02_18_093626_create_populations_table.php b/database/migrations/2021_02_18_093626_create_populations_table.php index 3e84591..7e6c5c6 100644 --- a/database/migrations/2021_02_18_093626_create_populations_table.php +++ b/database/migrations/2021_02_18_093626_create_populations_table.php @@ -16,8 +16,15 @@ class CreatePopulationsTable extends Migration Schema::create('populations', function (Blueprint $table) { $table->increments('id'); $table->string('name')->nullable(); + $table->integer('parent_id', false, true)->nullable(true)->default(null); + $table->char('stage',1)->nullable(true)->default(null); + $table->char('election_type')->nullable(true)->default(null); $table->timestamps(); }); + + Schema::table('populations', function (Blueprint $table) { + $table->foreign('parent_id')->references('id')->on('populations')->onDelete('cascade'); + }); } /** diff --git a/database/migrations/2021_02_18_093642_create_voters_table.php b/database/migrations/2021_02_18_093642_create_voters_table.php index 82f2a57..f4dbce6 100644 --- a/database/migrations/2021_02_18_093642_create_voters_table.php +++ b/database/migrations/2021_02_18_093642_create_voters_table.php @@ -21,6 +21,7 @@ class CreateVotersTable extends Migration $table->smallInteger('confidence', false, true)->nullable(false); $table->smallInteger('following', false, true)->nullable(false); $table->smallInteger('leadership', false, true)->nullable(false); + $table->integer('reputation', false, false)->default(0); $table->char('group')->nullable(false)->default('x'); }); } diff --git a/database/migrations/2021_03_16_084517_create_extension_delegation_elections_table.php b/database/migrations/2021_03_16_084517_create_extension_delegation_elections_table.php index 4de4715..a3ba374 100644 --- a/database/migrations/2021_03_16_084517_create_extension_delegation_elections_table.php +++ b/database/migrations/2021_03_16_084517_create_extension_delegation_elections_table.php @@ -22,7 +22,8 @@ class CreateExtensionDelegationElectionsTable extends Migration $table->integer('as_delegate', false, true)->nullable(false)->default(0); $table->integer('as_follower', false, true)->nullable(false)->default(0); $table->integer('as_independent', false, true)->nullable(false)->default(0); - + $table->integer('weight_a', false, true)->nullable(false)->default(0); + $table->integer('weight_b', false, true)->nullable(false)->default(0); }); } diff --git a/database/migrations/2021_03_24_161037_add_reputation_to_voters_table.php b/database/migrations/2021_03_24_161037_add_reputation_to_voters_table.php deleted file mode 100644 index 11f8e05..0000000 --- a/database/migrations/2021_03_24_161037_add_reputation_to_voters_table.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -use Illuminate\Database\Migrations\Migration; -use Illuminate\Database\Schema\Blueprint; -use Illuminate\Support\Facades\Schema; - -class AddReputationToVotersTable extends Migration -{ - /** - * Run the migrations. - * - * @return void - */ - public function up() - { - Schema::table('voters', function (Blueprint $table) { - $table->integer('reputation', false, false)->default(0)->after('leadership'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('voters', function (Blueprint $table) { - $table->dropColumn('reputation'); - }); - } -} diff --git a/public/js/app.js b/public/js/app.js index d492cf8..71714eb 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -2330,9 +2330,9 @@ __webpack_require__.r(__webpack_exports__); console.log('add...'); console.log(this.population); - axios.post(route('internal.api.population.post'), this.population).then(function (response) { + axios.post(route('internal.api.template.post'), this.population).then(function (response) { console.log(response.data); - Bus.$emit('PopulationCreated', true, response.data.meta); + Bus.$emit('TemplateCreated', true, response.data.meta); _this.clearAndClose(); })["catch"](function (err) { @@ -2481,6 +2481,38 @@ __webpack_require__.r(__webpack_exports__); // // // +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// /* harmony default export */ __webpack_exports__["default"] = ({ @@ -2508,19 +2540,19 @@ __webpack_require__.r(__webpack_exports__); this.feedback = 'Fetching population index..'; this.fetchPopulationIndex(); this.feedback = 'Population index fetched (newest first).'; - Bus.$on('PopulationCreated', function (refresh, data) { + Bus.$on('TemplateCreated', function (refresh, data) { if (data) { - _this.creationFeedback = "Population created, time: " + data.total_time; + _this.creationFeedback = "Population template created, time: " + data.total_time; _this.fetchPopulationIndex(); } if (refresh === true) { - _this.feedback = 'Population added reloading index..'; + _this.feedback = 'Population template added reloading index..'; _this.fetchPopulationIndex(true); - _this.feedback = 'Index reloaded. Newest population selected.'; + _this.feedback = 'Index reloaded. Newest population template selected.'; } }); }, @@ -2595,18 +2627,20 @@ __webpack_require__.r(__webpack_exports__); var selectFirst = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; this.populations = []; - axios.get(route('internal.api.population.index')).then(function (response) { + axios.get(route('internal.api.templates.index')).then(function (response) { _this2.populations = response.data; if (selectFirst && _this2.populations[0]) { _this2.current_population = _this2.populations[0]; } + + console.log(_this2.current_population); })["catch"](function (err) { _this2.feedback = 'population data error'; }); }, - getLink: function getLink(populationId) { - return route('population.show', populationId); + getLink: function getLink(templateId) { + return route('template.show', templateId); }, addPopulation: function addPopulation() { console.log('add population'); @@ -2887,10 +2921,6 @@ __webpack_require__.r(__webpack_exports__); // // // -// -// -// -// @@ -2930,6 +2960,7 @@ __webpack_require__.r(__webpack_exports__); auto_fetch_distribution: false, feedback: null, population_id: route().params.population_id, + template_id: route().params.template_id, population_stats: null, population_name: null, voters_fetched: false, @@ -2984,6 +3015,33 @@ __webpack_require__.r(__webpack_exports__); this.fetchPopulationStats(); }, computed: { + election_name: function election_name() { + var _this = this; + + if (this.population_stats) { + return this.election_timeline_selector.filter(function (election_type) { + return election_type.value === _this.population_stats.election_type; + })[0].text; + } + + return null; + }, + stage_name: function stage_name() { + if (this.population_stats) { + switch (this.population_stats.stage) { + case 'l': + return "Learning"; + + case 'p': + return "Performance"; + + default: + return "Unrecognized stage"; + } + } + + return null; + }, population_election_stats_chart_data: function population_election_stats_chart_data() { console.log('computing population_election_stats'); var colors = ['#205702', '#b7b30e', '#97510a', '#b22430', '#45026a']; @@ -3283,43 +3341,49 @@ __webpack_require__.r(__webpack_exports__); this.feedback = null; }, fetchPopulationDetails: function fetchPopulationDetails() { - var _this = this; + var _this2 = this; this.feedback = 'fetching voters data..'; - axios.get(route('internal.api.population.get.voters', this.population_id)).then(function (response) { - _this.feedback = 'voters data fetched'; - _this.voters = response.data; - _this.voters_fetched = true; + axios.get(route('internal.api.population.get.voters', { + "template": this.template_id, + "population": this.population_id + })).then(function (response) { + _this2.feedback = 'voters data fetched'; + _this2.voters = response.data; + _this2.voters_fetched = true; })["catch"](function (err) { - _this.feedback = 'voters data fetching error'; + _this2.feedback = 'voters data fetching error'; }); }, fetchPopulationStats: function fetchPopulationStats() { - var _this2 = this; + var _this3 = this; this.feedback = 'fetching population stats..'; - axios.get(route('internal.api.population.get', this.population_id)).then(function (response) { - _this2.feedback = 'population stats fetched'; - _this2.population_stats = response.data; - _this2.population_name = response.data.name; + axios.get(route('internal.api.population.get', { + "template": this.template_id, + "population": this.population_id + })).then(function (response) { + _this3.feedback = 'population stats fetched'; + _this3.population_stats = response.data; + _this3.population_name = response.data.name; - if (_this2.auto_fetch_voters) { - _this2.fetchPopulationDetails(); + if (_this3.auto_fetch_voters) { + _this3.fetchPopulationDetails(); } - if (_this2.auto_fetch_distribution) { - _this2.fetchMajorityElectionsDistribution(); + if (_this3.auto_fetch_distribution) { + _this3.fetchMajorityElectionsDistribution(); } - if (_this2.auto_fetch_elections_timeline) { - _this2.fetchElectionsTimeline(); + if (_this3.auto_fetch_elections_timeline) { + _this3.fetchElectionsTimeline(); } })["catch"](function (err) { - _this2.feedback = 'population stats fetching error'; + _this3.feedback = 'population stats fetching error'; }); }, runElections: function runElections(type, multi) { - var _this3 = this; + var _this4 = this; if (this.running_elections_lock) { this.feedback = 'already running elections, please wait...'; @@ -3327,50 +3391,271 @@ __webpack_require__.r(__webpack_exports__); this.running_elections_lock = true; this.feedback = 'running ' + type + '-type elections: (' + multi + ')...'; this.last_elections_data = null; - axios.post(route('internal.api.elections.run', this.population_id), { + axios.post(route('internal.api.elections.run', { + "template": this.template_id, + "population": this.population_id + }), { type: type, number: multi }).then(function (response) { - _this3.feedback = 'voting done, fetching updated population stats..'; + _this4.feedback = 'voting done, fetching updated population stats..'; - _this3.fetchPopulationStats(); + _this4.fetchPopulationStats(); console.log(response.data); - _this3.last_elections_data = response.data; - _this3.running_elections_lock = false; + _this4.last_elections_data = response.data; + _this4.running_elections_lock = false; })["catch"](function (err) { - _this3.feedback = 'election error'; - _this3.running_elections_lock = false; + _this4.feedback = 'election error'; + _this4.running_elections_lock = false; }); } }, + switchToPerformanceStage: function switchToPerformanceStage() { + var _this5 = this; + + axios.post(route('internal.api.population.stage.update', { + "template": this.template_id, + "population": this.population_id + })).then(function (response) { + _this5.population_stats.stage = 'p'; + _this5.feedback = "Changed population stage. Only elections that do not alter attributes are available."; + })["catch"](function (err) { + _this5.feedback = "Error while changing stage."; + }); + }, fetchMajorityElectionsDistribution: function fetchMajorityElectionsDistribution() { - var _this4 = this; + var _this6 = this; this.feedback = 'fetching majority distribution...'; this.me_distribution_metadata = null; - axios.get(route('internal.api.majority.distribution.get', this.population_id)).then(function (response) { - _this4.feedback = 'majority distribution fetched'; - _this4.majority_elections_distribution = response.data.distribution; - _this4.majority_elections_distribution_r_10 = response.data.distribution_r_10; - _this4.me_distribution_metadata = response.data.metadata; + axios.get(route('internal.api.majority.distribution.get', { + "template": this.template_id, + "population": this.population_id + })).then(function (response) { + _this6.feedback = 'majority distribution fetched'; + _this6.majority_elections_distribution = response.data.distribution; + _this6.majority_elections_distribution_r_10 = response.data.distribution_r_10; + _this6.me_distribution_metadata = response.data.metadata; })["catch"](function (err) { - _this4.feedback = 'majority distribution fetching error'; + _this6.feedback = 'majority distribution fetching error'; }); }, fetchElectionsTimeline: function fetchElectionsTimeline() { - var _this5 = this; + var _this7 = this; - axios.get(route('internal.api.population.get.elections.timeline', this.population_id), { + axios.get(route('internal.api.population.get.elections.timeline', { + "template": this.template_id, + "population": this.population_id + }), { params: { 'type': this.current_election_timeline_key.value, 'moving_average': this.moving_average } }).then(function (response) { - _this5.elections_timeline = response.data; - _this5.feedback = 'election timeline fetched'; + _this7.elections_timeline = response.data; + _this7.feedback = 'election timeline fetched'; + })["catch"](function (err) { + _this7.feedback = 'election timeline fetching error'; + }); + } + } +}); + +/***/ }), + +/***/ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/population-template-show.vue?vue&type=script&lang=js&": +/*!***********************************************************************************************************************************************************************************!*\ + !*** ./node_modules/babel-loader/lib??ref--4-0!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/components/population-template-show.vue?vue&type=script&lang=js& ***! + \***********************************************************************************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var vue_select__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue-select */ "./node_modules/vue-select/dist/vue-select.js"); +/* harmony import */ var vue_select__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(vue_select__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var vue_select_dist_vue_select_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! vue-select/dist/vue-select.css */ "./node_modules/vue-select/dist/vue-select.css"); +/* harmony import */ var vue_select_dist_vue_select_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(vue_select_dist_vue_select_css__WEBPACK_IMPORTED_MODULE_1__); +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// + + +/* harmony default export */ __webpack_exports__["default"] = ({ + name: "population-template-show", + components: { + vSelect: vue_select__WEBPACK_IMPORTED_MODULE_0___default.a + }, + data: function data() { + return { + current_election_type_key: null, + election_type_selector: [{ + value: 'm', + text: 'Majority (m)', + info: 'Majority election, check expertise only' + }, { + value: 'd1', + text: 'Delegation version 1 (d1)', + info: 'Delegation voting, no adjustments (performance stage)' + }, { + value: 'd2', + text: 'Delegation version 2 (d2)', + info: 'Delegation voting, include reputation adjustments (learning stage)' + }, { + value: 'd3', + text: 'Delegation version 3 (d3)', + info: 'Delegation voting, include reputation and voters attributes adjustments (learning stage)' + }], + current_template: null, + feedback: null, + template_id: route().params.template_id, + template_name: null + }; + }, + mounted: function mounted() { + this.fetchPopulationTemplate(); + }, + methods: { + resetFeedback: function resetFeedback() { + this.feedback = null; + }, + fetchPopulationTemplate: function fetchPopulationTemplate() { + var _this = this; + + this.feedback = 'fetching population template details..'; + axios.get(route('internal.api.template.get', this.template_id)).then(function (response) { + _this.feedback = 'population template details fetched'; + _this.current_template = response.data; + })["catch"](function (err) { + _this.feedback = 'population template fetching error'; + }); + }, + addChildPopulation: function addChildPopulation(key) { + var _this2 = this; + + this.feedback = 'Adding child population, binding ' + key; + axios.post(route('internal.api.populations.post', this.current_template.id), { + "election_type": key + }).then(function (response) { + _this2.feedback = 'Child population added'; + + _this2.fetchPopulationTemplate(); })["catch"](function (err) { - _this5.feedback = 'election timeline fetching error'; + _this2.feedback = 'Adding child population error'; + }); + }, + getChildPopulationLink: function getChildPopulationLink(populationId) { + return route('population.show', { + "template_id": this.current_template.id, + "population_id": populationId }); } } @@ -79022,7 +79307,7 @@ var render = function() { }, [ _vm._v( - "\n Add population\n " + "\n Add population template\n " ) ] ) @@ -79067,7 +79352,19 @@ var render = function() { ]), _vm._v(" "), _c("br"), - _c("i", [_vm._v("elections: ")]), + _c("i", [ + _vm._v( + "Number of child-populations: " + + _vm._s(population.children_count) + ) + ]), + _vm._v(" "), + _c("br"), + _c("i", [ + _vm._v("Number of elections run on template: ") + ]), + _vm._v(" "), + _c("br"), _vm._v(" "), _vm._l(population.elections_stats, function(election) { return _c("i", [ @@ -79100,24 +79397,81 @@ var render = function() { _vm._v(" "), _c("div", { staticClass: "card-body" }, [ _vm._v( - "\n Voters' details and new elections available in\n " + "\n Voters' and child-populations details and new elections available in\n " ), _c( "a", { attrs: { href: _vm.getLink(_vm.current_population.id) } }, - [_vm._v("detail view")] + [_vm._v("Population Template detail view")] ) ]) ]), _vm._v(" "), _c("div", { staticClass: "card" }, [ _c("div", { staticClass: "card-header" }, [ - _vm._v("Election stats") + _vm._v("Child Populations stats") ]), _vm._v(" "), - _vm.current_population.elections_stats + _vm.current_population.child_populations_stats + ? _c("div", { staticClass: "card-body" }, [ + _c("p", [ + _vm._v( + "\n Number of child populations locked to election type:\n " + ) + ]), + _vm._v(" "), + _c("table", [ + _vm._m(1), + _vm._v(" "), + _c( + "tbody", + _vm._l( + _vm.current_population.child_populations_stats, + function(election_type, key) { + return _c("tr", [ + _c("td", [_vm._v(_vm._s(key))]), + _vm._v(" "), + _c("td", [ + _vm._v(_vm._s(election_type.info)) + ]), + _vm._v(" "), + _c("td", [ + _vm._v(_vm._s(election_type.count)) + ]), + _vm._v(" "), + _c("td", [ + _vm._v( + _vm._s(election_type.learning_stage_count) + ) + ]), + _vm._v(" "), + _c("td", [ + _vm._v( + _vm._s( + election_type.performance_stage_count + ) + ) + ]) + ]) + } + ), + 0 + ) + ]) + ]) + : _c("div", { staticClass: "card-body" }, [ + _c("i", [_vm._v("N/A")]) + ]) + ]), + _vm._v(" "), + _c("div", { staticClass: "card" }, [ + _c("div", { staticClass: "card-header" }, [ + _vm._v("Voters stats") + ]), + _vm._v(" "), + _vm.current_population.voters_stats ? _c( "div", { staticClass: "card-body" }, @@ -79125,7 +79479,7 @@ var render = function() { _c("bar-chart", { attrs: { "chart-data": - _vm.population_election_stats_chart_data, + _vm.population_voters_stats_chart_data, options: { maintainAspectRatio: false, scales: { @@ -79155,10 +79509,10 @@ var render = function() { _vm._v(" "), _c("div", { staticClass: "card" }, [ _c("div", { staticClass: "card-header" }, [ - _vm._v("Voters stats") + _vm._v("Population Template election stats") ]), _vm._v(" "), - _vm.current_population.voters_stats + _vm.current_population.elections_stats ? _c( "div", { staticClass: "card-body" }, @@ -79166,7 +79520,7 @@ var render = function() { _c("bar-chart", { attrs: { "chart-data": - _vm.population_voters_stats_chart_data, + _vm.population_election_stats_chart_data, options: { maintainAspectRatio: false, scales: { @@ -79217,6 +79571,22 @@ var staticRenderFns = [ _vm._v("Select population for details.") ]) ]) + }, + function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c("thead", [ + _c("th", [_vm._v("Type")]), + _vm._v(" "), + _c("th", [_vm._v("Description")]), + _vm._v(" "), + _c("th", [_vm._v("Total")]), + _vm._v(" "), + _c("th", [_vm._v("Learning")]), + _vm._v(" "), + _c("th", [_vm._v("Performance")]) + ]) } ] render._withStripped = true @@ -79249,209 +79619,188 @@ var render = function() { _vm._v(_vm._s(_vm.population_name) + " Actions") ]), _vm._v(" "), - _c("div", { staticClass: "card-body" }, [ - _c("div", [ - _c("label", { staticClass: "text-info" }, [ - _vm._v("Number of elections: ") - ]), - _vm._v(" "), - _c("input", { - directives: [ - { - name: "model", - rawName: "v-model", - value: _vm.custom_number_elections, - expression: "custom_number_elections" - } - ], - staticStyle: { width: "70px" }, - attrs: { type: "number", min: "1", max: "100", step: "0" }, - domProps: { value: _vm.custom_number_elections }, - on: { - input: function($event) { - if ($event.target.composing) { - return - } - _vm.custom_number_elections = $event.target.value - } - } - }) - ]), - _vm._v(" "), - _c("div", [ - _c("h5", [_vm._v("Majority elections:")]), - _vm._v(" "), - _c( - "button", - { - staticClass: "btn btn-sm btn-outline-info", - attrs: { disabled: _vm.running_elections_lock }, - on: { - click: function($event) { - $event.preventDefault() - return _vm.runElections( - "m", - _vm.custom_number_elections - ) - } - } - }, - [ - _vm._v( - "\n Run " + - _vm._s(_vm.custom_number_elections) + - " election" - ), - _vm.custom_number_elections > 1 - ? _c("span", [_vm._v("s")]) - : _vm._e(), - _vm._v(" "), - _c("i", [_vm._v("(type m)")]) - ] - ), - _vm._v(" "), - _c("br"), - _vm._v(" "), - _c( - "button", - { - staticClass: "btn btn-sm btn-outline-info", - attrs: { disabled: _vm.running_elections_lock }, - on: { - click: function($event) { - $event.preventDefault() - return _vm.fetchMajorityElectionsDistribution($event) - } - } - }, - [_vm._v("Fetch majority elections distribution")] - ) - ]), - _vm._v(" "), - _c("hr"), - _vm._v(" "), - _vm._m(0), - _vm._v(" "), - _c("div", [ - _vm._v("\n Delegation elections"), - _c("i", [_vm._v("(type d1)")]), - _vm._v(" :"), - _c("br"), - _vm._v(" "), - _c( - "button", - { - staticClass: "btn btn-sm btn-outline-info", - attrs: { disabled: _vm.running_elections_lock }, - on: { - click: function($event) { - $event.preventDefault() - return _vm.runElections( - "d1", - _vm.custom_number_elections - ) - } - } - }, - [ - _vm._v( - "\n Run " + - _vm._s(_vm.custom_number_elections) + - " election" - ), - _vm.custom_number_elections > 1 - ? _c("span", [_vm._v("s")]) - : _vm._e(), - _vm._v(" "), - _c("i", [_vm._v("(type d1)")]) - ] - ) - ]), - _vm._v(" "), - _c("div", [ - _vm._v("\n Delegation elections "), - _c("i", [_vm._v("(type d2)")]), - _vm._v(" :"), - _c("br"), - _vm._v(" "), - _c("i", { staticClass: "text-muted text-sm-left" }, [ - _vm._v("Reputation included") - ]), - _c("br"), - _vm._v(" "), - _c( - "button", - { - staticClass: "btn btn-sm btn-outline-info", - attrs: { disabled: _vm.running_elections_lock }, - on: { - click: function($event) { - $event.preventDefault() - return _vm.runElections( - "d2", - _vm.custom_number_elections - ) - } - } - }, - [ - _vm._v( - "\n Run " + - _vm._s(_vm.custom_number_elections) + - " election" - ), - _vm.custom_number_elections > 1 - ? _c("span", [_vm._v("s")]) - : _vm._e(), - _vm._v(" "), - _c("i", [_vm._v("(type d2)")]) - ] - ) - ]), - _vm._v(" "), - _c("div", [ - _vm._v("\n Delegation elections "), - _c("i", [_vm._v("(type d3)")]), - _vm._v(" :"), - _c("br"), - _vm._v(" "), - _c("i", { staticClass: "text-muted text-sm-left" }, [ - _vm._v( - "Reputation included. Following and Leadership attributes may change for each election !! (do not use together with d1 or d2 on one population)" - ) - ]), - _c("br"), - _vm._v(" "), - _c( - "button", - { - staticClass: "btn btn-sm btn-outline-info", - attrs: { disabled: _vm.running_elections_lock }, - on: { - click: function($event) { - $event.preventDefault() - return _vm.runElections( - "d3", - _vm.custom_number_elections - ) - } - } - }, - [ + _vm.stage_name + ? _c("div", { staticClass: "card-body" }, [ + _vm.population_stats.stage === "l" + ? _c("div", [ + _c("h5", [_vm._v("Learning stage")]), + _vm._v(" "), + _vm.population_stats.stage === "l" + ? _c( + "button", + { + on: { + click: function($event) { + $event.preventDefault() + return _vm.switchToPerformanceStage() + } + } + }, + [ + _vm._v( + "\n Finish Learning stage\n " + ) + ] + ) + : _vm._e(), + _vm._v(" "), + _c("h5", [_vm._v(_vm._s(_vm.election_name) + ":")]), + _vm._v(" "), + _c("div", [ + _c("label", { staticClass: "text-info" }, [ + _vm._v("Number of elections: ") + ]), + _vm._v(" "), + _c("input", { + directives: [ + { + name: "model", + rawName: "v-model", + value: _vm.custom_number_elections, + expression: "custom_number_elections" + } + ], + staticStyle: { width: "70px" }, + attrs: { + type: "number", + min: "1", + max: "100", + step: "0" + }, + domProps: { value: _vm.custom_number_elections }, + on: { + input: function($event) { + if ($event.target.composing) { + return + } + _vm.custom_number_elections = + $event.target.value + } + } + }) + ]), + _vm._v(" "), + _c( + "button", + { + staticClass: "btn btn-sm btn-outline-info", + attrs: { disabled: _vm.running_elections_lock }, + on: { + click: function($event) { + $event.preventDefault() + return _vm.runElections( + _vm.population_stats.election_type, + _vm.custom_number_elections + ) + } + } + }, + [ + _vm._v( + "\n Run " + + _vm._s(_vm.custom_number_elections) + + " election" + ), + _vm.custom_number_elections > 1 + ? _c("span", [_vm._v("s")]) + : _vm._e(), + _vm._v(" "), + _c("i", [ + _vm._v( + "(" + + _vm._s(_vm.population_stats.election_type) + + ")" + ) + ]) + ] + ), + _vm._v(" "), + _c("hr") + ]) + : _vm.population_stats.stage === "p" + ? _c("div", [ + _c("h5", [_vm._v("Performance stage")]), + _vm._v(" "), + _c("h5", [_vm._v(_vm._s(_vm.election_name) + ":")]), + _vm._v(" "), + _c("div", [ + _c("label", { staticClass: "text-info" }, [ + _vm._v("Number of elections: ") + ]), + _vm._v(" "), + _c("input", { + directives: [ + { + name: "model", + rawName: "v-model", + value: _vm.custom_number_elections, + expression: "custom_number_elections" + } + ], + staticStyle: { width: "70px" }, + attrs: { + type: "number", + min: "1", + max: "100", + step: "0" + }, + domProps: { value: _vm.custom_number_elections }, + on: { + input: function($event) { + if ($event.target.composing) { + return + } + _vm.custom_number_elections = + $event.target.value + } + } + }) + ]), + _vm._v(" "), + _c( + "button", + { + staticClass: "btn btn-sm btn-outline-info", + attrs: { disabled: _vm.running_elections_lock }, + on: { + click: function($event) { + $event.preventDefault() + return _vm.runElections( + "d1", + _vm.custom_number_elections + ) + } + } + }, + [ + _vm._v( + "\n Run " + + _vm._s(_vm.custom_number_elections) + + " election" + ), + _vm.custom_number_elections > 1 + ? _c("span", [_vm._v("s")]) + : _vm._e(), + _vm._v(" "), + _c("i", [_vm._v("(type d1)")]) + ] + ), + _vm._v(" "), + _c("hr") + ]) + : _c("div", [ + _c("i", { staticClass: "text-warning" }, [ + _vm._v("Unrecognized stage code.") + ]) + ]) + ]) + : _c("div", [ + _c("i", [ _vm._v( - "\n Run " + - _vm._s(_vm.custom_number_elections) + - " election" - ), - _vm.custom_number_elections > 1 - ? _c("span", [_vm._v("s")]) - : _vm._e(), - _vm._v(" "), - _c("i", [_vm._v("(type d3)")]) - ] - ) - ]) - ]) + "Population stage not defined. Cannot run elections." + ) + ]) + ]) ]) ]) ]), @@ -80262,20 +80611,349 @@ var render = function() { ]) ]) } +var staticRenderFns = [] +render._withStripped = true + + + +/***/ }), + +/***/ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true&": +/*!***************************************************************************************************************************************************************************************************************************************!*\ + !*** ./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/components/population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true& ***! + \***************************************************************************************************************************************************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "render", function() { return render; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "staticRenderFns", function() { return staticRenderFns; }); +var render = function() { + var _vm = this + var _h = _vm.$createElement + var _c = _vm._self._c || _h + return _c("div", { staticClass: "p-2" }, [ + _c("div", { staticClass: "row" }, [ + _c("div", { staticClass: "col-md-3 col-lg-3" }), + _vm._v(" "), + _c("div", { staticClass: "col-md-9 col-lg-9" }, [ + _c("div", { staticClass: "row" }, [ + _vm.feedback + ? _c( + "div", + { staticClass: "alert alert-info col-md-12 col-lg-12" }, + [ + _vm._v( + "\n INFO: " + + _vm._s(_vm.feedback) + + "\n " + ), + _c( + "button", + { + staticClass: "float-right btn btn-sm btn-outline-info", + on: { + click: function($event) { + $event.preventDefault() + return _vm.resetFeedback($event) + } + } + }, + [_vm._v("x")] + ) + ] + ) + : _vm._e() + ]), + _vm._v(" "), + _vm.current_template + ? _c("div", { staticClass: "row" }, [ + _c( + "div", + { staticClass: "alert alert-info col-md-12 col-lg-12" }, + [ + _c("div", { staticClass: "card" }, [ + _c("div", { staticClass: "card-header" }, [ + _vm._v("Child Populations stats") + ]), + _vm._v(" "), + _vm.current_template.child_populations_stats + ? _c("div", { staticClass: "card-body" }, [ + _c("p", [ + _vm._v( + "\n Number of child populations locked to election type:\n " + ) + ]), + _vm._v(" "), + _c( + "table", + { + staticClass: + "table table-striped table-borderless" + }, + [ + _vm._m(0), + _vm._v(" "), + _c( + "tbody", + _vm._l( + _vm.current_template.child_populations_stats, + function(election_type, key) { + return _c("tr", [ + _c("td", [ + _c( + "button", + { + on: { + click: function($event) { + $event.preventDefault() + return _vm.addChildPopulation( + key + ) + } + } + }, + [ + _vm._v( + "\n Add child population\n " + ) + ] + ) + ]), + _vm._v(" "), + _c("td", [_vm._v(_vm._s(key))]), + _vm._v(" "), + _c("td", [ + _vm._v(_vm._s(election_type.info)) + ]), + _vm._v(" "), + _c("td", [ + _vm._v(_vm._s(election_type.count)) + ]), + _vm._v(" "), + _c("td", [ + _vm._v( + _vm._s( + election_type.learning_stage_count + ) + ) + ]), + _vm._v(" "), + _c("td", [ + _vm._v( + _vm._s( + election_type.performance_stage_count + ) + ) + ]) + ]) + } + ), + 0 + ) + ] + ) + ]) + : _c("div", { staticClass: "card-body" }, [ + _c("i", [_vm._v("N/A")]) + ]) + ]) + ] + ) + ]) + : _vm._e(), + _vm._v(" "), + _c("div", { staticClass: "row" }, [ + _c("div", { staticClass: "alert alert-info col-md-12 col-lg-12" }, [ + _c("div", { staticClass: "card" }, [ + _c("div", { staticClass: "card-header" }, [ + _vm._v("Child Populations") + ]), + _vm._v(" "), + _vm.current_template + ? _c("div", { staticClass: "card-body" }, [ + _c("div", { staticClass: "row" }, [ + _c( + "div", + { staticClass: "col-md-4 col-lg-4" }, + [ + _c("v-select", { + attrs: { + options: _vm.election_type_selector, + id: "election_type_selector", + placeholder: "Choose election type", + label: "text" + }, + model: { + value: _vm.current_election_type_key, + callback: function($$v) { + _vm.current_election_type_key = $$v + }, + expression: "current_election_type_key" + } + }) + ], + 1 + ), + _vm._v(" "), + _c("div", { staticClass: "col-md-8 col-lg-8" }, [ + _c( + "button", + { + staticClass: "btn-xs", + class: { + "btn-primary": _vm.current_election_type_key + }, + attrs: { disabled: !_vm.current_election_type_key }, + on: { + click: function($event) { + $event.preventDefault() + return _vm.addChildPopulation($event) + } + } + }, + [ + _vm.current_election_type_key + ? _c("i", [ + _vm._v( + "Add child population - bind election type: " + + _vm._s(_vm.current_election_type_key.text) + ) + ]) + : _c("i", [ + _vm._v( + "Select election type for child population" + ) + ]) + ] + ), + _vm._v(" "), + _vm.current_election_type_key + ? _c("i", { staticClass: "text-info" }, [ + _vm._v( + _vm._s(_vm.current_election_type_key.info) + ")" + ) + ]) + : _vm._e() + ]) + ]), + _vm._v(" "), + _c("table", { staticClass: "table table-striped" }, [ + _c( + "thead", + [ + _c("th", [_vm._v("Type")]), + _vm._v(" "), + _c("th", [_vm._v("Name")]), + _vm._v(" "), + _c("th", [_vm._v("Stage")]), + _vm._v(" "), + _vm._l(_vm.election_type_selector, function( + election_type + ) { + return _c("th", [ + _vm._v( + "\n " + + _vm._s(election_type.text) + + "\n " + ) + ]) + }) + ], + 2 + ), + _vm._v(" "), + _c( + "tbody", + _vm._l(_vm.current_template.child_populations, function( + child_population + ) { + return _c( + "tr", + [ + _c("td", [ + _vm._v(_vm._s(child_population.election_type)) + ]), + _vm._v(" "), + _c("td", [ + _c( + "a", + { + attrs: { + href: _vm.getChildPopulationLink( + child_population.id + ) + } + }, + [_vm._v(_vm._s(child_population.name))] + ) + ]), + _vm._v(" "), + _c("td", [ + _vm._v(_vm._s(child_population.stage)) + ]), + _vm._v(" "), + _vm._l(child_population.elections_stats, function( + election_type + ) { + return _c("td", [ + election_type.count > 0 + ? _c("span", [ + _vm._v( + "Count: " + + _vm._s(election_type.count) + + " (average correct: " + + _vm._s( + election_type.percent_correct.toFixed( + 2 + ) + ) + + ")" + ) + ]) + : _c("i", [_vm._v("N/A")]) + ]) + }), + _vm._v(" "), + _c("td", [ + _vm._v(_vm._s(child_population.elections_count)) + ]) + ], + 2 + ) + }), + 0 + ) + ]) + ]) + : _c("div", { staticClass: "card-body" }, [ + _c("i", [_vm._v("N/A")]) + ]) + ]) + ]) + ]) + ]) + ]) + ]) +} var staticRenderFns = [ function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h - return _c("div", [ - _c("h5", [_vm._v("Delegation elections")]), + return _c("thead", [ + _c("th", [_vm._v("Action")]), _vm._v(" "), - _c("i", { staticClass: "text-muted text-sm-left" }, [ - _vm._v( - "Three options, being: delegate/follower/independent (chance based on Leadership and Following), delegates and independents use own Expertise (single delegation level)." - ) - ]), - _c("br") + _c("th", [_vm._v("Type")]), + _vm._v(" "), + _c("th", [_vm._v("Description")]), + _vm._v(" "), + _c("th", [_vm._v("Total")]), + _vm._v(" "), + _c("th", [_vm._v("Learning")]), + _vm._v(" "), + _c("th", [_vm._v("Performance")]) ]) } ] @@ -92518,6 +93196,7 @@ window.Bus = new Vue(); Vue.component('example-component', __webpack_require__(/*! ./components/ExampleComponent.vue */ "./resources/js/components/ExampleComponent.vue")["default"]); Vue.component('population-index', __webpack_require__(/*! ./components/population-index.vue */ "./resources/js/components/population-index.vue")["default"]); Vue.component('population-show', __webpack_require__(/*! ./components/population-show.vue */ "./resources/js/components/population-show.vue")["default"]); +Vue.component('population-template-show', __webpack_require__(/*! ./components/population-template-show.vue */ "./resources/js/components/population-template-show.vue")["default"]); /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application @@ -92949,6 +93628,75 @@ __webpack_require__.r(__webpack_exports__); +/***/ }), + +/***/ "./resources/js/components/population-template-show.vue": +/*!**************************************************************!*\ + !*** ./resources/js/components/population-template-show.vue ***! + \**************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _population_template_show_vue_vue_type_template_id_e842ed8a_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true& */ "./resources/js/components/population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true&"); +/* harmony import */ var _population_template_show_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./population-template-show.vue?vue&type=script&lang=js& */ "./resources/js/components/population-template-show.vue?vue&type=script&lang=js&"); +/* empty/unused harmony star reexport *//* harmony import */ var _node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js */ "./node_modules/vue-loader/lib/runtime/componentNormalizer.js"); + + + + + +/* normalize component */ + +var component = Object(_node_modules_vue_loader_lib_runtime_componentNormalizer_js__WEBPACK_IMPORTED_MODULE_2__["default"])( + _population_template_show_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_1__["default"], + _population_template_show_vue_vue_type_template_id_e842ed8a_scoped_true___WEBPACK_IMPORTED_MODULE_0__["render"], + _population_template_show_vue_vue_type_template_id_e842ed8a_scoped_true___WEBPACK_IMPORTED_MODULE_0__["staticRenderFns"], + false, + null, + "e842ed8a", + null + +) + +/* hot reload */ +if (false) { var api; } +component.options.__file = "resources/js/components/population-template-show.vue" +/* harmony default export */ __webpack_exports__["default"] = (component.exports); + +/***/ }), + +/***/ "./resources/js/components/population-template-show.vue?vue&type=script&lang=js&": +/*!***************************************************************************************!*\ + !*** ./resources/js/components/population-template-show.vue?vue&type=script&lang=js& ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _node_modules_babel_loader_lib_index_js_ref_4_0_node_modules_vue_loader_lib_index_js_vue_loader_options_population_template_show_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/babel-loader/lib??ref--4-0!../../../node_modules/vue-loader/lib??vue-loader-options!./population-template-show.vue?vue&type=script&lang=js& */ "./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/population-template-show.vue?vue&type=script&lang=js&"); +/* empty/unused harmony star reexport */ /* harmony default export */ __webpack_exports__["default"] = (_node_modules_babel_loader_lib_index_js_ref_4_0_node_modules_vue_loader_lib_index_js_vue_loader_options_population_template_show_vue_vue_type_script_lang_js___WEBPACK_IMPORTED_MODULE_0__["default"]); + +/***/ }), + +/***/ "./resources/js/components/population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true&": +/*!*********************************************************************************************************!*\ + !*** ./resources/js/components/population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true& ***! + \*********************************************************************************************************/ +/*! exports provided: render, staticRenderFns */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_population_template_show_vue_vue_type_template_id_e842ed8a_scoped_true___WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! -!../../../node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../node_modules/vue-loader/lib??vue-loader-options!./population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true& */ "./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/vue-loader/lib/index.js?!./resources/js/components/population-template-show.vue?vue&type=template&id=e842ed8a&scoped=true&"); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "render", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_population_template_show_vue_vue_type_template_id_e842ed8a_scoped_true___WEBPACK_IMPORTED_MODULE_0__["render"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "staticRenderFns", function() { return _node_modules_vue_loader_lib_loaders_templateLoader_js_vue_loader_options_node_modules_vue_loader_lib_index_js_vue_loader_options_population_template_show_vue_vue_type_template_id_e842ed8a_scoped_true___WEBPACK_IMPORTED_MODULE_0__["staticRenderFns"]; }); + + + /***/ }), /***/ "./resources/sass/app.scss": diff --git a/resources/js/app.js b/resources/js/app.js index d46a2a4..6a1c2be 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -24,6 +24,7 @@ window.Bus = new Vue(); Vue.component('example-component', require('./components/ExampleComponent.vue').default); Vue.component('population-index', require('./components/population-index.vue').default); Vue.component('population-show', require('./components/population-show.vue').default); +Vue.component('population-template-show', require('./components/population-template-show.vue').default); /** * Next, we will create a fresh Vue application instance and attach it to diff --git a/resources/js/components/population-create.vue b/resources/js/components/population-create.vue index 8ccf64d..ec061d8 100644 --- a/resources/js/components/population-create.vue +++ b/resources/js/components/population-create.vue @@ -290,9 +290,9 @@ addNewPopulation() { console.log('add...'); console.log(this.population); - axios.post(route('internal.api.population.post'), this.population).then((response) => { + axios.post(route('internal.api.template.post'), this.population).then((response) => { console.log(response.data); - Bus.$emit('PopulationCreated', true, response.data.meta); + Bus.$emit('TemplateCreated', true, response.data.meta); this.clearAndClose(); }).catch((err) => { console.log(err.response.data); diff --git a/resources/js/components/population-index.vue b/resources/js/components/population-index.vue index ae8b192..bdf3989 100644 --- a/resources/js/components/population-index.vue +++ b/resources/js/components/population-index.vue @@ -24,7 +24,7 @@ data-target="#create-population-modal" data-toggle="modal" > - Add population + Add population template </button> </div> <hr> @@ -38,7 +38,9 @@ v-bind:class="{'btn-info text-white': (current_population != null && current_population.id == population.id)}" > {{population.name}} <i>voters: {{population.voters_stats.no_of_voters}},</i> - <br><i>elections: </i> + <br><i>Number of child-populations: {{population.children_count}}</i> + <br><i>Number of elections run on template: </i> + <br> <i v-for="election in population.elections_stats">(type-{{election.type}}: # {{election.count}})</i> </span> </div> @@ -53,14 +55,43 @@ <div class="card-header">{{current_population.name}}</div> <div class="card-body"> - Voters' details and new elections available in - <a :href="getLink(current_population.id)">detail view</a> + Voters' and child-populations details and new elections available in + <a :href="getLink(current_population.id)">Population Template detail view</a> </div> </div> + <div class="card"> - <div class="card-header">Election stats</div> - <div v-if="current_population.elections_stats" class="card-body"> - <bar-chart :chart-data="population_election_stats_chart_data" + <div class="card-header">Child Populations stats</div> + <div v-if="current_population.child_populations_stats" class="card-body"> + <p> + Number of child populations locked to election type: + </p> + <table> + <thead> + <th>Type</th> + <th>Description</th> + <th>Total</th> + <th>Learning</th> + <th>Performance</th> + </thead> + <tbody> + <tr v-for="(election_type, key) in current_population.child_populations_stats"> + <td>{{key}}</td> + <td>{{election_type.info}}</td> + <td>{{election_type.count}}</td> + <td>{{election_type.learning_stage_count}}</td> + <td>{{election_type.performance_stage_count}}</td> + </tr> + </tbody> + </table> + </div> + <div v-else class="card-body"><i>N/A</i></div> + </div> + + <div class="card"> + <div class="card-header">Voters stats</div> + <div v-if="current_population.voters_stats" class="card-body"> + <bar-chart :chart-data="population_voters_stats_chart_data" :options="{ maintainAspectRatio: false, scales: { @@ -78,11 +109,12 @@ </div> <div v-else class="card-body"><i>N/A</i></div> </div> + <div class="card"> - <div class="card-header">Voters stats</div> - <div v-if="current_population.voters_stats" class="card-body"> - <bar-chart :chart-data="population_voters_stats_chart_data" - :options="{ + <div class="card-header">Population Template election stats</div> + <div v-if="current_population.elections_stats" class="card-body"> + <bar-chart :chart-data="population_election_stats_chart_data" + :options="{ maintainAspectRatio: false, scales: { yAxes: [{ @@ -95,7 +127,7 @@ } }] }}" - :styles="{height: 200}"></bar-chart> + :styles="{height: 200}"></bar-chart> </div> <div v-else class="card-body"><i>N/A</i></div> </div> @@ -135,15 +167,15 @@ this.feedback = 'Fetching population index..'; this.fetchPopulationIndex(); this.feedback = 'Population index fetched (newest first).'; - Bus.$on('PopulationCreated', (refresh, data) => { + Bus.$on('TemplateCreated', (refresh, data) => { if (data) { - this.creationFeedback = "Population created, time: " + data.total_time; + this.creationFeedback = "Population template created, time: " + data.total_time; this.fetchPopulationIndex(); } if (refresh === true) { - this.feedback = 'Population added reloading index..'; + this.feedback = 'Population template added reloading index..'; this.fetchPopulationIndex(true); - this.feedback = 'Index reloaded. Newest population selected.'; + this.feedback = 'Index reloaded. Newest population template selected.'; } }); }, @@ -231,17 +263,18 @@ }, fetchPopulationIndex(selectFirst = false) { this.populations = []; - axios.get(route('internal.api.population.index')).then((response) => { + axios.get(route('internal.api.templates.index')).then((response) => { this.populations = response.data; if(selectFirst && this.populations[0]) { this.current_population = this.populations[0] } + console.log(this.current_population); }).catch((err) => { this.feedback = 'population data error'; }); }, - getLink(populationId) { - return route('population.show', populationId); + getLink(templateId) { + return route('template.show', templateId); }, addPopulation() { console.log('add population'); diff --git a/resources/js/components/population-show.vue b/resources/js/components/population-show.vue index a9aab02..ca752af 100644 --- a/resources/js/components/population-show.vue +++ b/resources/js/components/population-show.vue @@ -5,45 +5,41 @@ <div class="col-md-12"> <div class="card"> <div class="card-header">{{population_name}} Actions</div> - <div class="card-body"> - <div> - <label class="text-info">Number of elections: </label> - <input type="number" min="1" max="100" step="0" v-model="custom_number_elections" style="width:70px"> - </div> - <div> - <h5>Majority elections:</h5> - <button :disabled="running_elections_lock" class="btn btn-sm btn-outline-info" @click.prevent="runElections('m', custom_number_elections)"> - Run {{custom_number_elections}} election<span v-if="custom_number_elections > 1">s</span> <i>(type m)</i> + <div class="card-body" v-if="stage_name"> + <div v-if="population_stats.stage === 'l'"> + <h5>Learning stage</h5> + <button v-if="population_stats.stage === 'l'" @click.prevent="switchToPerformanceStage()"> + Finish Learning stage </button> - <br> - <button :disabled="running_elections_lock" class="btn btn-sm btn-outline-info" @click.prevent="fetchMajorityElectionsDistribution">Fetch majority elections distribution</button> - </div> - <hr> - <div> - <h5>Delegation elections</h5> - <i class="text-muted text-sm-left">Three options, being: delegate/follower/independent (chance based on Leadership and Following), delegates and independents use own Expertise (single delegation level).</i><br> - </div> - <div> - Delegation elections<i>(type d1)</i> :<br> - <button :disabled="running_elections_lock" class="btn btn-sm btn-outline-info" @click.prevent="runElections('d1', custom_number_elections)"> - Run {{custom_number_elections}} election<span v-if="custom_number_elections > 1">s</span> <i>(type d1)</i> + <h5>{{election_name}}:</h5> + <div> + <label class="text-info">Number of elections: </label> + <input type="number" min="1" max="100" step="0" v-model="custom_number_elections" style="width:70px"> + </div> + <button :disabled="running_elections_lock" class="btn btn-sm btn-outline-info" @click.prevent="runElections(population_stats.election_type, custom_number_elections)"> + Run {{custom_number_elections}} election<span v-if="custom_number_elections > 1">s</span> <i>({{population_stats.election_type}})</i> </button> + <hr> </div> - <div> - Delegation elections <i>(type d2)</i> :<br> - <i class="text-muted text-sm-left">Reputation included</i><br> - <button :disabled="running_elections_lock" class="btn btn-sm btn-outline-info" @click.prevent="runElections('d2', custom_number_elections)"> - Run {{custom_number_elections}} election<span v-if="custom_number_elections > 1">s</span> <i>(type d2)</i> + <div v-else-if="population_stats.stage === 'p'"> + <h5>Performance stage</h5> + <h5>{{election_name}}:</h5> + <div> + <label class="text-info">Number of elections: </label> + <input type="number" min="1" max="100" step="0" v-model="custom_number_elections" style="width:70px"> + </div> + <button :disabled="running_elections_lock" class="btn btn-sm btn-outline-info" @click.prevent="runElections('d1', custom_number_elections)"> + Run {{custom_number_elections}} election<span v-if="custom_number_elections > 1">s</span> <i>(type d1)</i> </button> + <hr> </div> - <div> - Delegation elections <i>(type d3)</i> :<br> - <i class="text-muted text-sm-left">Reputation included. Following and Leadership attributes may change for each election !! (do not use together with d1 or d2 on one population)</i><br> - <button :disabled="running_elections_lock" class="btn btn-sm btn-outline-info" @click.prevent="runElections('d3', custom_number_elections)"> - Run {{custom_number_elections}} election<span v-if="custom_number_elections > 1">s</span> <i>(type d3)</i> - </button> + <div v-else> + <i class="text-warning">Unrecognized stage code.</i> </div> </div> + <div v-else> + <i>Population stage not defined. Cannot run elections.</i> + </div> </div> </div> </div> @@ -300,6 +296,7 @@ auto_fetch_distribution: false, feedback : null, population_id: route().params.population_id, + template_id: route().params.template_id, population_stats: null, population_name: null, voters_fetched: false, @@ -351,6 +348,24 @@ this.fetchPopulationStats() }, computed: { + election_name() { + if (this.population_stats) { + return this.election_timeline_selector.filter(election_type => { + return election_type.value === this.population_stats.election_type; + })[0].text; + } + return null; + }, + stage_name() { + if (this.population_stats) { + switch (this.population_stats.stage) { + case 'l': return "Learning"; + case 'p': return "Performance"; + default: return "Unrecognized stage"; + } + } + return null; + }, population_election_stats_chart_data() { console.log('computing population_election_stats'); let colors = [ @@ -694,7 +709,13 @@ }, fetchPopulationDetails() { this.feedback = 'fetching voters data..'; - axios.get(route('internal.api.population.get.voters', this.population_id)).then((response) => { + axios.get(route( + 'internal.api.population.get.voters', + { + "template":this.template_id, + "population":this.population_id + } + )).then((response) => { this.feedback = 'voters data fetched'; this.voters = response.data; this.voters_fetched = true; @@ -704,7 +725,13 @@ }, fetchPopulationStats() { this.feedback = 'fetching population stats..'; - axios.get(route('internal.api.population.get', this.population_id)).then((response) => { + axios.get(route( + 'internal.api.population.get', + { + "template":this.template_id, + "population":this.population_id + } + )).then((response) => { this.feedback = 'population stats fetched'; this.population_stats = response.data; this.population_name = response.data.name; @@ -728,7 +755,17 @@ this.running_elections_lock = true; this.feedback = 'running ' + type + '-type elections: (' + multi + ')...'; this.last_elections_data = null; - axios.post(route('internal.api.elections.run', this.population_id), {type: type,number:multi}).then((response) => { + axios.post( + route( + 'internal.api.elections.run', + { + "template":this.template_id, + "population":this.population_id + }), + { + type: type, + number:multi + }).then((response) => { this.feedback = 'voting done, fetching updated population stats..'; this.fetchPopulationStats(); console.log(response.data); @@ -740,10 +777,31 @@ }); } }, + switchToPerformanceStage() { + axios.post(route( + 'internal.api.population.stage.update', + { + "template":this.template_id, + "population":this.population_id + } + )).then((response) => { + this.population_stats.stage = 'p'; + this.feedback = "Changed population stage. Only elections that do not alter attributes are available."; + }).catch((err) => { + this.feedback = "Error while changing stage."; + }); + }, fetchMajorityElectionsDistribution() { this.feedback = 'fetching majority distribution...'; this.me_distribution_metadata = null; - axios.get(route('internal.api.majority.distribution.get', this.population_id)).then((response) => { + axios.get( + route( + 'internal.api.majority.distribution.get', + { + "template":this.template_id, + "population":this.population_id + } + )).then((response) => { this.feedback = 'majority distribution fetched'; this.majority_elections_distribution = response.data.distribution; this.majority_elections_distribution_r_10 = response.data.distribution_r_10; @@ -753,7 +811,14 @@ }); }, fetchElectionsTimeline() { - axios.get(route('internal.api.population.get.elections.timeline', this.population_id), { + axios.get( + route( + 'internal.api.population.get.elections.timeline', + { + "template":this.template_id, + "population":this.population_id + } + ), { params: { 'type': this.current_election_timeline_key.value, 'moving_average': this.moving_average diff --git a/resources/js/components/population-template-show.vue b/resources/js/components/population-template-show.vue new file mode 100644 index 0000000..e6a9e62 --- /dev/null +++ b/resources/js/components/population-template-show.vue @@ -0,0 +1,187 @@ +<template> + <div class="p-2"> + <div class="row"> + <div class="col-md-3 col-lg-3"> + + </div> + <div class="col-md-9 col-lg-9"> + <div class="row"> + <div class="alert alert-info col-md-12 col-lg-12" v-if="feedback"> + INFO: {{feedback}} + <button class="float-right btn btn-sm btn-outline-info" @click.prevent="resetFeedback">x</button> + </div> + </div> + <div class="row" v-if="current_template"> + <div class="alert alert-info col-md-12 col-lg-12"> + <div class="card"> + <div class="card-header">Child Populations stats</div> + <div v-if="current_template.child_populations_stats" class="card-body"> + <p> + Number of child populations locked to election type: + </p> + <table class="table table-striped table-borderless"> + <thead> + <th>Action</th> + <th>Type</th> + <th>Description</th> + <th>Total</th> + <th>Learning</th> + <th>Performance</th> + </thead> + <tbody> + <tr v-for="(election_type, key) in current_template.child_populations_stats"> + <td> + <button @click.prevent="addChildPopulation(key)"> + Add child population + </button> + </td> + <td>{{key}}</td> + <td>{{election_type.info}}</td> + <td>{{election_type.count}}</td> + <td>{{election_type.learning_stage_count}}</td> + <td>{{election_type.performance_stage_count}}</td> + </tr> + </tbody> + </table> + </div> + <div v-else class="card-body"><i>N/A</i></div> + </div> + </div> + </div> + <div class="row"> + <div class="alert alert-info col-md-12 col-lg-12"> + <div class="card"> + <div class="card-header">Child Populations</div> + <div v-if="current_template" class="card-body"> + <div class="row"> + <div class="col-md-4 col-lg-4"> + <v-select :options="election_type_selector" + id="election_type_selector" + v-model="current_election_type_key" + placeholder="Choose election type" + label="text"> + </v-select> + </div> + <div class="col-md-8 col-lg-8"> + <button :disabled="!current_election_type_key" + v-on:click.prevent="addChildPopulation" + v-bind:class="{'btn-primary' : current_election_type_key }" + class="btn-xs"> + <i v-if="current_election_type_key">Add child population - bind election type: {{current_election_type_key.text}}</i> + <i v-else>Select election type for child population</i> + </button> + <i class="text-info" v-if="current_election_type_key">{{current_election_type_key.info}})</i> + </div> + </div> + <table class="table table-striped"> + <thead> + <th>Type</th> + <th>Name</th> + <th>Stage</th> + <th v-for="election_type in election_type_selector"> + {{election_type.text}} + </th> + </thead> + <tbody> + <tr v-for="child_population in current_template.child_populations"> + <td>{{child_population.election_type}}</td> + <td> + <a :href="getChildPopulationLink(child_population.id)">{{child_population.name}}</a> + </td> + <td>{{child_population.stage}}</td> + <td v-for="election_type in child_population.elections_stats"> + <span v-if="election_type.count > 0">Count: {{election_type.count}} (average correct: {{election_type.percent_correct.toFixed(2)}})</span> + <i v-else>N/A</i> + </td> + <td>{{child_population.elections_count}}</td> + </tr> + </tbody> + </table> + </div> + <div v-else class="card-body"><i>N/A</i></div> + </div> + </div> + </div> + </div> + </div> + + </div> +</template> + +<script> + + import vSelect from "vue-select"; + import 'vue-select/dist/vue-select.css'; + + export default { + name: "population-template-show", + components: {vSelect}, + data() { + return { + current_election_type_key: null, + election_type_selector : [ + { + value: 'm', + text: 'Majority (m)', + info: 'Majority election, check expertise only' + }, + { + value: 'd1', + text: 'Delegation version 1 (d1)', + info: 'Delegation voting, no adjustments (performance stage)' + }, + { + value: 'd2', + text: 'Delegation version 2 (d2)', + info: 'Delegation voting, include reputation adjustments (learning stage)' + }, + { + value: 'd3', + text: 'Delegation version 3 (d3)', + info: 'Delegation voting, include reputation and voters attributes adjustments (learning stage)' + } + ], + current_template: null, + feedback: null, + template_id: route().params.template_id, + template_name: null + } + }, + mounted() { + this.fetchPopulationTemplate(); + }, + methods: { + resetFeedback() { + this.feedback = null; + }, + fetchPopulationTemplate() { + this.feedback = 'fetching population template details..'; + axios.get(route('internal.api.template.get', this.template_id)).then((response) => { + this.feedback = 'population template details fetched'; + this.current_template = response.data; + }).catch((err) => { + this.feedback = 'population template fetching error'; + }) + }, + addChildPopulation(key) { + this.feedback = 'Adding child population, binding ' + key; + axios.post( + route('internal.api.populations.post', this.current_template.id), + {"election_type":key} + ).then((response) => { + this.feedback = 'Child population added'; + this.fetchPopulationTemplate(); + }).catch((err) => { + this.feedback = 'Adding child population error'; + }) + }, + getChildPopulationLink(populationId) { + return route('population.show', {"template_id":this.current_template.id, "population_id":populationId}); + } + } + } +</script> + +<style scoped> + +</style> diff --git a/resources/views/population-template-details.blade.php b/resources/views/population-template-details.blade.php new file mode 100644 index 0000000..2a3cd05 --- /dev/null +++ b/resources/views/population-template-details.blade.php @@ -0,0 +1,5 @@ +@extends('layouts.app') + +@section('content') + <population-template-show></population-template-show> +@endsection diff --git a/routes/api_internal.php b/routes/api_internal.php index a5f405c..1b34042 100644 --- a/routes/api_internal.php +++ b/routes/api_internal.php @@ -10,24 +10,44 @@ use Illuminate\Support\Facades\Route; | Internal routes 'web' middleware | */ -Route::post('/populations','APIController@generatePopulation') - ->name('internal.api.population.post'); -Route::get('/populations','APIController@getPopulations') - ->name('internal.api.population.index'); -Route::get('/populations/{population}','APIController@getPopulation') +Route::post('/templates','APIController@generatePopulationTemplate') + ->name('internal.api.templates.post'); + +Route::get('/templates','APIController@getPopulationTemplates') + ->name('internal.api.templates.index'); + +Route::get('/templates/{template}','APIController@getPopulationTemplate') + ->name('internal.api.template.get'); + +Route::post('/templates/{template}/populations','APIController@getChildPopulations') + ->name('internal.api.populations.index'); + +Route::post('/templates/{template}/populations','APIController@generateChildPopulation') + ->name('internal.api.populations.post'); + +Route::get('/templates/{template}/populations/{population}','APIController@getPopulation') ->name('internal.api.population.get'); -Route::get('/populations/{population}/voters','APIController@getVotersStats') + +Route::get('/templates/{template}/populations/{population}/voters','APIController@getVotersStats') ->name('internal.api.population.get.voters'); -Route::get('/populations/{population}/timeline','APIController@getElectionsTimeline') + +Route::get('/templates/{template}/populations/{population}/timeline','APIController@getElectionsTimeline') ->name('internal.api.population.get.elections.timeline'); -Route::get('/populations/{population}/majority-distribution','APIController@getMajorityElectionsDistribution') + +Route::get('/templates/{template}/populations/{population}/weights','APIController@getWeightsTimeline') + ->name('internal.api.population.get.weights.timeline'); + +Route::get('/templates/{template}/populations/{population}/majority-distribution','APIController@getMajorityElectionsDistribution') ->name('internal.api.majority.distribution.get'); -Route::post('/populations/{population}','APIController@runMajorityElections') +Route::post('/templates/{template}/populations/{population}','APIController@runMajorityElections') ->name('internal.api.population.majority.run'); -Route::post('/populations/{population}/elections','APIController@runElections') +Route::post('/templates/{template}/populations/{population}/stage','APIController@changeToPerformanceStage') + ->name('internal.api.population.stage.update'); + +Route::post('/templates/{template}/populations/{population}/elections','APIController@runElections') ->name('internal.api.elections.run'); diff --git a/routes/web.php b/routes/web.php index 0688de8..33b7152 100644 --- a/routes/web.php +++ b/routes/web.php @@ -31,4 +31,6 @@ Auth::routes(); Route::get('/home', 'HomeController@index')->name('home'); -Route::get('/populations/{population_id}', 'HomeController@showPopulation')->name('population.show'); +Route::get('/templates/{template_id}', 'HomeController@showPopulationTemplate')->name('template.show'); + +Route::get('/templates/{template_id}/populations/{population_id}', 'HomeController@showPopulation')->name('population.show'); -- GitLab