English | Magyar
PHP library for calculating Hungarian higher-education admission scores from structured input data.
- PHP
^8.2
- Builds a
Studentdomain object from raw input (CreateStudentFromInputuse case). - Builds a
Schooldomain object from raw catalog input (CreateSchoolFromInputuse case). - Checks eligibility with a rule chain (
EligibilityRule). - Calculates scores with pluggable scoring policies (
ScoreEngine). - Returns
AdmissionScorewithbasic + bonus = total. - Caps bonus score at
100. - Stops eligibility evaluation at the first failing rule.
Current default business rules in the provided setup:
- Mandatory subjects must include:
magyar nyelv és irodalomtörténelemmatematika
- Every graduation result must be at least
20%. - The course required subject must be present, and it may also require
emeltlevel. - At least one required-selectable subject must be present.
- If a graduation subject appears multiple times, the highest matching result is used.
- High-level (
emelt) graduation results add50bonus points each. - Language exams add bonus (
B2 = 28,C1 = 40) per language, keeping only the best result per language.
Compared to the previous root orchestration API:
- Root-level orchestration moved into
Application\UseCase\*classes. - Core models and business rules moved into
Domain\*. - Repository implementations moved into
Infrastructure\*. ScoreCalculatorwas replaced byApplication\UseCase\CalculateAdmissionScore.StudentFactorywas replaced byApplication\UseCase\CreateStudentFromInput.SchoolCollectionorchestration was replaced byDomain\Repository\SchoolRepositorywithInfrastructure\Repository\InMemorySchoolRepository.ScoreCalculatorExceptionwas replaced byApplication\Exception\CalculateAdmissionScoreException.StudentFactoryExceptionwas replaced byApplication\Exception\CreateStudentFromInputException.- Validator middleware classes were replaced by
EligibilityRuleclasses. - Calculator middleware classes were replaced by
ScoreEngine+ScoringPolicyclasses.
As a dependency:
composer require green-zen-monk/admission-score-calculatorIn this repository (development):
composer installApplication\UseCase\CreateStudentFromInput: maps raw applicant input toDomain\Model\Student.Application\UseCase\CreateSchoolFromInput: maps raw school catalog input toDomain\Model\School.Application\UseCase\CalculateAdmissionScore: checks eligibility and returnsDomain\Model\Scoring\AdmissionScore.Application\Input\InputFormat: selectsarray,json, or customobjectmappers.Infrastructure\Repository\InMemorySchoolRepository: simple in-memorySchoolRepositoryexample for setup and tests; production code can use any adapter, including one backed by an ORM or entity repository.Domain\Eligibility\Contract\EligibilityRule: chainable eligibility rule contract.Domain\Scoring\ScoreEngine: runs scoring policies for base and bonus scores.Domain\Model\Scoring\AdmissionScore: immutable calculation result object.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use GreenZenMonk\AdmissionScoreCalculator\Domain\Eligibility\Rule\GraduationResultMinNotReachRule;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Eligibility\Rule\RequiredDefaultGraduationSubjectsRule;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Eligibility\Rule\RequiredGraduationSubjectRule;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Eligibility\Rule\RequiredSelectableGraduationSubjectsRule;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Model\Graduation\GraduationSubject;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Model\School;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Model\School\Course;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Model\School\Course\RequiredGraduationSubject;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Model\School\Course\RequiredGraduationSubjectCollection;
use GreenZenMonk\AdmissionScoreCalculator\Infrastructure\Repository\InMemorySchoolRepository;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Scoring\Policy\BasicScore\BestRequiredSelectableGraduationSubjectPolicy;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Scoring\Policy\BasicScore\RequiredGraduationSubjectPolicy;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Scoring\Policy\BonusScore\GraduationSubjectTypeHighPolicy;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Scoring\Policy\BonusScore\LanguageExamTypePolicy;
use GreenZenMonk\AdmissionScoreCalculator\Domain\Scoring\ScoreEngine;
use GreenZenMonk\AdmissionScoreCalculator\Application\Exception\CalculateAdmissionScoreException;
use GreenZenMonk\AdmissionScoreCalculator\Application\Exception\CreateStudentFromInputException;
use GreenZenMonk\AdmissionScoreCalculator\Application\Input\InputFormat;
use GreenZenMonk\AdmissionScoreCalculator\Application\UseCase\CalculateAdmissionScore;
use GreenZenMonk\AdmissionScoreCalculator\Application\UseCase\CreateStudentFromInput;
$schools = new InMemorySchoolRepository([
new School(
'ELTE',
'IK',
new Course(
'Programtervező informatikus',
new RequiredGraduationSubject(GraduationSubject::Mathematics),
new RequiredGraduationSubjectCollection([
new RequiredGraduationSubject(GraduationSubject::Biology),
new RequiredGraduationSubject(GraduationSubject::Physics),
new RequiredGraduationSubject(GraduationSubject::InformationTechnology),
new RequiredGraduationSubject(GraduationSubject::Chemistry),
])
)
),
]);
$input = [
'valasztott-szak' => [
'egyetem' => 'ELTE',
'kar' => 'IK',
'szak' => 'Programtervező informatikus',
],
'erettsegi-eredmenyek' => [
['nev' => 'magyar nyelv és irodalom', 'tipus' => 'közép', 'eredmeny' => '70%'],
['nev' => 'történelem', 'tipus' => 'közép', 'eredmeny' => '80%'],
['nev' => 'matematika', 'tipus' => 'emelt', 'eredmeny' => '90%'],
['nev' => 'angol nyelv', 'tipus' => 'közép', 'eredmeny' => '94%'],
['nev' => 'informatika', 'tipus' => 'közép', 'eredmeny' => '95%'],
],
'tobbletpontok' => [
['kategoria' => 'Nyelvvizsga', 'tipus' => 'B2', 'nyelv' => 'angol'],
['kategoria' => 'Nyelvvizsga', 'tipus' => 'C1', 'nyelv' => 'német'],
],
];
$createStudent = new CreateStudentFromInput($schools);
$eligibilityRule = new GraduationResultMinNotReachRule();
$eligibilityRule
->setNext(new RequiredDefaultGraduationSubjectsRule())
->setNext(new RequiredGraduationSubjectRule())
->setNext(new RequiredSelectableGraduationSubjectsRule());
$scoreEngine = new ScoreEngine([
new RequiredGraduationSubjectPolicy(),
new BestRequiredSelectableGraduationSubjectPolicy(),
new GraduationSubjectTypeHighPolicy(),
new LanguageExamTypePolicy(),
]);
$calculateScore = new CalculateAdmissionScore($eligibilityRule, $scoreEngine);
try {
$student = $createStudent->execute($input, InputFormat::ArrayInput);
$result = $calculateScore->execute($student);
print_r([
'basicScore' => $result->getBasicScore(),
'bonusScore' => $result->getBonusScore(),
'totalScore' => $result->getTotalScore(),
]);
} catch (CreateStudentFromInputException $e) {
echo 'Input error: ' . $e->getMessage() . PHP_EOL;
} catch (CalculateAdmissionScoreException $e) {
echo 'Applicant is not scoreable: ' . $e->getMessage() . PHP_EOL;
}CreateStudentFromInput::execute(mixed $rawInput, InputFormat $format = InputFormat::ArrayInput) supports:
InputFormat::ArrayInput: associative array input (default)InputFormat::Json: JSON string inputInputFormat::Object: requires a custom injected mapper implementation
Required top-level keys:
valasztott-szak.egyetem(string)valasztott-szak.kar(string)valasztott-szak.szak(string)erettsegi-eredmenyek(list<array>)tobbletpontok(list<array>)
erettsegi-eredmenyek[] fields:
nev(for example:matematika,angol nyelv,informatika)tipus:középoremelteredmeny:0-100with optional%suffix (85or85%)- Duplicate subject entries are allowed; scoring uses the highest applicable result.
tobbletpontok[] fields:
kategoria: currentlyNyelvvizsgatipus:B2orC1nyelv:angol,német,francia,olasz,orosz,spanyol
Student input intentionally follows the original homework payload shape and Hungarian field names. School input uses domain-oriented English keys and enum-like values.
CreateSchoolFromInput::execute(mixed $rawInput, InputFormat $format = InputFormat::ArrayInput) supports:
InputFormat::ArrayInput: associative array input (default)InputFormat::Json: JSON string inputInputFormat::Object: requires a custom injected mapper implementation
Required top-level keys:
university(string)faculty(string)course.name(string)course.required_graduation_subject(array)course.required_selectable_graduation_subjects(list<array>)
course.required_graduation_subject fields:
subject: domain graduation subject value (for examplemathematics)type:mediumorhigh
course.required_selectable_graduation_subjects[] fields:
subject: domain graduation subject value (for examplephysics,biology)type:mediumorhigh
Example payload:
[
'university' => 'ELTE',
'faculty' => 'IK',
'course' => [
'name' => 'Programtervezo informatikus',
'required_graduation_subject' => [
'subject' => 'mathematics',
'type' => 'medium',
],
'required_selectable_graduation_subjects' => [
['subject' => 'physics', 'type' => 'high'],
['subject' => 'biology', 'type' => 'medium'],
],
],
]Domain\Repository\SchoolRepository: plug in any repository adapter that can return aSchool, including implementations backed by ORM or entity repositories and external services.Application\Input\Mapping\Student\Contract\StudentInputMapperInterface: add support for custom applicant input formats.Application\Input\Mapping\School\Contract\SchoolInputMapperInterface: add support for custom school catalog input formats.Application\Contract\ViolationMessageResolver: customize eligibility error messages returned byCalculateAdmissionScore.
CalculateAdmissionScore::execute() returns AdmissionScore:
getBasicScore(): intgetBonusScore(): int(0..100)getTotalScore(): int
CalculateAdmissionScore::check() returns EligibilityResult without calculating the score.
EligibilityResult:
isEligible(): boolviolations(): list<Violation>
Possible ViolationCode values:
required_graduation_subject_missingmandatory_subjects_missingselectable_subject_missingsubject_below_minimum
Thrown when input data is missing or invalid, such as a missing key, wrong structure, invalid subject, type or result format, unknown selected school, or unsupported extra point category.
Thrown when school catalog input is missing or invalid, such as a missing key, wrong structure, invalid graduation subject value, invalid graduation subject type, or unsupported mapper format.
Thrown by CalculateAdmissionScore::execute() when eligibility fails.
The default exception message is the first violation code value (for example required_graduation_subject_missing).
You can override this by passing a custom ViolationMessageResolver implementation to CalculateAdmissionScore.
optional school-catalog input (array/json/object)
|
v
CreateSchoolFromInput
- resolves mapper by InputFormat
- creates Course
- creates RequiredGraduationSubjectCollection
|
v
School
|
v
usable by any SchoolRepository implementation
raw input (array/json/object)
|
v
CreateStudentFromInput
- resolves mapper by InputFormat
- looks up selected program via SchoolRepository
- accepts any repository implementation that returns a School
- repository may wrap ORM/entity repositories or other external data sources
- repository may return preloaded or externally constructed School data
- creates GraduationResultCollection
- creates ExtraPointCollection
|
v
Student
|
v
CalculateAdmissionScore
|
+--> Eligibility chain (Chain of Responsibility)
| 1) GraduationResultMinNotReachRule
| 2) RequiredDefaultGraduationSubjectsRule
| 3) RequiredGraduationSubjectRule
| 4) RequiredSelectableGraduationSubjectsRule
|
+--> ScoreEngine policies
1) RequiredGraduationSubjectPolicy
2) BestRequiredSelectableGraduationSubjectPolicy
3) GraduationSubjectTypeHighPolicy
4) LanguageExamTypePolicy
-> AdmissionScore (basic, bonus, total)
make docker-build
make docker-up
make composer-install
make test-run
make docker-downAdditional targets:
make test-debug-run
make test-coverage-run
make docker-shellComposer scripts:
composer test
composer test:debug
composer test:coveragePHPUnit configuration:
tests/Unitis registered as theUnitsuite.tests/Integrationis registered as theIntegrationsuite.composer.jsonexposesTests\\throughautoload-devfor the current test namespace layout.
Docker Makefile equivalents:
make test-run
make test-debug-run
make test-coverage-runCoverage report output (phpunit.coverage.xml):
build/coverage/htmlbuild/coverage/clover.xmlbuild/coverage/cobertura.xmlbuild/coverage/xml
PHPStan (level 8) and PHP-CS-Fixer (PSR-12) are configured.
make phpstan
make cs-check
make cs-fixEquivalent Composer scripts:
composer phpstan
composer cs-check
composer cs-fixREADME.mdis the primary source for repository-facing documentation.README.hu.mdmust remain functionally equivalent to the English version.- New sections should be added in both languages.