Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/controllers/registration/policies_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ def set_locale

def policy_params
params.expect(registration_policy: [:kind, :phase, :allowed_domains,
:prerequisite_campaign_id])
:prerequisite_campaign_id,
:lecture_id])
end

def move(direction)
Expand Down
46 changes: 34 additions & 12 deletions app/frontend/registration/policies/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<%= form_with(model: [@campaign, @policy],
url: @policy.persisted? ? registration_campaign_policy_path(@campaign, @policy) : registration_campaign_policies_path(@campaign),
data: { controller: "registration-policy-form" }) do |f| %>

<% if @policy.errors[:base].present? %>
<div class="alert alert-danger">
<%= @policy.errors[:base].join(", ") %>
Expand All @@ -12,15 +11,18 @@
<div class="col-12 col-md-6">
<%= f.label :kind, t("registration.policy.kind"), class: "form-label" %>
<%= helpdesk(t("registration.policy.helpdesk.kind"), false) %>

<%= f.select :kind,
Registration::Policy.kinds.keys.map { |k| [t("registration.policy.kinds.#{k}"), k] },
{},
class: "form-select",
data: { action: "registration-policy-form#changeKind", registration_policy_form_target: "kindSelect" } %>
</div>

<div class="col-12 col-md-6">
<%= f.label :phase, t("registration.policy.phase"), class: "form-label" %>
<%= helpdesk(t("registration.policy.helpdesk.phase"), false) %>

<%= f.select :phase,
Registration::Policy.phases.keys.map { |k| [t("registration.policy.phases.#{k}"), k] },
{},
Expand All @@ -31,22 +33,36 @@
<!-- Config Fields -->
<div class="card bg-light mb-3">
<div class="card-body">
<h6 class="card-subtitle mb-2 text-muted"><%= t("registration.policy.config") %></h6>
<h6 class="card-subtitle mb-2 text-muted">
<%= t("registration.policy.config") %>
</h6>

<!-- Institutional Email -->
<div data-registration-policy-form-target="configSection" data-kind="institutional_email" class="d-none">
<div
data-registration-policy-form-target="configSection"
data-kind="institutional_email"
class="d-none"
>
<div class="mb-3">
<%= f.label :allowed_domains, t("registration.policy.configs.allowed_domains"), class: "form-label" %>
<%= helpdesk(t("registration.policy.helpdesk.allowed_domains"), false) %>
<%= f.text_field :allowed_domains, class: "form-control" %>
<div class="form-text"><%= t("registration.policy.hints.allowed_domains") %></div>

<div class="form-text">
<%= t("registration.policy.hints.allowed_domains") %>
</div>
</div>
</div>

<!-- Prerequisite Campaign -->
<div data-registration-policy-form-target="configSection" data-kind="prerequisite_campaign" class="d-none">
<div
data-registration-policy-form-target="configSection"
data-kind="prerequisite_campaign"
class="d-none"
>
<div class="mb-3">
<%= f.label :prerequisite_campaign_id, t("registration.policy.configs.prerequisite_campaign_id"), class: "form-label" %>

<%= f.collection_select :prerequisite_campaign_id,
Registration::Campaign.where.not(id: @campaign.id),
:id,
Expand All @@ -57,16 +73,22 @@
</div>

<!-- Student Performance -->
<div data-registration-policy-form-target="configSection" data-kind="student_performance" class="d-none">
<div
data-registration-policy-form-target="configSection"
data-kind="student_performance"
class="d-none"
>
<div class="mb-3">
<label class="form-label"><%= t("registration.policy.configs.lecture_id") %></label>
<%= select_tag "registration_policy[config][lecture_id]",
options_from_collection_for_select(Lecture.all, :id, :title, @policy.config&.fetch("lecture_id", nil)),
include_blank: true,
class: "form-select" %>
<label class="form-label">
<%= t("registration.policy.configs.lecture_id") %>
</label>

<%= f.collection_select :lecture_id,
Lecture.all, :id, :title,
{ include_blank: true, selected: @policy.lecture_id },
{ class: "form-select" } %>
</div>
</div>

</div>
</div>

Expand Down
11 changes: 10 additions & 1 deletion app/models/registration/policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ def prerequisite_campaign_id=(value)
self.config["prerequisite_campaign_id"] = value
end

def lecture_id
config&.fetch("lecture_id", nil)
end

def lecture_id=(value)
self.config ||= {}
self.config["lecture_id"] = value
end

def config_summary
handler.summary || "-"
end
Expand All @@ -71,7 +80,7 @@ def handler
when :prerequisite_campaign
Registration::Policy::PrerequisiteCampaignHandler.new(self)
when :student_performance
Registration::Policy::Handler.new(self)
Registration::Policy::StudentPerformanceHandler.new(self)
else
raise(ArgumentError, "Unknown policy kind: #{kind}")
end
Expand Down
62 changes: 62 additions & 0 deletions app/models/registration/policy/student_performance_handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
module Registration
class Policy
# Handles the "Student Performance" policy
# Checks if the user has passed a certification for a specific lecture.
class StudentPerformanceHandler < Handler
def evaluate(user)
if lecture_id.blank?
return fail_result(:configuration_error,
"Lecture not configured")
end
unless lecture
return fail_result(:lecture_not_found,
"Lecture not found")
end

