diff --git a/app/Http/Controllers/BlockListController.php b/app/Http/Controllers/BlockListController.php index 3f30aa5f..1faac287 100644 --- a/app/Http/Controllers/BlockListController.php +++ b/app/Http/Controllers/BlockListController.php @@ -3,8 +3,11 @@ namespace App\Http\Controllers; use App\Models\Course; +use App\Models\User; use App\Util\HtmlString; +use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Auth; class BlockListController extends Controller { @@ -20,14 +23,24 @@ public function index(Course $course) } /** - * Display the crib page which visualizes connections between blocks and requirements. + * Display the crib page which visualizes connections between blocks and requirements, as well as observation assignments. * + * @param Request $request * @param Course $course + * @param User $user * @return Response */ - public function crib(Course $course) + public function crib(Request $request, Course $course, User $user) { - return view('crib', ['blockManagementLink' => $this->blockManagementLink($course, 't.views.crib.here')]); + $userId = $user->id ?? Auth::id(); + $request->session()->flash('return_url', $request->url()); + return view('crib', [ + 'blockManagementLink' => $this->blockManagementLink($course, 't.views.crib.here'), + 'showObservationAssignments' => $course->observationAssignments()->count(), + 'userId' => $userId, + 'trainerObservationAssignments' => $course->observationAssignmentsPerUserAndPerBlock()[$userId] ?? [], + 'neededObservations' => 1, + ]); } /** diff --git a/app/Http/Controllers/ObservationAssignmentController.php b/app/Http/Controllers/ObservationAssignmentController.php new file mode 100644 index 00000000..3a419dd7 --- /dev/null +++ b/app/Http/Controllers/ObservationAssignmentController.php @@ -0,0 +1,109 @@ +validated(); + + DB::transaction(function() use ($request,$course, $data){ + + $observationAssignment = ObservationAssignment::create(array_merge($data, ['course_id' => $course->id])); + + $observationAssignment->participants()->attach(array_filter(explode(',', $data['participants']))); + $observationAssignment->blocks()->attach(array_filter(explode(',', $data['blocks']))); + $observationAssignment->users()->attach(array_filter(explode(',', $data['users']))); + + $request->session()->flash('alert-success', __('t.views.admin.observation_assignments.create_success')); + }); + + return Redirect::route('admin.observationAssignments', ['course' => $course->id]); + + } + + + /** + * Show the form for editing the specified resource. + * @param Course $course + * @param ObservationAssignment $observationAssignment + * @return \Illuminate\Http\Response + */ + public function edit(Course $course, ObservationAssignment $observationAssignment) + { + return view('admin.observationAssignments.edit', ['observationAssignment' => $observationAssignment]); + } + + /** + * Update the specified resource in storage. + * + * + * @param ObservationAssignmentRequest $request + * @param Course $course + * @param ObservationAssignment $observationAssignment + * @return RedirectResponse + */ + public function update(ObservationAssignmentRequest $request, Course $course, ObservationAssignment $observationAssignment) + { + DB::transaction(function () use ($request, $course, $observationAssignment) { + $data = $request->validated(); + + $observationAssignment->update($data); + $observationAssignment->participants()->detach(null); + $observationAssignment->participants()->attach(array_filter(explode(',', $data['participants']))); + $observationAssignment->blocks()->detach(null); + $observationAssignment->blocks()->attach(array_filter(explode(',', $data['blocks']))); + $observationAssignment->users()->detach(null); + $observationAssignment->users()->attach(array_filter(explode(',', $data['users']))); + $request->session()->flash('alert-success', __('t.views.admin.observation_assignments.edit_success')); + }); + return Redirect::route('admin.observationAssignments', ['course' => $course->id]); + + } + + /** + * Remove the specified resource from storage. + * + * @param Request $request + * @param Course $course + * @param ObservationAssignment $observationAssignment + * @return RedirectResponse + * + */ + public function destroy(Request $request, Course $course, ObservationAssignment $observationAssignment) + { + $observationAssignment->delete(); + $request->session()->flash('alert-success', __('t.views.admin.observation_assignments.delete_success')); + return Redirect::route('admin.observationAssignments', ['course' => $course->id]); + + } +} diff --git a/app/Http/Controllers/ObservationController.php b/app/Http/Controllers/ObservationController.php index 330dd176..ac36ebcb 100644 --- a/app/Http/Controllers/ObservationController.php +++ b/app/Http/Controllers/ObservationController.php @@ -25,6 +25,7 @@ class ObservationController extends Controller { * @return Response */ public function create(Request $request) { + $this->rememberPreviouslyActiveView($request); return view('observation.new', ['participants' => $request->input('participant'), 'block' => $request->input('block')]); } @@ -51,13 +52,14 @@ public function store(ObservationRequest $request, Course $course) { $participant = $observation->participants()->first(); $route = route('participants.detail', ['course' => $course->id, 'participant' => $participant->id]); $flash->s(" ") - ->__('t.views.observations.back_to_participant', ['name' => $participant->scout_name]) + ->__('t.views.observations.go_to_participant', ['name' => $participant->scout_name]) ->s(' '); } + $request->session()->flash('alert-success', $flash); }); - return Redirect::route('observation.new', ['course' => $course->id, 'participant' => $data['participants'], 'block' => $data['block']]); + return $this->redirectToPreviouslyActiveView($request, $course, collect([]), route('observation.new', ['course' => $course->id, 'participant' => $data['participants'], 'block' => $data['block']])); } /** @@ -80,6 +82,7 @@ public function edit(Request $request, Course $course, Observation $observation) */ protected function rememberPreviouslyActiveView(Request $request) { $returnTo = $this->extractPathParameter(URL::previous(), 'participants.detail', 'participant'); + $request->session()->keep(['return_url']); $request->session()->flash('participant_before_edit', $request->session()->get('participant_before_edit', $returnTo)); } @@ -90,15 +93,19 @@ protected function rememberPreviouslyActiveView(Request $request) { * @param Request $request * @param Course $course * @param Collection $returnOptions a collection of participant ids that are legal to be viewed + * @param string|null $fallback * @return RedirectResponse */ - protected function redirectToPreviouslyActiveView(Request $request, Course $course, Collection $returnOptions) { + protected function redirectToPreviouslyActiveView(Request $request, Course $course, Collection $returnOptions, $fallback = null) { + if ($request->session()->has('return_url')) return Redirect::to($request->session()->get('return_url')); + $returnTo = $request->session()->get('participant_before_edit'); if (!$returnOptions->contains($returnTo)) { $returnTo = $returnOptions->first(); } - return Redirect::to(route('participants.detail', ['course' => $course->id, 'participant' => $returnTo])); + if ($returnTo) return Redirect::to(route('participants.detail', ['course' => $course->id, 'participant' => $returnTo])); + return Redirect::to($fallback ?? URL::previous()); } /** diff --git a/app/Http/Controllers/ParticipantDetailController.php b/app/Http/Controllers/ParticipantDetailController.php index 79633258..8e627eef 100644 --- a/app/Http/Controllers/ParticipantDetailController.php +++ b/app/Http/Controllers/ParticipantDetailController.php @@ -2,12 +2,9 @@ namespace App\Http\Controllers; -use App\Models\Category; use App\Models\Course; use App\Models\Observation; use App\Models\Participant; -use App\Models\Quali; -use App\Models\Requirement; use Illuminate\Http\Request; use Illuminate\Http\Response; diff --git a/app/Http/Requests/ObservationAssignmentRequest.php b/app/Http/Requests/ObservationAssignmentRequest.php new file mode 100644 index 00000000..0cb9e10a --- /dev/null +++ b/app/Http/Requests/ObservationAssignmentRequest.php @@ -0,0 +1,38 @@ + 'required|max:1023', + 'blocks' => 'required|regex:/^\d+(,\d+)*$/|allExistInCourse', + 'participants' => 'required|regex:/^\d+(,\d+)*$/|allExistInCourse', + 'users' => 'required|regex:/^\d+(,\d+)*$/|allExistInCourse:' . Trainer::class . ',user_id' + ]; + } + + + public function attributes() { + return Lang::get('t.models.observation_assignment'); + } +} diff --git a/app/Models/Block.php b/app/Models/Block.php index a2d887f1..38eff96f 100644 --- a/app/Models/Block.php +++ b/app/Models/Block.php @@ -18,6 +18,8 @@ * @property Collection $requirement_ids * @property Course $course * @property Observation[] $observations + * @property ObservationAssignment[] $observationAssignments + * @property Collection $requirements * @property Collection $requirementIds * @property int $num_observations */ @@ -61,6 +63,52 @@ public function course() { public function observations() { return $this->hasMany('App\Models\Observation'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function observationsWithParticipants(){ + + return $this->hasMany('App\Models\Observation')->with('participants'); + + } + + + public function observationsMultipleParticipantsId(){ + + return $this->hasMany('App\Models\Observation')->with('participants:id'); + + + } + public function observationsMultipleParticipants(){ + return $this->observationsMultipleParticipantsId(); + + foreach ($o as $obs){ + $obj=array_push($obs->participants->map(function ($o){ + return $o->pluck('id'); + })); + } + return $obj; + $obj->participants->map(function ($participant){return $participant->pluck('id');}); + } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function observationAssignments() { + return $this->belongsToMany('App\Models\ObservationAssignment', 'observation_assignment_blocks')->with('users', 'participants'); + } + + + public function observationAssignmentsPerUser() { + return $this->course->participants()->select(['users.id as user_id', 'participants.*'])->distinct() + ->join('observation_assignment_participants', 'participants.id', 'observation_assignment_participants.participant_id') + ->join('observation_assignment_users', 'observation_assignment_participants.observation_assignment_id', 'observation_assignment_users.observation_assignment_id') + ->join('observation_assignment_blocks', 'observation_assignment_participants.observation_assignment_id', 'observation_assignment_blocks.observation_assignment_id') + ->join('users', 'users.id', 'observation_assignment_users.user_id') + ->join('trainers', 'users.id', 'trainers.user_id') + ->mergeConstraintsFrom($this->course->users()->getQuery()) + ->where('observation_assignment_blocks.block_id', $this->id)->get() + ->groupBy('user_id'); + } /** * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany diff --git a/app/Models/Course.php b/app/Models/Course.php index 9e0c7264..29b82429 100644 --- a/app/Models/Course.php +++ b/app/Models/Course.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; /** * @property int $id @@ -25,6 +26,8 @@ class Course extends Model { * @var array */ protected $fillable = ['name', 'course_number', 'archived']; + protected $observationAssignments; + /** * @return \Illuminate\Database\Eloquent\Relations\HasMany @@ -60,7 +63,51 @@ public function participantGroups() { return $this->hasMany('App\Models\ParticipantGroup', 'course_id'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function observationAssignments() + { + return $this->hasMany('App\Models\ObservationAssignment', 'course_id'); + } + + public function observationAssignmentsPerUserAndPerBlock() { + if (!$this->observationAssignments) { + $observationAssignmentsQuery = ObservationAssignment::select([ + 'users.id as user_id', + 'observation_assignment_blocks.block_id as block_id', + DB::raw('COUNT(DISTINCT observations.id) as observation_count'), + 'participants.id as participant_id' + ])->distinct() + ->join('observation_assignment_participants', 'observation_assignments.id', 'observation_assignment_participants.observation_assignment_id') + ->join('observation_assignment_users', 'observation_assignments.id', 'observation_assignment_users.observation_assignment_id') + ->join('observation_assignment_blocks', 'observation_assignments.id', 'observation_assignment_blocks.observation_assignment_id') + ->join('users', 'users.id', 'observation_assignment_users.user_id') + ->join('participants', 'participants.id', 'observation_assignment_participants.participant_id') + ->leftJoin('observations_participants', 'participants.id', 'observations_participants.participant_id') + ->leftJoin('observations', function($join) { + $join->on('observations.id', 'observations_participants.observation_id'); + $join->on('observations.block_id', 'observation_assignment_blocks.block_id'); + $join->on('observations.user_id', 'users.id'); + }) + ->join('trainers', 'users.id', 'trainers.user_id') + ->mergeConstraintsFrom($this->users()->getQuery()) + ->groupBy('user_id', 'block_id', 'participant_id'); + $this->observationAssignments = $this->participants()->select([ + 'query.user_id as user_id', + 'query.block_id as block_id', + 'query.observation_count as observation_count', + 'participants.*' + ])->joinSub($observationAssignmentsQuery, 'query', function ($join) { + $join->on('participants.id', 'query.participant_id'); + })->get() + ->groupBy('user_id') + ->map->groupBy('block_id'); + } + + return $this->observationAssignments; + } /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ diff --git a/app/Models/ObservationAssignment.php b/app/Models/ObservationAssignment.php new file mode 100644 index 00000000..6b85fb42 --- /dev/null +++ b/app/Models/ObservationAssignment.php @@ -0,0 +1,46 @@ +belongsToMany('App\Models\Block', 'observation_assignment_blocks'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function participants() + { + return $this->belongsToMany('App\Models\Participant', 'observation_assignment_participants'); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function users() + { + return $this->belongsToMany('App\Models\User', 'observation_assignment_users'); + } +} diff --git a/app/Models/Participant.php b/app/Models/Participant.php index 771b06b2..5103e5cf 100644 --- a/app/Models/Participant.php +++ b/app/Models/Participant.php @@ -36,7 +36,7 @@ class Participant extends Model { * * @var array */ - protected $appends = ['num_observations', 'image_path']; + protected $appends = ['num_observations', 'image_path', 'name_and_group']; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo @@ -66,6 +66,14 @@ public function qualis() { return $this->hasMany(Quali::class); } + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function observationAssignments() + { + return $this->hasMany('App\Models\ObservationAssignment'); + } + /** * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ @@ -86,6 +94,10 @@ public function getNegativeAttribute() { return $this->observations()->where('impression', '=', '0'); } + public function getNameAndGroupAttribute() { + return $this->group ? $this->scout_name . ' (' . $this->group . ')' : $this->scout_name; + } + /** * Get the number of observations grouped by the users that created the observation. * diff --git a/app/Models/User.php b/app/Models/User.php index cf6cff9a..b36a552e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -70,6 +70,15 @@ public function observations() return $this->hasMany('App\Models\Observation'); } + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function observationAssignments() + { + return $this->belongsToMany('App\Models\ObservationAssignment', 'observation_assignment_users'); + } + + /** * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ @@ -121,4 +130,5 @@ public function getLastUsedBlockDate(Course $course) { public function setLastUsedBlockDate($value, Course $course) { $this->courses()->updateExistingPivot($course->id, ['last_used_block_date' => Carbon::parse($value)]); } + } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 58683d7d..179e9c64 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -67,6 +67,11 @@ public function boot() $course = $route->parameter('course'); return $course->participantGroups()->findOrFail($id); }); + Route::bind('observationAssignment', function($id, \Illuminate\Routing\Route $route) { + /** @var Course $course */ + $course = $route->parameter('course'); + return $course->observationAssignments()->findOrFail($id); + }); Route::bind('quali_data', function($id, \Illuminate\Routing\Route $route) { /** @var Course $course */ $course = $route->parameter('course'); diff --git a/app/Services/Import/Blocks/BlockListImporter.php b/app/Services/Import/Blocks/BlockListImporter.php old mode 100755 new mode 100644 diff --git a/app/Services/Import/Blocks/BlockListParser.php b/app/Services/Import/Blocks/BlockListParser.php old mode 100755 new mode 100644 diff --git a/app/Services/Import/Blocks/ECamp2/ECamp2BlockOverviewImporter.php b/app/Services/Import/Blocks/ECamp2/ECamp2BlockOverviewImporter.php old mode 100755 new mode 100644 diff --git a/app/Services/Import/Blocks/ECamp2/ECamp2BlockOverviewParser.php b/app/Services/Import/Blocks/ECamp2/ECamp2BlockOverviewParser.php old mode 100755 new mode 100644 diff --git a/app/Services/Import/Participants/MiData/MiDataParticipantListImporter.php b/app/Services/Import/Participants/MiData/MiDataParticipantListImporter.php old mode 100755 new mode 100644 diff --git a/app/Services/Import/Participants/MiData/MiDataParticipantListParser.php b/app/Services/Import/Participants/MiData/MiDataParticipantListParser.php old mode 100755 new mode 100644 diff --git a/app/Services/Import/Participants/ParticipantListImporter.php b/app/Services/Import/Participants/ParticipantListImporter.php old mode 100755 new mode 100644 diff --git a/app/Services/Import/Participants/ParticipantListParser.php b/app/Services/Import/Participants/ParticipantListParser.php old mode 100755 new mode 100644 diff --git a/app/Services/Translator.php b/app/Services/Translator.php old mode 100755 new mode 100644 diff --git a/cypress/integration/observations.spec.js b/cypress/integration/observations.spec.js index 23bcc44a..f02e76a7 100644 --- a/cypress/integration/observations.spec.js +++ b/cypress/integration/observations.spec.js @@ -36,7 +36,7 @@ describe('observation form', () => { cy.contains('Speichern').click() cy.contains('Beobachtung erfasst.') - cy.contains('Zurück zu').click() + cy.contains('Zu ').click() cy.contains('hat sich mehrmals gut eingebracht') }) diff --git a/database/migrations/2020_09_04_145745_create_observation_assignments_table.php b/database/migrations/2020_09_04_145745_create_observation_assignments_table.php new file mode 100644 index 00000000..04a47b87 --- /dev/null +++ b/database/migrations/2020_09_04_145745_create_observation_assignments_table.php @@ -0,0 +1,43 @@ +integer('id', true); + $table->integer('course_id')->nullable(false); + $table->string('name')->nullable(false); + $table->timestamps(); + }); + Schema::table('observation_assignments', function(Blueprint $table) + { + $table->foreign('course_id', 'fk_course_observation_assignment')->references('id')->on('courses')->onUpdate('CASCADE')->onDelete('CASCADE'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('observation_assignments', function(Blueprint $table) + { + $table->dropForeign('fk_course_observation_assignment'); + + }); + Schema::dropIfExists('observation_assignments'); + } +} diff --git a/database/migrations/2020_09_04_193813_create_observation_assignment_blocks.php b/database/migrations/2020_09_04_193813_create_observation_assignment_blocks.php new file mode 100644 index 00000000..356ef7ed --- /dev/null +++ b/database/migrations/2020_09_04_193813_create_observation_assignment_blocks.php @@ -0,0 +1,42 @@ +integer('observation_assignment_id')->nullable(false); + $table->integer('block_id')->nullable(false); + }); + + Schema::table('observation_assignment_blocks', function (Blueprint $table) { + $table->foreign('observation_assignment_id', 'fk_order_observation_assignment_block')->references('id')->on('observation_assignments')->onUpdate('CASCADE')->onDelete('CASCADE'); + $table->foreign('block_id', 'fk_block_observation_assignment_block')->references('id')->on('blocks')->onUpdate('CASCADE')->onDelete('CASCADE'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('observation_assignment_blocks', function(Blueprint $table) + { + $table->dropForeign('fk_block_observation_assignment_block'); + $table->dropForeign('fk_order_observation_assignment_block'); + }); + Schema::dropIfExists('observation_assignment_blocks'); + } +} diff --git a/database/migrations/2020_09_04_193818_create_observation_assignment_users.php b/database/migrations/2020_09_04_193818_create_observation_assignment_users.php new file mode 100644 index 00000000..c304dcda --- /dev/null +++ b/database/migrations/2020_09_04_193818_create_observation_assignment_users.php @@ -0,0 +1,42 @@ +integer('observation_assignment_id')->nullable(false); + $table->integer('user_id')->nullable(false); + }); + + Schema::table('observation_assignment_users', function (Blueprint $table) { + $table->foreign('observation_assignment_id', 'fk_order_observation_assignment_user')->references('id')->on('observation_assignments')->onUpdate('CASCADE')->onDelete('CASCADE'); + $table->foreign('user_id', 'fk_user_observation_assignment_user')->references('id')->on('users')->onUpdate('CASCADE')->onDelete('CASCADE'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('observation_assignment_users', function(Blueprint $table) + { + $table->dropForeign('fk_user_observation_assignment_user'); + $table->dropForeign('fk_order_observation_assignment_user'); + }); + Schema::dropIfExists('observation_assignment_users'); + } +} diff --git a/database/migrations/2020_09_04_193823_create_observation_assignment_participants.php b/database/migrations/2020_09_04_193823_create_observation_assignment_participants.php new file mode 100644 index 00000000..facdacee --- /dev/null +++ b/database/migrations/2020_09_04_193823_create_observation_assignment_participants.php @@ -0,0 +1,42 @@ +integer('observation_assignment_id')->nullable(false); + $table->integer('participant_id')->nullable(false); + }); + + Schema::table('observation_assignment_participants', function (Blueprint $table) { + $table->foreign('observation_assignment_id', 'fk_order_observation_assignment_participant')->references('id')->on('observation_assignments')->onUpdate('CASCADE')->onDelete('CASCADE'); + $table->foreign('participant_id', 'fk_participant_observation_assignment_participant')->references('id')->on('participants')->onUpdate('CASCADE')->onDelete('CASCADE'); + + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('observation_assignment_participants', function(Blueprint $table) + { + $table->dropForeign('fk_participant_observation_assignment_participant'); + $table->dropForeign('fk_order_observation_assignment_participant'); + }); + Schema::dropIfExists('observation_assignment_participants'); + } +} diff --git a/resources/js/components/FormBasic.vue b/resources/js/components/FormBasic.vue index f8710526..271f3f4f 100644 --- a/resources/js/components/FormBasic.vue +++ b/resources/js/components/FormBasic.vue @@ -1,7 +1,7 @@ @@ -27,8 +27,11 @@ export default { method() { return this.routeMethod(...this.arrayAction) }, + methodIsNotGet() { + return this.method !== 'GET' + }, formMethod() { - return (this.method && this.method === 'GET') ? 'GET' : 'POST' + return this.methodIsNotGet ? 'POST' : 'GET' }, } } diff --git a/resources/lang/de/t.php b/resources/lang/de/t.php index 083ea8fc..ed94b9b9 100644 --- a/resources/lang/de/t.php +++ b/resources/lang/de/t.php @@ -93,6 +93,12 @@ "requirements" => "Anforderungen", "user" => "Beobachter", ), + "observation_assignment" => array( + "blocks" => "Blöcke", + "name" => "Beobachtungsauftrag", + "participants" => "TN", + "users" => "Equipe", + ), "participant" => array( "group" => "Abteilung", "image" => "Bild", @@ -230,6 +236,25 @@ "menu_name" => "Neuen Kurs erstellen", "title" => "Neuen Kurs erstellen", ), + "observation_assignments" => array( + "are_observation_assignments_required" => array( + "answer" => "Nein, aber sie sind ein nützliches Tool beim RQF (Rückmelde-, Qualifizierungs-, Förderungs-) Prozess.", + "question" => "Muss ich Beobachtungsaufträge für meinen Kurs erfassen?", + ), + "create_success" => "Beobachtungsauftrag wurde erfolgreich erstellt.", + "delete_success" => "Beobachtungsauftrag wurde erfolgreich gelöscht.", + "edit" => "Beobachtungsauftrag bearbeiten", + "edit_success" => "Beobachtungsauftrag wurde erfolgreich geändert.", + "existing" => "Beobachtungsaufträge :courseName", + "menu_name" => "Beobachtungsaufträge", + "new" => "Neuer Beobachtungsauftrag", + "no_observation_assignment" => "Bisher sind keine Beobachtungsaufträge erfasst.", + "really_delete" => "Willst du den Beobachtungsauftrag \":name\" wirklich löschen?", + "what_are_observation_assignments" => array( + "answer" => "Viele Equipen definieren, wer wann wen beobachten sollte. Diese Aufträge könnt ihr hier definieren und seht diese (und ob sie schon erfüllt sind) z.Bsp. beim Spick.", + "question" => "Was sind Beobachtungsaufträge?", + ), + ), "participants" => array( "add_success" => "TN \":name\" erfolgreich erfasst.", "edit" => "TN ändern", @@ -348,6 +373,7 @@ "question" => "Siehst du nur leere Blöcke ohne Anforderungen?", ), "title" => "Welche Anforderungen können in welchen Blöcken beobachtet werden", + "view_as" => "Aus Sicht von", ), "error_form" => array( "back" => "Zurück zu wo ich gerade noch war...", @@ -381,7 +407,7 @@ ), "observations" => array( "add_success" => "Beobachtung erfasst. Mässi!", - "back_to_participant" => "Zurück zu :name", + "go_to_participant" => "Zu :name", "edit" => "Beobachtung bearbeiten", "edit_success" => "Beobachtung aktualisiert.", "new" => "Beobachtung erfassen", diff --git a/resources/lang/fr/t.php b/resources/lang/fr/t.php index 40324752..60ee481d 100644 --- a/resources/lang/fr/t.php +++ b/resources/lang/fr/t.php @@ -291,7 +291,7 @@ ), "observations" => array( "add_success" => "Observation saisie. Merci!", - "back_to_participant" => "Revenir à :name", + "go_to_participant" => "Venir à :name", "edit" => "Modifier l'observation", "edit_success" => "Observation actualisée", "new" => "Créer une observation", diff --git a/resources/sass/app.scss b/resources/sass/app.scss index ff6af33b..24f6f62e 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -98,6 +98,12 @@ } } +.text-overflow-ellipsis{ + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + .bg-success-light { background-color: #d7f3e3; } diff --git a/resources/views/admin/observationAssignments/edit.blade.php b/resources/views/admin/observationAssignments/edit.blade.php new file mode 100644 index 00000000..80bde370 --- /dev/null +++ b/resources/views/admin/observationAssignments/edit.blade.php @@ -0,0 +1,58 @@ +@extends('layouts.default') + +@section('content') + + + + + + + + + + + + + @php + $days = $course->blocks->mapToGroups(function($block) { + return [$block->block_date->formatLocalized(__('t.global.date_format')) => $block->id]; + })->map(function($ids, $date) { + return implode(',', $ids->all()); + }); + @endphp + + + + + + + + + +@endsection diff --git a/resources/views/admin/observationAssignments/index.blade.php b/resources/views/admin/observationAssignments/index.blade.php new file mode 100644 index 00000000..5c888c74 --- /dev/null +++ b/resources/views/admin/observationAssignments/index.blade.php @@ -0,0 +1,94 @@ +@extends('layouts.default') + +@section('content') + + + + + + + + + + + + @php + $days = $course->blocks->mapToGroups(function($block) { + return [$block->block_date->formatLocalized(__('t.global.date_format')) => $block->id]; + })->map(function($ids, $date) { + return implode(',', $ids->all()); + }); + @endphp + + + + + + @component('components.help-text', ['id' => 'requirementsHelp', 'key' => 't.views.admin.observation_assignments.what_are_observation_assignments'])@endcomponent + + + + + + + + + + + @if (count($course->observationAssignments)) + + + + @else + + {{__('t.views.admin.observation_assignments.no_observation_assignment')}} + + @component('components.help-text', ['id' => 'noGroupsHelp', 'key' => 't.views.admin.observation_assignments.are_observation_assignments_required'])@endcomponent + + @endif + + + +@endsection diff --git a/resources/views/admin/participantGroups/index.blade.php b/resources/views/admin/participantGroups/index.blade.php index 337f2188..cf4b89e0 100644 --- a/resources/views/admin/participantGroups/index.blade.php +++ b/resources/views/admin/participantGroups/index.blade.php @@ -37,7 +37,7 @@ :data="{{ json_encode($course->participantGroups) }}" :fields="[ { label: $t('t.models.participant_group.group_name'), value: participantGroup => participantGroup.group_name }, - { label: $t('t.models.participant_group.group_name'), value: participantGroup => participantGroup.participant_names }, + { label: $t('t.models.participant_group.participants'), value: participantGroup => participantGroup.participant_names }, ]" :actions="{ edit: participantGroup => routeUri('admin.participantGroups.edit', {course: {{ $course->id }}, participantGroup: participantGroup.id}), diff --git a/resources/views/crib.blade.php b/resources/views/crib.blade.php index d9d06015..036f918a 100644 --- a/resources/views/crib.blade.php +++ b/resources/views/crib.blade.php @@ -14,6 +14,20 @@ } @endphp + @if($showObservationAssignments) +
+ + +
+ @endif + @foreach($days as $day) block_date->timestamp }}> @@ -25,22 +39,47 @@ archived || $day[0]->block_date->gt(\Carbon\Carbon::now()->subDays(2))) ? 'visible' : '' }}> @foreach ($day as $block) - - {{ $block->blockname_and_number }} - @if(count($block->mandatory_requirements)) -
- {{__('t.views.crib.mandatory_requirements')}}: - @foreach($block->mandatory_requirements as $requirement) - {{$requirement->content}} - @endforeach - @endif - @if(count($block->non_mandatory_requirements)) -
- {{__('t.views.crib.non_mandatory_requirements')}}: - @foreach($block->non_mandatory_requirements as $requirement) - {{$requirement->content}} - @endforeach - @endif + + + +
{{ $block->blockname_and_number }}
+ @if(count($block->mandatory_requirements)) + {{__('t.views.crib.mandatory_requirements')}}: + @foreach($block->mandatory_requirements as $requirement) + {{$requirement->content}} + @endforeach + @endif + @if(count($block->non_mandatory_requirements)) +
+ {{__('t.views.crib.non_mandatory_requirements')}}: + @foreach($block->non_mandatory_requirements as $requirement) + {{$requirement->content}} + @endforeach + @endif +
+ @if($showObservationAssignments && isset($trainerObservationAssignments[$block->id])) + +
+ @foreach($trainerObservationAssignments[$block->id] as $participant) + + @endforeach +
+
+ @endif + +
+
@endforeach diff --git a/resources/views/includes/header.blade.php b/resources/views/includes/header.blade.php index 0fcc7b85..c11a1150 100644 --- a/resources/views/includes/header.blade.php +++ b/resources/views/includes/header.blade.php @@ -58,6 +58,8 @@ @if(!$course->archived) {{__('t.views.admin.participant_groups.menu_name')}} + {{__('t.views.admin.observation_assignments.menu_name')}} @endif {{__('t.views.admin.qualis.menu_name')}} diff --git a/routes/web.php b/routes/web.php index b5e61cdc..c5cae5da 100644 --- a/routes/web.php +++ b/routes/web.php @@ -24,7 +24,7 @@ Route::get('/course/{course}', 'HomeController@index')->name('index'); Route::get('/course/{course}/blocks', 'BlockListController@index')->name('blocks'); - Route::get('/course/{course}/crib', 'BlockListController@crib')->name('crib'); + Route::get('/course/{course}/crib/{user?}', 'BlockListController@crib')->name('crib'); Route::middleware('courseNotArchived')->group(function () { Route::get('/course/{course}/participants', 'ParticipantListController@index')->name('participants'); @@ -72,10 +72,13 @@ Route::post('/course/{course}/admin/participantGroups/{participantGroup}', 'ParticipantGroupController@update')->name('admin.participantGroups.update'); Route::delete('/course/{course}/admin/participantGroups/{participantGroup}', 'ParticipantGroupController@destroy')->name('admin.participantGroups.delete'); + Route::get('/course/{course}/admin/observationAssignments', 'ObservationAssignmentController@index')->name('admin.observationAssignments'); + Route::post('/course/{course}/admin/observationAssignments', 'ObservationAssignmentController@store')->name('admin.observationAssignments.store'); + Route::get('/course/{course}/admin/observationAssignments/{observationAssignment}', 'ObservationAssignmentController@edit')->name('admin.observationAssignments.edit'); + Route::post('/course/{course}/admin/observationAssignments/{observationAssignment}', 'ObservationAssignmentController@update')->name('admin.observationAssignments.update'); + Route::delete('/course/{course}/admin/observationAssignments/{observationAssignment}', 'ObservationAssignmentController@destroy')->name('admin.observationAssignments.delete'); }); - - Route::get('/course/{course}/admin/blocks', 'BlockController@index')->name('admin.blocks'); Route::post('/course/{course}/admin/blocks', 'BlockController@store')->name('admin.block.store'); Route::get('/course/{course}/admin/blocks/import', 'BlockController@upload')->name('admin.block.upload'); diff --git a/tests/Feature/Admin/ObservationAssignment/CreateObservationAssignmentTest.php b/tests/Feature/Admin/ObservationAssignment/CreateObservationAssignmentTest.php new file mode 100644 index 00000000..930e97b1 --- /dev/null +++ b/tests/Feature/Admin/ObservationAssignment/CreateObservationAssignmentTest.php @@ -0,0 +1,465 @@ +payload = ['name' => 'Auftrag 1', 'participants' => '' . $this->participantId, 'users' => '' . Auth::id(), 'blocks' => '' . $this->blockId]; + } + + public function test_shouldRequireLogin() + { + // given + auth()->logout(); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $this->payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/login'); + } + + public function test_shouldRequireNonArchivedCourse() + { + // given + Course::find($this->courseId)->update(['archived' => true]); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $this->payload); + + // then + $response->assertStatus(302); + $response->assertRedirect(route('admin.course', ['course' => $this->courseId])); + } + + public function test_shouldCreateAndDisplayObservationAssignment() + { + // given + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $this->payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments'); + /** @var TestResponse $response */ + $response = $response->followRedirects(); + $response->assertSee('Beobachtungsauftrag wurde erfolgreich erstellt.'); + } + + public function test_shouldValidateNewObservationAssignment_noName() + { + // given + $payload = $this->payload; + unset($payload['name']); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Beobachtungsauftrag muss ausgefüllt sein.', $exception->validator->errors()->first('name')); + } + + public function test_shouldValidateNewObservationAssignment_longContent() + { + // given + $payload = $this->payload; + $payload['name'] = 'Unglaublich langer Auftragsname. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr.'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Beobachtungsauftrag darf maximal 1023 Zeichen haben.', $exception->validator->errors()->first('name')); + } + + public function test_shouldValidateNewObservationAssignment_noParticipantIds() + { + // given + $payload = $this->payload; + unset($payload['participants']); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('TN muss ausgefüllt sein.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateNewObservationAssignment_invalidParticipantIds() + { + // given + $payload = $this->payload; + $payload['participants'] = 'a'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('TN Format ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateNewObservationAssignment_oneValidParticipantId() + { + // given + $payload = $this->payload; + $participantId = $this->createParticipant(); + $payload['participants'] = $participantId; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments'); + $this->assertEquals([$participantId], ObservationAssignment::latest()->first()->participants->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignment_multipleValidParticipantIds() + { + // given + $payload = $this->payload; + $participantIds = [$this->createParticipant(), $this->createParticipant()]; + $payload['participants'] = implode(',', $participantIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments'); + $this->assertEquals($participantIds, ObservationAssignment::latest()->first()->participants->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignment_someNonexistentParticipantIds() + { + // given + $payload = $this->payload; + $participantIds = [$this->createParticipant(), '999999', $this->createParticipant()]; + $payload['participants'] = implode(',', $participantIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für TN ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateNewObservationAssignment_someInvalidParticipantIds() + { + // given + $payload = $this->payload; + $participantIds = [$this->createParticipant(), 'abc', $this->createParticipant()]; + $payload['participants'] = implode(',', $participantIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('TN Format ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldShowEscapedNotice_afterCreatingObservationAssignment() + { + // given + $participantName = 'Participant name with \'some" formatting'; + $payload = $this->payload; + $payload['participants'] = $this->createParticipant($participantName); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload)->followRedirects(); + + // then + $response->assertDontSee($participantName, false); + $response->assertSee('<b>Participant name<\/b> with 'some\" formatting', false); + } + + public function test_shouldNotAllowCreatingObservationAssignment_withParticipantFromADifferentCourse() + { + // given + $differentCourse = $this->createCourse('Other course', '', false); + $participantFromDifferentCourse = $this->createParticipant('Foreign', $differentCourse); + $payload = $this->payload; + $payload['participants'] = $this->participantId . ',' . $participantFromDifferentCourse; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für TN ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateNewObservationAssignment_noUserIds() + { + // given + $payload = $this->payload; + unset($payload['users']); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Equipe muss ausgefüllt sein.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateNewObservationAssignment_invalidUserIds() + { + // given + $payload = $this->payload; + $payload['users'] = 'a'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Equipe Format ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateNewObservationAssignment_oneValidUserId() + { + // given + $payload = $this->payload; + $userId = $this->createUser()->id; + Course::find($this->courseId)->users()->attach($userId); + $payload['users'] = $userId; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments'); + $this->assertEquals([$userId], ObservationAssignment::latest()->first()->users->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignment_multipleValidUserIds() + { + // given + $payload = $this->payload; + $userIds = [$this->createUser()->id, $this->createUser()->id]; + Course::find($this->courseId)->users()->attach($userIds); + $payload['users'] = implode(',', $userIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments'); + $this->assertEquals($userIds, ObservationAssignment::latest()->first()->users->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignment_someNonexistentUserIds() + { + // given + $payload = $this->payload; + $userIds = [$this->createUser()->id, '999999', $this->createUser()->id]; + Course::find($this->courseId)->users()->attach([$userIds[0], $userIds[2]]); + $payload['users'] = implode(',', $userIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Equipe ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateNewObservationAssignment_someInvalidUserIds() + { + // given + $payload = $this->payload; + $userIds = [$this->createUser()->id, 'abc', $this->createUser()->id]; + Course::find($this->courseId)->users()->attach([$userIds[0], $userIds[2]]); + $payload['users'] = implode(',', $userIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Equipe Format ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldNotAllowCreatingObservationAssignment_withUserFromADifferentCourse() + { + // given + $differentCourse = $this->createCourse('Other course', '', false); + $userFromDifferentCourse = $this->createUser(['name' => 'Foreign'])->id; + Course::find($differentCourse)->users()->attach($userFromDifferentCourse); + $payload = $this->payload; + $payload['users'] = Auth::id() . ',' . $userFromDifferentCourse; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Equipe ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateNewObservationAssignment_noBlockIds() + { + // given + $payload = $this->payload; + unset($payload['blocks']); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Blöcke muss ausgefüllt sein.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldValidateNewObservationAssignment_invalidBlockIds() + { + // given + $payload = $this->payload; + $payload['blocks'] = 'a'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Blöcke Format ist ungültig.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldValidateNewObservationAssignment_oneValidBlockId() + { + // given + $payload = $this->payload; + $blockId = $this->createBlock(); + $payload['blocks'] = $blockId; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments'); + $this->assertEquals([$blockId], ObservationAssignment::latest()->first()->blocks->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignment_multipleValidBlockIds() + { + // given + $payload = $this->payload; + $blockIds = [$this->createBlock(), $this->createBlock()]; + $payload['blocks'] = implode(',', $blockIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments'); + $this->assertEquals($blockIds, ObservationAssignment::latest()->first()->blocks->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignment_someNonexistentBlockIds() + { + // given + $payload = $this->payload; + $blockIds = [$this->createBlock(), '999999', $this->createBlock()]; + $payload['blocks'] = implode(',', $blockIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Blöcke ist ungültig.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldValidateNewObservationAssignment_someInvalidBlockIds() + { + // given + $payload = $this->payload; + $blockIds = [$this->createBlock(), 'abc', $this->createBlock()]; + $payload['blocks'] = implode(',', $blockIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Blöcke Format ist ungültig.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldNotAllowCreatingObservationAssignment_withBlockFromADifferentCourse() + { + // given + $differentCourse = $this->createCourse('Other course', '', false); + $blockFromDifferentCourse = $this->createBlock('Foreign', '1.1', '01.01.2019', null, $differentCourse); + $payload = $this->payload; + $payload['blocks'] = $this->blockId . ',' . $blockFromDifferentCourse; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments', $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Blöcke ist ungültig.', $exception->validator->errors()->first('blocks')); + } + +} diff --git a/tests/Feature/Admin/ObservationAssignment/DeleteObservationAssignmentTest.php b/tests/Feature/Admin/ObservationAssignment/DeleteObservationAssignmentTest.php new file mode 100644 index 00000000..dceccf8d --- /dev/null +++ b/tests/Feature/Admin/ObservationAssignment/DeleteObservationAssignmentTest.php @@ -0,0 +1,65 @@ +observationAssignmentId = $this->createObservationAssignment('Beobachtungsauftrag 1'); + } + + public function test_shouldRequireLogin() { + // given + auth()->logout(); + + // when + $response = $this->delete('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId); + + // then + $response->assertStatus(302); + $response->assertRedirect('/login'); + } + + public function test_shouldRequireNonArchivedCourse() { + // given + Course::find($this->courseId)->update(['archived' => true]); + + // when + $response = $this->delete('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId); + + // then + $response->assertStatus(302); + $response->assertRedirect(route('admin.course', ['course' => $this->courseId])); + } + + public function test_shouldDeleteObservationAssignment() { + // given + + // when + $response = $this->delete('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/'); + /** @var TestResponse $response */ + $response = $response->followRedirects(); + $response->assertDontSee('UN Gruppe 1'); + } + + public function test_shouldValidateDeletedObservationAssignmentUrl_wrongId() { + // given + + // when + $response = $this->delete('/course/' . $this->courseId . '/admin/observationAssignments/' . ($this->observationAssignmentId + 1)); + + // then + $response->assertStatus(404); + } +} diff --git a/tests/Feature/Admin/ObservationAssignment/ReadObservationAssignmentTest.php b/tests/Feature/Admin/ObservationAssignment/ReadObservationAssignmentTest.php new file mode 100644 index 00000000..98e46192 --- /dev/null +++ b/tests/Feature/Admin/ObservationAssignment/ReadObservationAssignmentTest.php @@ -0,0 +1,70 @@ +observationAssignmentId = $this->createObservationAssignment('Auftrag 1'); + } + + + public function test_shouldRequireLogin() + { + // given + auth()->logout(); + + // when + $response = $this->get('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId); + + // then + $response->assertStatus(302); + $response->assertRedirect('/login'); + } + + public function test_shouldRequireNonArchivedCourse() + { + // given + Course::find($this->courseId)->update(['archived' => true]); + + // when + $response = $this->get('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId); + + // then + $response->assertStatus(302); + $response->assertRedirect(route('admin.course', ['course' => $this->courseId])); + } + + public function test_shouldDisplayObservationAssignment() + { + // given + + // when + $response = $this->get('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId); + + // then + $response->assertOk(); + $response->assertSee('Auftrag 1'); + } + + public function test_shouldNotDisplayObservationAssignment_fromOtherCourseOfSameUser() + { + // given + $otherKursId = $this->createCourse('Zweiter Kurs', ''); + + // when + $response = $this->get('/course/' . $otherKursId . '/admin/observationAssignments/' . $this->observationAssignmentId); + + // then + $this->assertInstanceOf(ModelNotFoundException::class, $response->exception); + } +} diff --git a/tests/Feature/Admin/ObservationAssignment/UpdateObservationAssignmentTest.php b/tests/Feature/Admin/ObservationAssignment/UpdateObservationAssignmentTest.php new file mode 100644 index 00000000..260a6522 --- /dev/null +++ b/tests/Feature/Admin/ObservationAssignment/UpdateObservationAssignmentTest.php @@ -0,0 +1,453 @@ +observationAssignmentId = $this->createObservationAssignment("Auftrag 1"); + + + $this->payload = ['name' => 'Besserer Auftrag', 'participants' => '' . $this->participantId, 'users' => '' . Auth::id(), 'blocks' => '' . $this->blockId]; + } + + public function test_shouldRequireLogin() + { + // given + auth()->logout(); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $this->payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/login'); + } + + public function test_shouldRequireNonArchivedCourse() + { + // given + Course::find($this->courseId)->update(['archived' => true]); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $this->payload); + + // then + $response->assertStatus(302); + $response->assertRedirect(route('admin.course', ['course' => $this->courseId])); + } + + public function test_shouldUpdateObservationAssignment() + { + // given + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $this->payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/' ); + /** @var TestResponse $response */ + $response = $response->followRedirects(); + $response->assertSee($this->payload['name']); + $response->assertDontSee('Auftrag 1'); + } + + public function test_shouldValidateNewObservationAssignmentData_noName() + { + // given + $payload = $this->payload; + unset($payload['name']); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Beobachtungsauftrag muss ausgefüllt sein.', $exception->validator->errors()->first('name')); + } + + public function test_shouldValidateNewObservationAssignmentData_longName() + { + // given + $payload = $this->payload; + $payload['name'] = 'Unglaublich langer Name. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr. Und noch etwas mehr.'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Beobachtungsauftrag darf maximal 1023 Zeichen haben.', $exception->validator->errors()->first('name')); + } + + public function test_shouldValidateObservationAssignmentData_noParticipantIds() + { + // given + $payload = $this->payload; + $payload['participants'] = ''; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('TN muss ausgefüllt sein.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateNewObservationAssignmentData_invalidParticipantIds() + { + // given + $payload = $this->payload; + $payload['participants'] = 'a'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('TN Format ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateNewObservationAssignmentData_oneValidParticipantId() + { + // given + $payload = $this->payload; + $participantId = $this->createParticipant(); + $payload['participants'] = $participantId; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/'); + $this->assertEquals([$participantId], ObservationAssignment::latest()->first()->participants()->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignmentData_multipleValidParticipantIds() + { + // given + $payload = $this->payload; + $participantIds = [$this->createParticipant(), $this->createParticipant()]; + $payload['participants'] = implode(',', $participantIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/'); + $this->assertEquals($participantIds, ObservationAssignment::latest()->first()->participants()->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignmentData_someNonexistentParticipantIds() + { + // given + $payload = $this->payload; + $participantIds = [$this->createParticipant(), '999999', $this->createParticipant()]; + $payload['participants'] = implode(',', $participantIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für TN ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateNewObservationAssignmentData_someInvalidParticipantIds() + { + // given + $payload = $this->payload; + $participantIds = [$this->createParticipant(), 'abc', $this->createParticipant()]; + $payload['participants'] = implode(',', $participantIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('TN Format ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldNotAllowChangingParticipantToSomeoneFromADifferentCourse() + { + // given + $differentCourse = $this->createCourse('Other course', '', false); + $participantFromDifferentCourse = $this->createParticipant('Foreign', $differentCourse); + $payload = $this->payload; + $payload['participants'] = $this->participantId . ',' . $participantFromDifferentCourse; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für TN ist ungültig.', $exception->validator->errors()->first('participants')); + } + + public function test_shouldValidateObservationAssignmentData_noUserIds() + { + // given + $payload = $this->payload; + $payload['users'] = ''; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Equipe muss ausgefüllt sein.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateNewObservationAssignmentData_invalidUserIds() + { + // given + $payload = $this->payload; + $payload['users'] = 'a'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Equipe Format ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateNewObservationAssignmentData_oneValidUserId() + { + // given + $payload = $this->payload; + $userId = $this->createUser()->id; + Course::find($this->courseId)->users()->attach($userId); + $payload['users'] = $userId; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/'); + $this->assertEquals([$userId], ObservationAssignment::latest()->first()->users()->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignmentData_multipleValidUserIds() + { + // given + $payload = $this->payload; + $userIds = [$this->createUser()->id, $this->createUser()->id]; + Course::find($this->courseId)->users()->attach($userIds); + $payload['users'] = implode(',', $userIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/'); + $this->assertEquals($userIds, ObservationAssignment::latest()->first()->users()->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignmentData_someNonexistentUserIds() + { + // given + $payload = $this->payload; + $userIds = [$this->createUser()->id, '999999', $this->createUser()->id]; + Course::find($this->courseId)->users()->attach([$userIds[0], $userIds[2]]); + $payload['users'] = implode(',', $userIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Equipe ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateNewObservationAssignmentData_someInvalidUserIds() + { + // given + $payload = $this->payload; + $userIds = [$this->createUser()->id, 'abc', $this->createUser()->id]; + Course::find($this->courseId)->users()->attach([$userIds[0], $userIds[2]]); + $payload['users'] = implode(',', $userIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Equipe Format ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldNotAllowChangingUserToSomeoneFromADifferentCourse() + { + // given + $differentCourse = $this->createCourse('Other course', '', false); + $userFromDifferentCourse = $this->createUser(['name' => 'Foreign'])->id; + Course::find($differentCourse)->users()->attach($userFromDifferentCourse); + $payload = $this->payload; + $payload['users'] = Auth::id() . ',' . $userFromDifferentCourse; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Equipe ist ungültig.', $exception->validator->errors()->first('users')); + } + + public function test_shouldValidateObservationAssignmentData_noBlockIds() + { + // given + $payload = $this->payload; + $payload['blocks'] = ''; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Blöcke muss ausgefüllt sein.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldValidateNewObservationAssignmentData_invalidBlockIds() + { + // given + $payload = $this->payload; + $payload['blocks'] = 'a'; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Blöcke Format ist ungültig.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldValidateNewObservationAssignmentData_oneValidBlockId() + { + // given + $payload = $this->payload; + $blockId = $this->createBlock(); + $payload['blocks'] = $blockId; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/'); + $this->assertEquals([$blockId], ObservationAssignment::latest()->first()->blocks()->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignmentData_multipleValidBlockIds() + { + // given + $payload = $this->payload; + $blockIds = [$this->createBlock(), $this->createBlock()]; + $payload['blocks'] = implode(',', $blockIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/admin/observationAssignments/'); + $this->assertEquals($blockIds, ObservationAssignment::latest()->first()->blocks()->pluck('id')->all()); + } + + public function test_shouldValidateNewObservationAssignmentData_someNonexistentBlockIds() + { + // given + $payload = $this->payload; + $blockIds = [$this->createBlock(), '999999', $this->createBlock()]; + $payload['blocks'] = implode(',', $blockIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Blöcke ist ungültig.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldValidateNewObservationAssignmentData_someInvalidBlockIds() + { + // given + $payload = $this->payload; + $blockIds = [$this->createBlock(), 'abc', $this->createBlock()]; + $payload['blocks'] = implode(',', $blockIds); + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Blöcke Format ist ungültig.', $exception->validator->errors()->first('blocks')); + } + + public function test_shouldNotAllowChangingBlockToSomeoneFromADifferentCourse() + { + // given + $differentCourse = $this->createCourse('Other course', '', false); + $blockFromDifferentCourse = $this->createBlock('Foreign', '1.1', '01.01.2019', null, $differentCourse); + $payload = $this->payload; + $payload['blocks'] = $this->blockId . ',' . $blockFromDifferentCourse; + + // when + $response = $this->post('/course/' . $this->courseId . '/admin/observationAssignments/' . $this->observationAssignmentId, $payload); + + // then + $this->assertInstanceOf(ValidationException::class, $response->exception); + /** @var ValidationException $exception */ + $exception = $response->exception; + $this->assertEquals('Der gewählte Wert für Blöcke ist ungültig.', $exception->validator->errors()->first('blocks')); + } + + } diff --git a/tests/Feature/Crib/ReadCribTest.php b/tests/Feature/Crib/ReadCribTest.php index de49c57f..4e3055b1 100644 --- a/tests/Feature/Crib/ReadCribTest.php +++ b/tests/Feature/Crib/ReadCribTest.php @@ -2,8 +2,11 @@ namespace Tests\Feature\Crib; +use App\Models\Observation; +use App\Models\ObservationAssignment; use App\Models\Participant; use Illuminate\Database\Eloquent\ModelNotFoundException; +use Illuminate\Support\Facades\Auth; use Tests\TestCaseWithCourse; class ReadCribTest extends TestCaseWithCourse { @@ -110,4 +113,110 @@ public function test_shouldNotDisplayCrib_toOtherUser() { // then $this->assertInstanceOf(ModelNotFoundException::class, $response->exception); } + + public function test_shouldCalculateObservationAssignmentsCorrectly() { + // given + $block1 = $this->createBlock('Block 1', '1.1', '01.01.2019'); + $block2 = $this->createBlock('Block 2', '1.2', '01.01.2019'); + $participant1 = $this->createParticipant('One'); + $participant2 = $this->createParticipant('Two'); + $observation = Observation::create(['content' => 'test', 'block' => $block1, 'user_id' => Auth::id()]); + $observation->participants()->attach($participant1); + $multiObservation = Observation::create(['content' => 'haben die ganze Zeit nur geschnädert', 'block' => $block1, 'user_id' => Auth::id()]); + $multiObservation->participants()->attach([$participant1, $participant2]); + + $observationAssignment = ObservationAssignment::create(['name' => 'Assignment', 'course_id' => $this->courseId]); + $observationAssignment->blocks()->attach([$block1, $block2]); + $observationAssignment->participants()->attach([$participant1, $participant2]); + $observationAssignment->users()->attach(Auth::id()); + + $userId = Auth::id(); + + // when + $response = $this->get('/course/' . $this->courseId . '/crib'); + + // then + $response->assertOk(); + $response->assertSee('Block 1'); + $response->assertSee('Block 2'); + $data = $response->getOriginalContent()->getData()['trainerObservationAssignments']; + $this->assertEquals([$block2, $block1], array_keys($data->all())); + $block1Participant1 = collect($data[$block1])->first(function ($entry) use($participant1) { return $entry['id'] === $participant1; }); + $this->assertEquals($block1Participant1['user_id'], $userId); + $this->assertEquals($block1Participant1['block_id'], $block1); + $this->assertEquals($block1Participant1['observation_count'], 2); + $this->assertEquals($block1Participant1['id'], $participant1); + $this->assertEquals($block1Participant1['scout_name'], 'One'); + $block1Participant2 = collect($data[$block1])->first(function ($entry) use($participant2) { return $entry['id'] === $participant2; }); + $this->assertEquals($block1Participant2['user_id'], $userId); + $this->assertEquals($block1Participant2['block_id'], $block1); + $this->assertEquals($block1Participant2['observation_count'], 1); + $this->assertEquals($block1Participant2['id'], $participant2); + $this->assertEquals($block1Participant2['scout_name'], 'Two'); + $block2Participant1 = collect($data[$block2])->first(function ($entry) use($participant1) { return $entry['id'] === $participant1; }); + $this->assertEquals($block2Participant1['user_id'], $userId); + $this->assertEquals($block2Participant1['block_id'], $block2); + $this->assertEquals($block2Participant1['observation_count'], 0); + $this->assertEquals($block2Participant1['id'], $participant1); + $this->assertEquals($block2Participant1['scout_name'], 'One'); + $block2Participant2 = collect($data[$block2])->first(function ($entry) use($participant2) { return $entry['id'] === $participant2; }); + $this->assertEquals($block2Participant2['user_id'], $userId); + $this->assertEquals($block2Participant2['block_id'], $block2); + $this->assertEquals($block2Participant2['observation_count'], 0); + $this->assertEquals($block2Participant2['id'], $participant2); + $this->assertEquals($block2Participant2['scout_name'], 'Two'); + } + + public function test_shouldReturnToCrib_afterAddingObservationInAssignment() { + // given + $block1 = $this->createBlock('Block 1', '1.1', '01.01.2019'); + $block2 = $this->createBlock('Block 2', '1.2', '01.01.2019'); + $participant1 = $this->createParticipant('One'); + $participant2 = $this->createParticipant('Two'); + + $observationAssignment = ObservationAssignment::create(['name' => 'Assignment', 'course_id' => $this->courseId]); + $observationAssignment->blocks()->attach([$block1, $block2]); + $observationAssignment->participants()->attach([$participant1, $participant2]); + $observationAssignment->users()->attach(Auth::id()); + + $this->get('/course/' . $this->courseId . '/crib'); + + // when + $this->get('/course/' . $this->courseId . '/observation/new?participant=' . $participant1 . '&block=' . $block1); + $response = $this->post('/course/' . $this->courseId . '/observation/new', [ + 'participants' => '' . $participant1, + 'content' => 'hat gut mitgemacht', + 'impression' => '1', + 'block' => '' . $block1, + 'requirements' => '', + 'categories' => '' + ]); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/crib'); + } + + public function test_shouldReturnToCrib_afterAddingObservationInBlock() { + // given + $block1 = $this->createBlock('Block 1', '1.1', '01.01.2019'); + $participant1 = $this->createParticipant('One'); + + $this->get('/course/' . $this->courseId . '/crib'); + + // when + $this->get('/course/' . $this->courseId . '/observation/new'); + $response = $this->post('/course/' . $this->courseId . '/observation/new', [ + 'participants' => '' . $participant1, + 'content' => 'hat gut mitgemacht', + 'impression' => '1', + 'block' => '' . $block1, + 'requirements' => '', + 'categories' => '' + ]); + + // then + $response->assertStatus(302); + $response->assertRedirect('/course/' . $this->courseId . '/crib'); + } } diff --git a/tests/TestCaseWithBasicData.php b/tests/TestCaseWithBasicData.php index 70bbfc97..0303d60f 100644 --- a/tests/TestCaseWithBasicData.php +++ b/tests/TestCaseWithBasicData.php @@ -3,8 +3,10 @@ namespace Tests; use App\Models\Observation; +use App\Models\ObservationAssignment; use App\Models\ParticipantGroup; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\Auth; abstract class TestCaseWithBasicData extends TestCaseWithCourse { @@ -30,6 +32,13 @@ protected function createParticipantGroup($group_name = "Test Gruppe", $particip $participant_group = ParticipantGroup::create(['course_id' => ($courseId !== null ? $courseId : $this->courseId), 'group_name' => $group_name]); $participant_group->participants()->attach(($participantIds !== null ? Arr::wrap($participantIds) : [$this->participantId])); return $participant_group->id; + } + protected function createObservationAssignment($name = "Test Auftrag", $participantIds = null, $blockIds = null, $userIds = null, $courseId = null) { + $observation_assignment = ObservationAssignment::create(['course_id' => ($courseId !== null ? $courseId : $this->courseId), 'name' => $name]); + $observation_assignment->participants()->attach(($participantIds !== null ? Arr::wrap($participantIds) : [$this->participantId])); + $observation_assignment->blocks()->attach(($blockIds !== null ? Arr::wrap($blockIds) : [$this->blockId])); + $observation_assignment->users()->attach(($userIds !== null ? Arr::wrap($userIds) : [Auth::id()])); + return $observation_assignment->id; } }