Skip to content

Commit

Permalink
Limit number of requirements in a feedback to 40
Browse files Browse the repository at this point in the history
  • Loading branch information
carlobeltrame committed Jan 30, 2024
1 parent 9e6a0c7 commit bff1102
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ MAIL_FROM_NAME="${APP_NAME}"
HITOBITO_BASE_URL=
HITOBITO_CLIENT_UID=xxx
HITOBITO_CLIENT_SECRET=yyy
HITOBITO_CALLBACK_URI=https://localhost/login/hitobito/callback
HITOBITO_CALLBACK_URI=http://localhost/login/hitobito/callback

# Collaboration in the feedback editor via WebRTC
COLLABORATION_ENABLED=true
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

##### Januar 2024
- Aus technischen und praktischen Gründen wurde die Anzahl relevante Anforderungen in einer Rückmeldung auf maximal 40 limitiert. Auslöser war, dass die Übersichtstabelle sonst technisch wie auch visuell nicht mehr sinnvoll angezeigt werden konnte. Auch fachlich gesehen ist das Konzept der Rückmeldungen in Qualix nicht darauf ausgelegt, sehr viele eingebettete Anforderungen zu enthalten, da Übersichtlichkeit, Fördergedanke, Überprüfbarkeit, zweite Chancen, Zweitausbildung etc. alle darunter leiden. Dies sehen wir in folgenden Textstellen der RQF-Broschüre bestätigt, welche klar machen dass mit jeder einzelnen Mindestanforderung der Zeitaufwand für das Kursteam wie auch für die TN markant ansteigt:
> [Es] muss beachtet werden, dass zu jeder Mindestanforderung auch ein Beobachtungsmoment gehört, bei dem die TN zeigen können, was sie gelernt haben und das Kursteam ebendies wahrnehmen kann.[^1]
> Im Kurs sollen die TN unbedingt auch diejenigen Kompetenzen (weiter)entwickeln können, welche nicht explizit geprüft werden.[^1]
> Wichtig ist auch, dass die TN die Möglichkeit erhalten zu üben, Neues auszuprobieren und Fehler zu machen, bevor die Mindestanforderungen zur Hand genommen werden und die Leistung der TN überprüft wird.[^2]
> Es muss sichergestellt werden, dass zu allen definierten Mindestanforderungen im Kursverlauf auch die entsprechenden Inhalte ausgebildet werden und die TN die Gelegenheit haben, die erwarteten Leistungen zu erbringen.[^3]
> Jede einzelne Mindestanforderung muss für sich erfüllt sein. Eine Kompensation von Schwächen durch besonders gute Leistungen in anderen Bereichen ist deshalb nicht möglich.[^4]
Aus diesen Gründen empfehlen wir den Einsatz von maximal 10 Mindestanforderungen in einem Kurs. Die Nutzendenoberfläche und Features von Qualix sind ebenfalls auf dieser Annahme basierend optimiert.

- Beim neu Erstellen von Rückmeldungen werden aus obigen Gründen nur noch Anforderungen vorausgewählt, welche explizit als "Mindestanforderung" markiert sind. Die restlichen Anforderungen sind natürlich weiterhin auswählbar.

