Commit 1b630c4
committed
Add COPEN transition plans, with COPEN to CTFYS as the first
Starts #66. A COPEN student takes one common year and then transfers into a
five-year programme, so what they actually study is published nowhere: COPEN's
plan stops after year 1, and the target's plan assumes its own year 1. Picking
COPEN and then a continuation programme now composes the real thing - COPEN year
1 followed by years 2-3 of the target, with the transition plan applied.
THE PLAN RECORDS THE DIFFERENCE, NOT A COURSE LIST
src/data/transitions.json holds one entry per (from, to) pair with three shapes
of difference:
exempt a target course the student does not take because a source course
credits it - CTFYS drops SF1544, credited by COPEN's SF1546
added a course from neither published plan - SF1920 in P3 of year 2, taken
with CELTE's second year
moved a target course shifted to a later year, keeping its periods; unused
today but the natural shape for "the target's own course, later"
A hand-written combined course list would go stale the moment either programme
is re-extracted, and silently - nothing would report that the composition no
longer matched. Recording only the difference makes composeTransition a pure
function of both programmes' current data.
Year numbering needs no adjustment: the source contributes year 1 and the target
years 2-3, so the composed years already read 1/2/3.
WHY SF1920 AND NOT SF1922
A COPEN student has no probability course, and CTFYS teaches SF1922 in its year
1, which the transfer student was never present for. Issue #66 suggested taking
SF1922 in P4 of year 2 with CTFYS's year-1 students. Taking SF1920 in P3 with
CELTE's second year is better on both counts - the student does not wait until
P4, and the credits land in the period the exemption emptied:
SF1922 moved to y2 P4 15 / 14 / 10 / 21
SF1920 from CELTE in P3 15 / 14 / 16 / 15
Both total 60 hp, which is exactly why the load check has to be per period. The
residual +-1 is structural: SF1544 carried 1 hp in P2 and 5 in P3, while SF1920
is 6 hp all in P3.
An added course needs its own data, since CELTE is not a programme this app
models. It is embedded in the plan in the same raw shape a data file uses and
parsed with parseCourseEntries - the loader's own parser, split out for this
purpose so the period/credit normalisation is not reimplemented. SF1920's values
come from the live sources the extractor uses: 6 hp and P3 from CELTE's year-2
study plan, TEN1 and the SF1625 prerequisite from its course page. That
prerequisite resolves inside the composed plan, because SF1625 is one of the
nine COPEN courses, and the arrow is drawn.
cosmeticsGroup is needed in practice: without it an added course falls to the
default colour, which beside the light-tone palette reads as a bug rather than as
"this came from elsewhere". SF1920 declares Matematik and renders identically to
CTFYS's other maths courses.
PREREQUISITE ARROWS FOLLOW THE COURSE ACTUALLY TAKEN
A target course in years 2-3 states its prerequisites in the target's own terms:
CTFYS's SF1683 and SI1146 both require SF1674. A transfer student never took
SF1674 - they took SF1626, the same subject - so an arrow drawn to the letter
starts from a course that is not in their plan and simply vanishes, leaving those
courses looking as though they had no prerequisites at all.
credited[] is therefore a list of objects, each able to say what it replaces, and
redirectPrerequisites rewrites every reference through those equivalences. It is a
general rule over the plan's data rather than a list of special cases. Exactly
five CTFYS year-1 courses are referenced from years 2-3, and all five resolve:
SF1674 -> SF1626 arrows into SF1683, SI1146
SF1672 -> SF1624 arrow into SF1681
DD1331 -> DD1310 arrow into DD1327
SG1112 -> SG1133 arrows into SE1055, SG1113
SK1104 -> SK1115 arrow into SH1014
Three fields feed the same map, so an equivalence is stated once:
credited[].replaces, exempt[].creditedBy (SF1546 -> SF1544, which is why SF1546
carries no 'replaces'), and added[].substitutesFor. A reference that survives the
rewrite but names a course outside the composed plan is reported - that means an
equivalence is missing, and the symptom would otherwise be a silently absent
arrow. validate-data checks the same invariant statically, so a missing
equivalence fails in CI rather than only in the browser.
COSMETICS HAVE TO BE MERGED
The two files share no course codes, so rendering a composed chart from the
target's cosmetics alone drew all nine COPEN courses in the default colour.
mergeCosmetics merges by group name, so Matematik from both programmes becomes
one legend row, with the target's colour winning on conflict: CTFYS has
Ingenjörsämnen brick where COPEN has it turquoise. A group only the source has
takes the first unused family rather than its own, because COPEN's Programmering
is brick, which CTFYS already spends on Ingenjörsämnen. COPEN+CTFYS lands on
exactly five families, which is the documented hard cap; an overflow is reported
and those courses fall back to the default colour.
FULL-TIME LOAD IS CHECKED PER PERIOD
The same signal validate-data applies to the programme files, applied to the
composition, because a swap can balance across a year while leaving individual
periods lopsided and the year total hides it. Composed year 2 is 15/14/16/15;
the ±1 is reported in the UI rather than corrected, since where the plan puts a
course is the program director's call.
THE CHART IS TITLED FOR BOTH PROGRAMMES
"Civilingenjörsutbildning Öppen ingång -> Teknisk fysik (COPEN -> CTFYS)". The
target's name is shortened because the qualification is identical on both sides
and pure noise the second time. shortProgramName strips a fixed set of openers
rather than the longest common word prefix, which looks more general but is wrong
on the English names: "Degree Program in Engineering - Open Entrance" and "Degree
Program in Engineering Physics" share "Degree Program in Engineering", which would
reduce CTFYS to "Physics". An unrecognised name falls through unchanged. The
composed code also reaches the export audit stamp, which is wanted; export
filenames are fixed strings and unaffected.
VALIDATION
validate-data cross-checks each plan against both programmes in both directions.
`credited` must match what the source actually teaches in those years - a course
added to COPEN that nobody added to the plan would otherwise be dropped silently
- and every exempt/moved code must exist in the target with the year the plan
claims. verified: false warns, exactly as in programs.json.
Verified in headless Chrome, COPEN + CTFYS: three years render; SF1544 and
SF1922 both absent; SF1920 drawn at year 2 P3 in the Matematik colour, with the
SF1625 prerequisite arrow; all nine COPEN courses present and coloured; every
CTFYS year-1-only course absent; the notice line naming what was credited,
dropped and added; and the load line showing 15/14/16/15. No console or page
errors. COPEN alone is unchanged.
Arrows verified against CTFYS alone: every arrow that started from one of the five
year-1 courses now starts from its COPEN equivalent, and no arrow in the composed
view starts from a CTFYS year-1 course. lint, tsc --noEmit, validate-data and the
build are clean.
WHAT IS NOT DONE
Only COPEN -> CTFYS exists, and it is marked verified: false. The other targets
(CINEK, CTMAT, ...) need their plans from @sellberg in whatever format they
arrive; the schema above is the target to map them onto, and adding one is a
data-only change.1 parent bad9109 commit 1b630c4
7 files changed
Lines changed: 1224 additions & 5 deletions
File tree
- scripts
- src
- app
- data
- lib
- types
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
574 | 574 | | |
575 | 575 | | |
576 | 576 | | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
577 | 712 | | |
578 | 713 | | |
579 | 714 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
795 | 795 | | |
796 | 796 | | |
797 | 797 | | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
| 849 | + | |
| 850 | + | |
| 851 | + | |
| 852 | + | |
| 853 | + | |
| 854 | + | |
| 855 | + | |
| 856 | + | |
| 857 | + | |
| 858 | + | |
| 859 | + | |
| 860 | + | |
| 861 | + | |
| 862 | + | |
| 863 | + | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
| 883 | + | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
| 897 | + | |
| 898 | + | |
| 899 | + | |
| 900 | + | |
| 901 | + | |
| 902 | + | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
| 924 | + | |
| 925 | + | |
| 926 | + | |
| 927 | + | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
| 959 | + | |
| 960 | + | |
| 961 | + | |
| 962 | + | |
| 963 | + | |
| 964 | + | |
| 965 | + | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
| 969 | + | |
| 970 | + | |
798 | 971 | | |
799 | 972 | | |
800 | 973 | | |
| |||
893 | 1066 | | |
894 | 1067 | | |
895 | 1068 | | |
| 1069 | + | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
| 1073 | + | |
| 1074 | + | |
| 1075 | + | |
| 1076 | + | |
| 1077 | + | |
| 1078 | + | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
896 | 1103 | | |
897 | 1104 | | |
898 | 1105 | | |
| |||
0 commit comments