cert = StudentPerformance::Certification.find_by(
lecture: lecture, user: user
)

if cert&.passed?
pass_result(:certification_passed)
else
fail_result(
:certification_not_passed,
"Lecture performance certification required",
certification_status: cert&.status&.to_sym || :missing
)
end
end

def validate
if lecture_id.blank?
policy.errors.add(
:lecture_id,
I18n.t("registration.policy.errors.missing_lecture")
)
elsif !Lecture.exists?(lecture_id)
policy.errors.add(
:lecture_id,
I18n.t("registration.policy.errors.lecture_not_found")
)
end
end

def summary
lecture&.title
end

private

def lecture_id
config["lecture_id"]
end

def lecture
return @lecture if defined?(@lecture)

@lecture = Lecture.find_by(id: lecture_id)
end
end
end
end
2 changes: 2 additions & 0 deletions config/locales/registration/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ de:
missing_domains: "Es muss mindestens eine erlaubte Domain angegeben werden."
missing_prerequisite_campaign: "Es muss ein vorausgesetztes Anmeldeverfahren ausgewählt werden."
prerequisite_campaign_not_found: "Das ausgewählte vorausgesetzte Anmeldeverfahren existiert nicht."
missing_lecture: "Es muss eine Veranstaltung ausgewählt werden."
lecture_not_found: "Die ausgewählte Veranstaltung existiert nicht."
configs:
allowed_domains: "Erlaubte Domains"
prerequisite_campaign_id: "Vorausgesetztes Anmeldeverfahren"
Expand Down
2 changes: 2 additions & 0 deletions config/locales/registration/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ en:
missing_domains: "At least one allowed domain must be specified."
missing_prerequisite_campaign: "A prerequisite campaign must be selected."
prerequisite_campaign_not_found: "The selected prerequisite campaign does not exist."
missing_lecture: "A lecture must be selected."
lecture_not_found: "The selected lecture does not exist."
configs:
allowed_domains: "Allowed Domains"
prerequisite_campaign_id: "Prerequisite Campaign"
Expand Down
7 changes: 7 additions & 0 deletions spec/factories/registration_policies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@

trait :student_performance do
kind { :student_performance }
after(:build) do |policy|
unless policy.config && policy.config["lecture_id"]
lecture = create(:lecture, :with_organizational_stuff)
policy.config ||= {}
policy.config["lecture_id"] = lecture.id
end
end
end

trait :prerequisite_campaign do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require "rails_helper"

RSpec.describe(Registration::Policy::StudentPerformanceHandler, type: :model) do
let(:lecture) { create(:lecture, :with_organizational_stuff) }
let(:policy) do
build(:registration_policy, :student_performance,
config: { "lecture_id" => lecture.id })
end
let(:handler) { described_class.new(policy) }
let(:user) { create(:confirmed_user) }

describe "#evaluate" do
it "passes if user has a passed certification" do
create(:student_performance_certification, :passed,
lecture: lecture,
user: user,
certified_by: create(:confirmed_user))
result = handler.evaluate(user)
expect(result[:pass]).to be(true)
expect(result[:code]).to eq(:certification_passed)
end

it "fails if user has a failed certification" do
create(:student_performance_certification, :failed,
lecture: lecture,
user: user,
certified_by: create(:confirmed_user))
result = handler.evaluate(user)
expect(result[:pass]).to be(false)
expect(result[:code]).to eq(:certification_not_passed)
expect(result[:details][:certification_status]).to eq(:failed)
end

it "fails if user has a pending certification" do
create(:student_performance_certification, :pending,
lecture: lecture,
user: user)
result = handler.evaluate(user)
expect(result[:pass]).to be(false)
expect(result[:code]).to eq(:certification_not_passed)
expect(result[:details][:certification_status]).to eq(:pending)
end

it "fails with :missing if user has no certification" do
result = handler.evaluate(user)
expect(result[:pass]).to be(false)
expect(result[:code]).to eq(:certification_not_passed)
expect(result[:details][:certification_status]).to eq(:missing)
end

it "fails with configuration error if lecture_id is blank" do
policy.config = {}
result = handler.evaluate(user)
expect(result[:pass]).to be(false)
expect(result[:code]).to eq(:configuration_error)
end

it "fails with lecture_not_found if lecture was deleted" do
policy.config["lecture_id"] = 99_999
result = handler.evaluate(user)
expect(result[:pass]).to be(false)
expect(result[:code]).to eq(:lecture_not_found)
end
end

describe "#validate" do
it "adds error if lecture_id is missing" do
policy.config = {}
handler.validate
expect(policy.errors[:lecture_id])
.to include(I18n.t("registration.policy.errors.missing_lecture"))
end

it "adds error if lecture does not exist" do
policy.config["lecture_id"] = 99_999
handler.validate
expect(policy.errors[:lecture_id])
.to include(I18n.t("registration.policy.errors.lecture_not_found"))
end

it "does not add errors for a valid lecture" do
handler.validate
expect(policy.errors).to be_empty
end
end

describe "#summary" do
it "returns lecture title" do
expect(handler.summary).to eq(lecture.title)
end

it "returns nil if lecture does not exist" do
policy.config["lecture_id"] = 99_999
expect(handler.summary).to be_nil
end
end
end
Loading