[^1]: [Rückmelden, Qualifizieren und Fördern im Ausbildungskurs, Seite 14](https://issuu.com/pbs-msds-mss/docs/3118.01de-rqf-20160831-akom/14)
[^2]: [Rückmelden, Qualifizieren und Fördern im Ausbildungskurs, Seite 15](https://issuu.com/pbs-msds-mss/docs/3118.01de-rqf-20160831-akom/15)
[^3]: [Rückmelden, Qualifizieren und Fördern im Ausbildungskurs, Seite 31](https://issuu.com/pbs-msds-mss/docs/3118.01de-rqf-20160831-akom/31)
[^4]: [Rückmelden, Qualifizieren und Fördern im Ausbildungskurs, Seite 35](https://issuu.com/pbs-msds-mss/docs/3118.01de-rqf-20160831-akom/35)

##### November 2023
- Es können nun die Rückmeldungs-PDFs für alle TN gleichzeitig heruntergeladen werden [#325](https://github.com/gloggi/qualix/pull/325)
- Tägliche "Sonstiges"-Blöcke o.ä. können jetzt automatisch generiert werden [#326](https://github.com/gloggi/qualix/issues/76)
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Requests/FeedbackUpdateRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function rules() {
return [
'name' => 'required|max:255',
'participants' => 'required|regex:/^\d+(,\d+)*$/|allExistInCourse',
'requirements' => 'nullable|regex:/^\d+(,\d+)*$/|allExistInCourse',
'requirements' => 'nullable|regex:/^\d+(,\d+)*$/|allExistInCourse|maxEntries:40',
'feedbacks' => 'nullable|array',
'feedbacks.*.users' => 'nullable|regex:/^\d+(,\d+)*$/|allExistInCourse:trainers,user_id',
];
Expand Down
6 changes: 6 additions & 0 deletions app/Providers/ValidationServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Services\Validation\AllExistInCourse;
use App\Services\Validation\ExistsInCourse;
use App\Services\Validation\MaxEntries;
use App\Services\Validation\ValidFeedbackContent;
use App\Services\Validation\ValidFeedbackContentWithoutObservations;
use Illuminate\Support\Facades\Validator;
Expand All @@ -24,5 +25,10 @@ public function boot(): void {
Validator::extend('allExistInCourse', AllExistInCourse::class . '@validate');
Validator::extend('validFeedbackContent', ValidFeedbackContent::class . '@validate');
Validator::extend('validFeedbackContentWithoutObservations', ValidFeedbackContentWithoutObservations::class . '@validate');

Validator::extend('maxEntries', MaxEntries::class . '@validate');
Validator::replacer('maxEntries', function ($message, $attribute, $rule, $parameters, $validator) {
return str_replace(':max', $parameters[0], $message);
});
}
}
22 changes: 22 additions & 0 deletions app/Services/Validation/MaxEntries.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Services\Validation;

use Illuminate\Validation\Validator;

class MaxEntries {

/**
* Determine if the validation rule passes.
*
* @param $attribute
* @param string $value
* @param $parameters
* @param $validator Validator
* @return bool
*/
public function validate($attribute, $value, $parameters, $validator) {
return $parameters[0] >= count(explode(',', $value));
}

}
1 change: 1 addition & 0 deletions lang/de/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"numeric" => ":attribute darf maximal :max sein.",
"string" => ":attribute darf maximal :max Zeichen haben.",
),
"max_entries" => ":attribute darf nicht mehr als :max ausgewählte Elemente haben.",
"mimes" => ":attribute muss den Dateityp :values haben.",
"mimetypes" => ":attribute muss den Dateityp :values haben.",
"min" => array(
Expand Down
5 changes: 4 additions & 1 deletion resources/js/components/feedback/FormFeedbackData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,13 @@ export default {
feedbackContentsTemplate: { type: Boolean, default: false },
},
data() {
const noMandatoryRequirements = !this.requirements.some(r => r.mandatory)
return {
currentName: this.name,
participantsFormValue: this.feedbacks ? this.feedbacks.map(q => q.participant.id).join() : this.participants.map(p => p.id).join(),
requirementsFormValue: this.feedbacks ? [...new Set(this.feedbacks.flatMap(q => q.requirements.map(r => r.id)))].join() : this.requirements.map(r => r.id).join(),
requirementsFormValue: this.feedbacks ?
[...new Set(this.feedbacks.flatMap(q => q.requirements.map(r => r.id)))].join() :
this.requirements.filter(r => noMandatoryRequirements || r.mandatory).map(r => r.id).join(),
trainerAssignments: this.participants
.sort((a, b) => a.scout_name.localeCompare(b.scout_name))
.map(p => {
Expand Down
2 changes: 1 addition & 1 deletion resources/views/admin/feedbacks/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
$course->participantGroups->mapWithKeys(function ($group) {
return [$group['group_name'] => $group->participants->pluck('id')->join(',')];
}), JSON_FORCE_OBJECT)}}"
:requirements="{{ json_encode($course->requirements->map->only('id', 'content')) }}"
:requirements="{{ json_encode($course->requirements->map->only('id', 'content', 'mandatory')) }}"
:requirement-statuses="{{ json_encode($course->requirement_statuses) }}"
:trainers="{{ json_encode($course->users->map->only('id', 'name')) }}"
feedback-contents-template>
Expand Down
23 changes: 23 additions & 0 deletions tests/Feature/Admin/Feedback/CreateFeedbackTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,29 @@ public function test_shouldValidateNewFeedbackData_someInvalidRequirementIds() {
$this->assertEquals('Relevante Anforderungen Format ist ungültig.', $exception->validator->errors()->first('requirements'));
}

public function test_shouldValidateNewFeedbackData_tooManyValidRequirementIds() {
// given
$payload = $this->payload;
$requirementIds = array_map(function () { return $this->createRequirement(); }, range(1, 41));
$requirementStatusId = $this->createRequirementStatus();
$payload['requirements'] = implode(',', $requirementIds);
$payload['feedback_contents_template'] = json_encode([
'type' => 'doc',
'content' => array_map(function ($requirementId) use ($requirementStatusId) {
return ['type' => 'requirement', 'attrs' => ['id' => $requirementId, 'status_id' => $requirementStatusId, 'comment' => 'something']];
}, $requirementIds)
]);

// when
$response = $this->post('/course/' . $this->courseId . '/admin/feedbacks', $payload);

// then
$this->assertInstanceOf(ValidationException::class, $response->exception);
/** @var ValidationException $exception */
$exception = $response->exception;
$this->assertEquals('Relevante Anforderungen darf nicht mehr als 40 ausgewählte Elemente haben.', $exception->validator->errors()->first('requirements'));
}

public function test_shouldValidateNewFeedbackData_noFeedbackNotesTemplate() {
// given
$payload = $this->payload;
Expand Down
16 changes: 16 additions & 0 deletions tests/Feature/Admin/Feedback/UpdateFeedbackTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,22 @@ public function test_shouldValidateNewFeedbackData_someInvalidRequirementIds() {
$this->assertEquals('Relevante Anforderungen Format ist ungültig.', $exception->validator->errors()->first('requirements'));
}

public function test_shouldValidateNewFeedbackData_tooManyValidRequirementIds() {
// given
$payload = $this->payload;
$requirementIds = array_map(function () { return $this->createRequirement(); }, range(1, 41));
$payload['requirements'] = implode(',', $requirementIds);

// when
$response = $this->post('/course/' . $this->courseId . '/admin/feedbacks/' . $this->feedbackDataId, $payload);

// then
$this->assertInstanceOf(ValidationException::class, $response->exception);
/** @var ValidationException $exception */
$exception = $response->exception;
$this->assertEquals('Relevante Anforderungen darf nicht mehr als 40 ausgewählte Elemente haben.', $exception->validator->errors()->first('requirements'));
}

public function test_shouldValidateNewFeedbackData_invalidTrainerAssignment() {
// given
$payload = $this->payload;
Expand Down

0 comments on commit bff1102

Please sign in to comment.