Grading Boundaries Wrong in Supervisor View #98

Merged
ansv7779 merged 23 commits from 97-grading-boundaries-wrong-in-suervisor-view into develop 2025-02-21 14:06:29 +01:00
8 changed files with 408 additions and 201 deletions
core/src/main/java/se/su/dsv/scipro
view/src
main/java/se/su/dsv/scipro/grading
test/java/se/su/dsv/scipro

@ -25,6 +25,7 @@ import se.su.dsv.scipro.profiles.CurrentProfile;
import se.su.dsv.scipro.profiles.Profiles; import se.su.dsv.scipro.profiles.Profiles;
import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.report.AbstractGradingCriterion; import se.su.dsv.scipro.report.AbstractGradingCriterion;
import se.su.dsv.scipro.report.GradeLimit;
import se.su.dsv.scipro.report.GradingCriterionPointTemplate; import se.su.dsv.scipro.report.GradingCriterionPointTemplate;
import se.su.dsv.scipro.report.GradingReportTemplate; import se.su.dsv.scipro.report.GradingReportTemplate;
import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; import se.su.dsv.scipro.reviewing.ReviewerAssignmentService;
@ -194,11 +195,18 @@ public class DataInitializer implements Lifecycle {
} }
private void createProjects() { private void createProjects() {
createProject(PROJECT_1, eric_employee, sture_student, stina_student, eve_employee); createProject(PROJECT_1, eric_employee, sture_student, stina_student, eve_employee, 135);
project2 = createProject(PROJECT_2, eve_employee, sid_student, simon_student, eric_employee); project2 = createProject(PROJECT_2, eve_employee, sid_student, simon_student, eric_employee, 246);
} }
private Project createProject(String title, User headSupervisor, User student1, User student2, User reviewer) { private Project createProject(
String title,
User headSupervisor,
User student1,
User student2,
User reviewer,
int daisyId
) {
Project project = Project.builder() Project project = Project.builder()
.title(title) .title(title)
.projectType(bachelorClass) .projectType(bachelorClass)
@ -208,6 +216,7 @@ public class DataInitializer implements Lifecycle {
project.addProjectParticipant(student2); project.addProjectParticipant(student2);
project.addProjectParticipant(student1); project.addProjectParticipant(student1);
project.addReviewer(reviewer); project.addReviewer(reviewer);
project.setIdentifier(daisyId);
save(project); save(project);
return project; return project;
} }
@ -218,24 +227,25 @@ public class DataInitializer implements Lifecycle {
eric_employee = createEmployee(EMPLOYEE_1); eric_employee = createEmployee(EMPLOYEE_1);
eve_employee = createEmployee(EMPLOYEE_2); eve_employee = createEmployee(EMPLOYEE_2);
sture_student = createStudent(STUDENT_1); sture_student = createStudent(STUDENT_1, 3);
stina_student = createStudent(STUDENT_2); stina_student = createStudent(STUDENT_2, 5);
sid_student = createStudent(STUDENT_3); sid_student = createStudent(STUDENT_3, 7);
simon_student = createStudent(STUDENT_4); simon_student = createStudent(STUDENT_4, 11);
// Used to test submitting ideas // Used to test submitting ideas
// can not be used as author on any idea // can not be used as author on any idea
// can not be used as author on any project // can not be used as author on any project
createStudent("Stig"); createStudent("Stig", 13);
// Used to test assign supervisor to student idea // Used to test assign supervisor to student idea
// this student has a submitted idea, which has not assigned supervisor // this student has a submitted idea, which has not assigned supervisor
// don't use this student for anything else // don't use this student for anything else
sofia_student = createStudent("Sofia"); sofia_student = createStudent("Sofia", 17);
} }
private User createStudent(String firstName) { private User createStudent(String firstName, int identifier) {
User user = createUser(firstName, STUDENT_LAST); User user = createUser(firstName, STUDENT_LAST);
user.setIdentifier(identifier);
createBeta(user); createBeta(user);
return user; return user;
} }
@ -360,6 +370,36 @@ public class DataInitializer implements Lifecycle {
LocalDate.of(2024, Month.JANUARY, 1) LocalDate.of(2024, Month.JANUARY, 1)
); );
gradingReportTemplate.setFailingGrade("F");
List<GradeLimit> gradeLimits = new ArrayList<>();
GradeLimit gradeLimit = new GradeLimit();
gradeLimit.setGrade("A");
gradeLimit.setLowerLimit(29);
gradeLimits.add(gradeLimit);
gradeLimit = new GradeLimit();
gradeLimit.setGrade("B");
gradeLimit.setLowerLimit(26);
gradeLimits.add(gradeLimit);
gradeLimit = new GradeLimit();
gradeLimit.setGrade("C");
gradeLimit.setLowerLimit(22);
gradeLimits.add(gradeLimit);
Review

Extracting a method creating the GradeLimit here may have been justified.

Extracting a method creating the `GradeLimit` here may have been justified.
gradeLimit = new GradeLimit();
gradeLimit.setGrade("D");
gradeLimit.setLowerLimit(19);
gradeLimits.add(gradeLimit);
gradeLimit = new GradeLimit();
gradeLimit.setGrade("E");
gradeLimit.setLowerLimit(16);
gradeLimits.add(gradeLimit);
gradingReportTemplate.setGradeLimits(gradeLimits);
// U1 Sammanfattning & U1 Abstract
List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = initPointTemplates(); List<GradingCriterionPointTemplate> gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -368,7 +408,7 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att uppsatsens sammanfattning korrekt återspeglar uppsatsens innehåll genom att beskriva problem, frågeställning, metodval, metodtillämpning, resultat och slutsatser samt att den kan läsas och förstås fristående från uppsatsen." "För 1 poäng krävs: att uppsatsens sammanfattning korrekt återspeglar uppsatsens innehåll genom att beskriva problem, frågeställning, metodval, metodtillämpning, resultat och slutsatser samt att den kan läsas och förstås fristående från uppsatsen."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the abstract of the thesis accurately portrays the contents of the thesis by describing the problem, the research question, the choice and application of the research methods, the result, and conclusions and that it can be read and understood separately from the thesis." "For 1 point, the abstract must accurately reflect the thesis content by describing the problem, research question, method selection, method application, results, and conclusions, and it should be understandable on its own, separate from the thesis."
) )
.build() .build()
); );
@ -379,13 +419,14 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U2 Introduktion & U2 Introduction
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(1) .point(1)
.description("För 1 poäng krävs: att uppsatsen ger en introduktion till uppsatsens ämne och problem.") .description("För 1 poäng krävs: att uppsatsen ger en introduktion till uppsatsens ämne och problem.")
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the thesis introduces the subject and problem of the thesis." "For 1 point, the thesis must provide an introduction to the subject and problem of the thesis."
) )
.build() .build()
); );
@ -396,6 +437,7 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U3 Problem & U3 Problem
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -404,21 +446,22 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att det finns ett problem av generellt intresse som helt eller delvis kan lösas genom att frågeställningen besvaras." "För 1 poäng krävs: att det finns ett problem av generellt intresse som helt eller delvis kan lösas genom att frågeställningen besvaras."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that a problem of general interest exists that entirely or partially can be solved by answering the research question." "For 1 point, there must be a problem of general interest that can be wholly or partially solved by answering the research question."
) )
.build() .build()
); );
gradingReportTemplate.addProjectCriterion("U3 Problem", "U3 Problem", 1, gradingCriterionPointTemplates); gradingReportTemplate.addProjectCriterion("U3 Problem", "U3 Problem", 1, gradingCriterionPointTemplates);
// U4 Frågeställning & U4 Research Question
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(1) .point(1)
.description( .description(
"För 1 poäng krävs: en tydligt formulerad och väl avgränsad frågeställning som är av generellt intresse. Studenten skall självständigt identifiera och formulera frågeställningen. Frågeställningen skall motiveras utifrån problemet så att det är tydligt hur svaret på frågan löser en del av eller hela problemet." "För 1 poäng krävs: en tydligt formulerad och väl avgränsad frågeställning som är av generellt intresse. Studenten ska självständigt identifiera och formulera frågeställningen. Frågeställningen ska motiveras utifrån problemet så att det är tydligt hur svaret på frågan löser en del av eller hela problemet."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: a clearly formulated and delimited research question that is of general interest. The student is responsible for identifying and formulating the research question. The research question should derive from the presented problem so that it is clear how the answer to the research question solves a portion or the entire problem." "For 1 point, a clearly formulated and well-defined research question of general interest is required. The student must independently identify and formulate the research question. The research question should be justified based on the problem so that it is clear howthe answer addresses part or all of the problem."
) )
.build() .build()
); );
@ -426,10 +469,10 @@ public class DataInitializer implements Lifecycle {
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(2) .point(2)
.description( .description(
"För 2 poäng krävs dessutom: en innovativ frågeställning som ger förutsättningar för att arbetet skall kunna ge ett signifikant bidrag." "För 2 poäng krävs dessutom: en innovativ frågeställning som ger förutsättningar för att arbetet ska kunna ge ett signifikant bidrag."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: an innovative research question that provides necessary conditions so that the thesis could provide a significant scientific contribution." "For 2 points, an innovative research question that enables the work to make a significant contribution is also required."
) )
.build() .build()
); );
@ -440,15 +483,16 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U5 Vetenskaplig förankring & U5 Scientific Grounding
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(1) .point(1)
.description( .description(
"För 1 poäng krävs: att uppsatsen ger en ämnesmässig förankring utifrån tidigare vetenskapliga arbeten. Det skall också framgå till vilket område inom data- och systemvetenskap som arbetet bidrar genom en redovisning av de vetenskapliga arbeten som uppsatsen relaterar till." "För 1 poäng krävs: att uppsatsen ger en ämnesmässig förankring utifrån tidigare vetenskapliga arbeten. Det ska också framgå till vilket område inom data- och systemvetenskap som arbetet bidrar genom en redovisning av de vetenskapliga arbeten som uppsatsen relaterar till. "
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the thesis provides a base for the topic of the thesis based on previous scientific research. The area within computer and systems sciences to which the thesis contributes should also be named by presenting the scientific research to which the thesis refers." "For 1 point, the thesis must provide subject matter grounding based on previous scientific work. The thesis should clearly indicate which specific area of computer and systems sciences it contributes to by referencing the relevant scientific research."
) )
.build() .build()
); );
@ -459,17 +503,18 @@ public class DataInitializer implements Lifecycle {
"För 2 poäng krävs dessutom: att en djupgående och kritisk diskussion förs om hur arbetet bygger vidare på tidigare vetenskapliga arbeten." "För 2 poäng krävs dessutom: att en djupgående och kritisk diskussion förs om hur arbetet bygger vidare på tidigare vetenskapliga arbeten."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: that a deep and critical discussion is made about how the thesis builds upon previous scientific research." "For 2 points, an in-depth and critical discussion of how the work builds on previous scientific work is also required."
) )
.build() .build()
); );
gradingReportTemplate.addProjectCriterion( gradingReportTemplate.addProjectCriterion(
"U5 Vetenskaplig förankring", "U5 Vetenskaplig förankring",
"U5 Scientific base", "U5 Scientific Grouding",
1, 1,
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U6 Methodval & U6 Method Choice
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -478,7 +523,7 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att valet av forskningsstrategier och forskningsmetoder tydligt beskrivs och motiveras utifrån forskningsfrågan, att minst en alternativ forskningsstrategi och forskningsmetod som kan användas för att angripa frågeställningen diskuteras, samt att relevanta etiska överväganden diskuteras." "För 1 poäng krävs: att valet av forskningsstrategier och forskningsmetoder tydligt beskrivs och motiveras utifrån forskningsfrågan, att minst en alternativ forskningsstrategi och forskningsmetod som kan användas för att angripa frågeställningen diskuteras, samt att relevanta etiska överväganden diskuteras."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the choice of a research strategy and research methods is clearly described and motivated based on the research question, that at least one alternative research strategy and method that could be used to solve the research question is discussed, as well as that relevant ethical considerations are discussed." "For 1 point, the selection of research strategies and research methods must be clearly described and justified based on the research question. At least one alternative research strategy and method that could be used to address the research question should be discussed, and relevant ethical considerations should be addressed."
) )
.build() .build()
); );
@ -489,7 +534,7 @@ public class DataInitializer implements Lifecycle {
"För 2 poäng krävs dessutom: att alternativa tillämpliga forskningsstrategier och forskningsmetoder diskuteras utförligt och att ett djupgående resonemang kring strategi- och metodval förs, där motiven för gjorda val tydligt framgår." "För 2 poäng krävs dessutom: att alternativa tillämpliga forskningsstrategier och forskningsmetoder diskuteras utförligt och att ett djupgående resonemang kring strategi- och metodval förs, där motiven för gjorda val tydligt framgår."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: that alternative, applicable research strategies and methods are comprehensively discussed and that a profound reasoning about the chosen strategies and methods is made, where the motives for choices made are clearly evident." "For 2 points, a thorough discussion of applicable alternative research strategies and methods and a detailed argument for the choices made are required."
) )
.build() .build()
); );
@ -500,15 +545,16 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U7 Metodtillämpning & U7 Method Application
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(1) .point(1)
.description( .description(
"För 1 poäng krävs: att tillämpningen av valda forskningsstrategier och forskningsmetoder är tydligt beskriven, att användning av programvaruverktyg beskrivs, samt att relevanta etiska aspekter diskuteras." "För 1 poäng krävs: att tillämpningen av valda forskningsstrategier och forskningsmetoder är tydligt beskriven, att användning av programvaruverktyg beskrivs, samt att relevanta etiska aspekter diskuteras. Beskrivningen av programvaruverktyg ska innehålla diskussion av särskilda verktyg för datainsamling, analys samt AI-baserad text och innehållsgenerering. "
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the application of the chosen scientific strategies and methods are clearly described, that the use of software tools is described, and that relevant ethical aspects are discussed." "For 1 point, the application of selected research strategies and methods must be clearly described, the use of software tools should be described, and relevant ethical aspects should be discussed. The description of software tools should include a discussion of specific tools for data collection, analysis, and AI-based text and content generation."
) )
.build() .build()
); );
@ -519,17 +565,18 @@ public class DataInitializer implements Lifecycle {
"För 2 poäng krävs dessutom: att tillämpningen av forskningsstrategier och forskningsmetoder är genomförd i enlighet med de krav som dessa ställer och att det finns en tydlig argumentation för detta." "För 2 poäng krävs dessutom: att tillämpningen av forskningsstrategier och forskningsmetoder är genomförd i enlighet med de krav som dessa ställer och att det finns en tydlig argumentation för detta."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: that the application of research strategies and methods are done in accordance to the demands of said methods and strategies and that a clear argumentation exists for this." "For 2 points, the application of research strategies and methods should be conducted in accordance with their requirements, with a clear argument for this."
) )
.build() .build()
); );
gradingReportTemplate.addProjectCriterion( gradingReportTemplate.addProjectCriterion(
"U7 Metodtillämpning", "U7 Metodtillämpning",
"U7 Application of research method", "U7 Method Application",
1, 1,
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U8 Resultat & U8 Results
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -538,7 +585,7 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att resultaten är av sådan omfattning och kvalitet och presenteras på ett sådant sätt att frågeställningen till viss del kan besvaras." "För 1 poäng krävs: att resultaten är av sådan omfattning och kvalitet och presenteras på ett sådant sätt att frågeställningen till viss del kan besvaras."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the results are of such a magnitude and quality and are presented in such a way that the research question can to some extent be answered." "For 1 point, the results should be of sufficient scope and quality and be presented in such a way that the research question can be partially answered."
) )
.build() .build()
); );
@ -549,21 +596,22 @@ public class DataInitializer implements Lifecycle {
"För 2 poäng krävs dessutom: att resultaten är av tillräcklig omfattning och av hög kvalitet, så att frågeställningen till stor del kan besvaras." "För 2 poäng krävs dessutom: att resultaten är av tillräcklig omfattning och av hög kvalitet, så att frågeställningen till stor del kan besvaras."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: that the results are of sufficient magnitude and high quality so that the research question can to a great extent be answered." "For 2 points, the results should be of sufficient scope and high quality so that the research question can be largely answered."
) )
.build() .build()
); );
gradingReportTemplate.addProjectCriterion("U8 Resultat", "U8 Result", 1, gradingCriterionPointTemplates); gradingReportTemplate.addProjectCriterion("U8 Resultat", "U8 Result", 1, gradingCriterionPointTemplates);
// U9 Slutsatser och diskussion & U9 Conclusions and Discussion
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(1) .point(1)
.description( .description(
"För 1 poäng krävs: att frågeställningen ges ett tydligt svar, att begränsningar i studiens upplägg och deras påverkan på slutsatserna diskuteras liksom hur resultaten relaterar till tidigare studier, att möjliga framtida studier med utgångspunkt från den aktuella studien diskuteras, samt att etiska och samhälleliga konsekvenser av studiens slutsatser diskuteras." "För 1 poäng krävs: att frågeställningen ges ett tydligt svar, att begränsningar i studiens upplägg och deras påverkan på slutsatserna diskuteras liksom hur resultaten relaterar till tidigare studier, att möjliga framtida studier med utgångspunkt från den aktuella studien diskuteras, att etiska och samhälleliga konsekvenser av studiens slutsatser diskuteras, samt att användningen av IT verktyg (inkl AI verktyg) för uppsatsarbetet beskrivs och diskuteras."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the research question has a clear answer, that the limitations in the design of the study and their impact on the conclusions are discussed as well as how the results relate to previous research; that possible future research based on the study in the thesis is discussed; and that ethical and societal consequences of the conclusions in the thesis are discussed." "For 1 point, the research question should be clearly answered, limitations in the study design and their impact on the conclusions should be discussed, as well as how the results relate to previous studies, possible future studies based on the current study should be discussed, ethical and societal implications of the studys conclusions should be discussed, and the use of IT tools (including AI tools) for the thesis work should be described and discussed."
) )
.build() .build()
); );
@ -571,10 +619,10 @@ public class DataInitializer implements Lifecycle {
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(2) .point(2)
.description( .description(
"För 2 poäng krävs dessutom: att studiens begränsningar diskuteras utförligt och att ett djupgående resonemang om möjliga och relevanta framtida studier förs." "För 2 poäng krävs dessutom: att studiens begränsningar diskuteras utförligt och att ett djupgående resonemangommöjliga och relevanta framtida studier förs."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: that the limitations of the study are thoroughly discussed and that a profound reasoning about possible and relevant future studies is made." "For 2 points, the studys limitations should be discussed in depth, and a detailed discussion of possible and relevant future studies should be provided."
) )
.build() .build()
); );
@ -585,6 +633,7 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U10 Form, struktur och språk & U10 Form, Structure, and Language
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -593,7 +642,18 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att uppsatsen är indelad i tydliga och väl sammanhållna avsnitt och uppfyller grundläggande krav på layout samt att texten är skriven med ett adekvat och professionellt språkbruk." "För 1 poäng krävs: att uppsatsen är indelad i tydliga och väl sammanhållna avsnitt och uppfyller grundläggande krav på layout samt att texten är skriven med ett adekvat och professionellt språkbruk."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the thesis is divided into distinct, logical, and coherent sections; that it fulfils the fundamental layout requirements; and that the text is written with an adequate and professional language." "For 1 point, the thesis must be divided into clear and cohesive sections, meet basic layout requirements, and the text should be written in appropriate and professional language."
)
.build()
);
gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder()
.point(2)
.description(
"För 2 poäng krävs dessutom att uppsatsen uppfyller höga krav på layout samt att texten är skriven med ett adekvat, professionellt och akademiskt språkbruk."
)
.descriptionEn(
"For 2 points, the thesis must also meet high layout standards, and the text should be written in appropriate, professional, and academic language."
) )
.build() .build()
); );
@ -604,15 +664,27 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U11 Argumentation & U11 Argumentation
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(1) .point(1)
.description( .description(
"För 1 poäng krävs: att argumentationen är välgrundad, logiskt sammanhållen, koncis, tydlig och lättbegriplig." "För 1 poäng krävs: att argumentationen i stort är välgrundad, logiskt sammanhållen, koncis, tydlig och lättbegriplig."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the argumentation is well grounded, logically coherent, concise, clear, and easily understood." "For 1 point, the argumentation should generally be well-founded, logically coherent, concise, clear, and understandable."
)
.build()
);
gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder()
.point(2)
.description(
"För 2 poäng krävs dessutom: att argumentationen i hög grad är välgrundad, logiskt sammanhållen, koncis, tydlig och lättbegriplig."
)
.descriptionEn(
"For 2 points, the argumentation should be highly well-founded, logically coherent, concise, clear, and understandable."
) )
.build() .build()
); );
@ -623,6 +695,7 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U12 Källhänvisningar och dokumentation & U12 References and Documentation
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -631,26 +704,27 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att tidigare arbeten refereras till på ett korrekt sätt enligt ett vedertaget referenssystem, att en tydlig förteckning över använda källor anges enligt samma system, att samtliga citat från tidigare arbeten anges tydligt, samt att relevanta bilagor inkluderas." "För 1 poäng krävs: att tidigare arbeten refereras till på ett korrekt sätt enligt ett vedertaget referenssystem, att en tydlig förteckning över använda källor anges enligt samma system, att samtliga citat från tidigare arbeten anges tydligt, samt att relevanta bilagor inkluderas."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that references in the thesis to previous research are made in a correct way according to a recognized reference system, that a clear listing of used references is made in the same system, that all quotes from previous work are clearly specified, and that relevant supplemental attachments are included." "For 1 point, previous work must be correctly referenced according to an accepted referencing system, a clear list of sources used should be provided according to the same system, all quotes from previous work should be clearly cited, and relevant appendices should be included."
) )
.build() .build()
); );
gradingReportTemplate.addProjectCriterion( gradingReportTemplate.addProjectCriterion(
"U12 Källhänvisningar och dokumentation", "U12 Källhänvisningar och dokumentation",
"U12 References and documentation", "U12 References and Documentation",
1, 1,
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// U13 Originalitet och signifikans & U13 Originality and Significance
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(1) .point(1)
.description( .description(
"För 1 poäng krävs: att arbetet ger originella och signifikanta kunskapsbidrag, till exempel i form av idéer, artefakter, produkter eller tjänster." "För 1 p krävs: att uppsatsen ger nya kunskapsbidrag, till exempel för utveckling av nya idéer, artefakter eller produkter. "
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the thesis contributes with significant or original research contributions, in the form of new ideas, artifacts, products, or services." "For 1 point, the thesis must provide new contributions to knowledge, in the form of new ideas, artifacts, or products."
) )
.build() .build()
); );
@ -658,10 +732,10 @@ public class DataInitializer implements Lifecycle {
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(2) .point(2)
.description( .description(
"För 2 poäng krävs dessutom: att kunskapsbidragen är av sådan kvalitet att arbetet skulle kunna presenteras på en vetenskaplig workshop av god kvalitet." "För 2 poäng krävs dessutom: att uppsatsen innehåller forskningsbidrag som har potential att bidra till utveckling inom aktuellt forskningsområde."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: that the research contributions are of such quality that the thesis could be presented in a scientific workshop of good quality." "For 2 points, the thesis must also contain research contributions with the potential to advance the current research area."
) )
.build() .build()
); );
@ -669,22 +743,21 @@ public class DataInitializer implements Lifecycle {
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
.point(3) .point(3)
.description( .description(
"För 3 poäng krävs dessutom: att kunskapsbidragen är av sådan kvalitet att arbetet skulle kunna presenteras på en vetenskaplig konferens av god kvalitet eller kunna ligga till grund för användbara lösningar, till exempel i form av kommersialiserbara produkter." "För 3 poäng krävs dessutom: att uppsatsen innehåller resultat av hög kvalitet med potential att bidra till state-of-the-art inom aktuellt forskningsområde eller att de kan ligga till grund för användbara lösningar, till exempel vid utveckling av kommersialiserbara produkter."
) )
.descriptionEn( .descriptionEn(
"For 3 points the following is also required: that the research contributions are of such quality that the thesis could be presented at an academic conference of good quality or that they could be a basis for useful solutions, for example commercializable products." "For 3 points, the thesis must also include high-quality results with the potential to contribute to the state-of-the-art within the current research area or to form the basis for usable solutions, such as in the development of commercializable products."
) )
.build() .build()
); );
gradingReportTemplate gradingReportTemplate.addProjectCriterion(
.addProjectCriterion( "U13 Originalitet och signifikans",
"U13 Originalitet och signifikans", "U13 Originality and significance",
"U13 Originality and significance", 0,
0, gradingCriterionPointTemplates
gradingCriterionPointTemplates );
)
.setFx(false);
// Ö1 Oppositionsrapport & Ö1 Opposition Report
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -693,7 +766,7 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att oppositionsrapporten ger en kort sammanfattning av det utvärderade arbetet, resonerar kring uppsatsens vetenskapliga förankring, originalitet, signifikans, formulering av problem och frågeställning, metodval och metodtillämpning, samt innehåller tydliga förslag till förbättringar; och att oppositionsrapporten inkluderar både redigering och korrekturläsning av uppsatsen." "För 1 poäng krävs: att oppositionsrapporten ger en kort sammanfattning av det utvärderade arbetet, resonerar kring uppsatsens vetenskapliga förankring, originalitet, signifikans, formulering av problem och frågeställning, metodval och metodtillämpning, samt innehåller tydliga förslag till förbättringar; och att oppositionsrapporten inkluderar både redigering och korrekturläsning av uppsatsen."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the opposition report provides a short summary of the evaluated thesis, that it deliberates about the scientific basis, originality, significance, and formulation of the problem and research question, as well as that it contains clear suggestions for improvements; and that the opposition report includes proofreading as well as editing of the thesis." "For 1 point, the opposition report must provide a short summary of the evaluated work, discuss the thesiss scientific grounding, originality, significance, formulation of problem and research question, method choice, and method application, and contain clear suggestions for improvement. The opposition report must include both editing and proofreading of the thesis."
) )
.build() .build()
); );
@ -704,7 +777,7 @@ public class DataInitializer implements Lifecycle {
"För 2 poäng krävs dessutom: att oppositionsrapporten ingående och välbalanserat beskriver styrkor och svagheter hos det utvärderade arbetet ur flera aspekter samt att den innehåller tydliga och välmotiverade förslag till förbättringar." "För 2 poäng krävs dessutom: att oppositionsrapporten ingående och välbalanserat beskriver styrkor och svagheter hos det utvärderade arbetet ur flera aspekter samt att den innehåller tydliga och välmotiverade förslag till förbättringar."
) )
.descriptionEn( .descriptionEn(
"For 2 points the following is also required: that the opposition report thoroughly and in a well-balanced way describes from numerous aspects the strengths and weaknesses of the evaluated thesis and that it offers clear and well-motivated suggestions for improvements." "For 2 points, the opposition report must provide a thorough and balanced evaluation of the strengths and weaknesses of the work from various perspectives, along with clear and well-reasoned suggestions for improvement."
) )
.build() .build()
); );
@ -716,6 +789,7 @@ public class DataInitializer implements Lifecycle {
AbstractGradingCriterion.Flag.OPPOSITION AbstractGradingCriterion.Flag.OPPOSITION
); );
// Ö2 Presentationer
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -724,7 +798,7 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att muntliga presentationer av tillräcklig kvalitet har hållits vid anvisade tillfällen samt att förmåga att muntligt försvara det egna arbetet har uppvisats." "För 1 poäng krävs: att muntliga presentationer av tillräcklig kvalitet har hållits vid anvisade tillfällen samt att förmåga att muntligt försvara det egna arbetet har uppvisats."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that oral presentations are of sufficient quality, that they have been given on the assigned dates, and that the ability to orally defend ones own thesis has been shown." "For 1 point, oral presentations of sufficient quality must have been given at designated times, and the ability to orally defend ones work must have been demonstrated."
) )
.build() .build()
); );
@ -735,6 +809,7 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// Ö3 Aktivitet vid seminarier och möten & Ö3 Participation in Seminars and Meetings
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -743,7 +818,7 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att förmåga att muntligt diskutera och ge konstruktiv kritik när det gäller andras arbete har uppvisats vid seminarier och möten." "För 1 poäng krävs: att förmåga att muntligt diskutera och ge konstruktiv kritik när det gäller andras arbete har uppvisats vid seminarier och möten."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the ability to orally discuss and provide constructive criticism regarding others work has been shown in seminars and meetings." "For 1 point, the ability to orally discuss and provide constructive criticism of others work must have been demonstrated at seminars and meetings."
) )
.build() .build()
); );
@ -754,6 +829,7 @@ public class DataInitializer implements Lifecycle {
gradingCriterionPointTemplates gradingCriterionPointTemplates
); );
// Ö4 Deadlines & Ö4 Deadlines
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -762,14 +838,13 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att förmåga har uppvisats att i tid förbereda och leverera material och presentationer vid alla tillfällen som detta krävs." "För 1 poäng krävs: att förmåga har uppvisats att i tid förbereda och leverera material och presentationer vid alla tillfällen som detta krävs."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the ability to prepare and deliver material and presentations on time has been demonstrated at all the necessary occasions." "For 1 point, the ability to prepare and deliver materials and presentations on time at all required occasions must have been demonstrated."
) )
.build() .build()
); );
gradingReportTemplate gradingReportTemplate.addIndividualCriterion("Ö4 Deadlines", "Ö4 Deadlines", 0, gradingCriterionPointTemplates);
.addIndividualCriterion("Ö4 Deadlines", "Ö4 Deadlines", 0, gradingCriterionPointTemplates)
.setFx(false);
// Ö5 Revision efter slutseminarium & Ö5 Revision after Final Seminar
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -778,19 +853,25 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att endast en mindre revision av uppsatsen krävs efter slutseminariet." "För 1 poäng krävs: att endast en mindre revision av uppsatsen krävs efter slutseminariet."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that only one smaller revision of the thesis is needed after the final seminar." "For 1 point, only minor revisions of the thesis should be required after the final seminar."
) )
.build() .build()
); );
gradingReportTemplate gradingCriterionPointTemplates.add(
.addIndividualCriterion( new GradingCriterionPointTemplate.Builder()
"Ö5 Revision efter slutseminarium", .point(2)
"Ö5 Revisions after the final seminar", .description("För 2 poäng krävs dessutom: att nästan ingen revision krävs efter slutseminariet.")
0, .descriptionEn("For 2 points, almost no revisions should be required after the final seminar.")
gradingCriterionPointTemplates .build()
) );
.setFx(false); gradingReportTemplate.addIndividualCriterion(
"Ö5 Revision efter slutseminarium",
"Ö5 Revision after Final Seminar",
0,
gradingCriterionPointTemplates
);
// Ö6 Reflektion & Ö6 Reflection
gradingCriterionPointTemplates = initPointTemplates(); gradingCriterionPointTemplates = initPointTemplates();
gradingCriterionPointTemplates.add( gradingCriterionPointTemplates.add(
new GradingCriterionPointTemplate.Builder() new GradingCriterionPointTemplate.Builder()
@ -799,19 +880,17 @@ public class DataInitializer implements Lifecycle {
"För 1 poäng krävs: att förmåga har uppvisats att reflektera över det genomförda examensarbetet genom individuellt författande av ett reflektionsdokument." "För 1 poäng krävs: att förmåga har uppvisats att reflektera över det genomförda examensarbetet genom individuellt författande av ett reflektionsdokument."
) )
.descriptionEn( .descriptionEn(
"Requirement for 1 point: that the ability to reflect about the thesis work has been demonstrated through the individual writing of a reflection document." "For 1 point, the ability to reflect on the completed thesis work must have been demonstrated through the individual writing of a reflection document."
) )
.build() .build()
); );
gradingReportTemplate gradingReportTemplate.addIndividualCriterion(
.addIndividualCriterion( "Ö6 Reflektion",
"Ö6 Reflektion", "Ö6 Reflection",
"Ö6 Reflection", 1,
0, gradingCriterionPointTemplates,
gradingCriterionPointTemplates, AbstractGradingCriterion.Flag.REFLECTION
AbstractGradingCriterion.Flag.REFLECTION );
)
.setFx(false);
return gradingReportTemplate; return gradingReportTemplate;
} }

@ -14,6 +14,8 @@ public interface GradingReportTemplateService {
GradingReportTemplate getCurrentTemplate(ProjectType projectType); GradingReportTemplate getCurrentTemplate(ProjectType projectType);
GradingReportTemplate getCurrentTemplate(ProjectType projectType, LocalDate startDate);
GradingReportTemplate getTemplate(long templateId); GradingReportTemplate getTemplate(long templateId);
/** /**

@ -216,6 +216,11 @@ public class GradingReportServiceImpl implements GradingReportTemplateService, G
return gradingReportTemplateRepo.getCurrentTemplate(projectType, LocalDate.now(clock)); return gradingReportTemplateRepo.getCurrentTemplate(projectType, LocalDate.now(clock));
} }
@Override
public GradingReportTemplate getCurrentTemplate(ProjectType projectType, LocalDate startDate) {
return gradingReportTemplateRepo.getCurrentTemplate(projectType, startDate);
}
@Override @Override
public GradingReportTemplate getTemplate(long templateId) { public GradingReportTemplate getTemplate(long templateId) {
return gradingReportTemplateRepo.findOne(templateId); return gradingReportTemplateRepo.findOne(templateId);

@ -165,6 +165,10 @@ public class GradingReportTemplate extends DomainObject {
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// Other Methods // Other Methods
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
public int getMaxPoints() {
return getCriteria().stream().mapToInt(GradingCriterionTemplate::getMaxPoints).sum();
}
public SupervisorGradingReport createSupervisorReport(Project project, User student) { public SupervisorGradingReport createSupervisorReport(Project project, User student) {
if (!this.projectType.equals(project.getProjectType())) { if (!this.projectType.equals(project.getProjectType())) {
throw new IllegalArgumentException("Project has a different project class than this template"); throw new IllegalArgumentException("Project has a different project class than this template");

@ -2,156 +2,169 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<body> <body>
<wicket:panel> <wicket:panel>
<wicket:container wicket:id="from2017"> <wicket:container wicket:id="from202411">
<div wicket:id="bachelorContainer">
<div class="card mb-3 bg-light">
<div class="card-body p-2">
<p>
<strong>Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande schema:</strong>
</p>
<p>[A] 28-26, at least 1 point on components U1-U12 and Ö1-Ö4, Ö6</p>
<p>[B] 25-24, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[C] 23-20, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[D] 19-18, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[E] 17-16, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[Fx] at least 1 point on 8 of the components U1-U12 and at least 2 points altogether on components
Ö1-Ö3
</p>
</div>
</div>
</div>
<div wicket:id="master15Container">
<div class="card mb-3 bg-light"> <div class="card mb-3 bg-light">
<div class="card-body p-2"> <div class="card-body p-2">
<p> <p>
<strong>Conversion from points to grades is done according to the following schema:</strong> <strong>Conversion from points to grades is done according to the following schema:</strong>
</p> </p>
<div wicket:id="gradewithlimits_list">
<p>[A] 29-27, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö4, Ö6, and at least 2 points on U8-U9</p> <p wicket:id="gradewithlimits"></p>
</div>
<p>[B] 26-25, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[C] 24-23, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[D] 22-21, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[E] 20-19, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[Fx] at least 1 point on 10 of the components U1-U13 and at least 2 points altogether on components Ö1-Ö3</p>
</div> </div>
</div> </div>
</div> </wicket:container>
<div wicket:id="master30Container"> <wicket:container wicket:id="between2017_202410">
<div class="card mb-3 bg-light"> <div wicket:id="bachelorContainer">
<div class="card-body p-2"> <div class="card mb-3 bg-light">
<p> <div class="card-body p-2">
<strong>Conversion from points to grades is done according to the following schema:</strong> <p>
<span class="text-body-secondary">Updated 2017-02-20</span> <strong>Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande schema:</strong>
</p> </p>
<p>[A] 33-31, at least 1 point on components U1-U4, U10-U12, Ö1-Ö4, Ö6, and at least 2 points on U5-U9, U13</p> <p>[A] 28-26, at least 1 point on components U1-U12 and Ö1-Ö4, Ö6</p>
<p>[B] 30-29, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p> <p>[B] 25-24, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[C] 28-27, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p> <p>[C] 23-20, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[D] 26-25, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p> <p>[D] 19-18, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[E] 24-23, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p> <p>[E] 17-16, at least 1 point on components U1-U12 and Ö1-Ö3, Ö6</p>
<p>[Fx] at least 1 point on 12 of the components U1-U13 and at least 2 points altogether on components Ö1-Ö3</p> <p>[Fx] at least 1 point on 8 of the components U1-U12 and at least 2 points altogether on components
Ö1-Ö3
</p>
</div>
</div>
</div>
<div wicket:id="master15Container">
<div class="card mb-3 bg-light">
<div class="card-body p-2">
<p>
<strong>Conversion from points to grades is done according to the following schema:</strong>
</p>
<p>[A] 29-27, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö4, Ö6, and at least 2 points on U8-U9</p>
<p>[B] 26-25, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[C] 24-23, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[D] 22-21, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[E] 20-19, at least 1 point on components U1-U7, U10-U13 and Ö1-Ö3, Ö6, and at least 2 points on U8-U9</p>
<p>[Fx] at least 1 point on 10 of the components U1-U13 and at least 2 points altogether on components Ö1-Ö3</p>
</div>
</div>
</div>
<div wicket:id="master30Container">
<div class="card mb-3 bg-light">
<div class="card-body p-2">
<p>
<strong>Conversion from points to grades is done according to the following schema:</strong>
<span class="text-body-secondary">Updated 2017-02-20</span>
</p>
<p>[A] 33-31, at least 1 point on components U1-U4, U10-U12, Ö1-Ö4, Ö6, and at least 2 points on U5-U9, U13</p>
<p>[B] 30-29, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p>
<p>[C] 28-27, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p>
<p>[D] 26-25, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p>
<p>[E] 24-23, at least 1 point on components U1-U4, U10-U12, Ö1-Ö3, Ö6, and at least 2 points on U5-U9, U13</p>
<p>[Fx] at least 1 point on 12 of the components U1-U13 and at least 2 points altogether on components Ö1-Ö3</p>
</div>
</div> </div>
</div> </div>
</div>
</wicket:container> </wicket:container>
<wicket:container wicket:id="upto2016"> <wicket:container wicket:id="upto2016">
<div wicket:id="bachelorContainer"> <div wicket:id="bachelorContainer">
<div class="card mb-3 bg-light"> <div class="card mb-3 bg-light">
<div class="card-body p-2"> <div class="card-body p-2">
<p> <p>
<strong> <strong>
Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande
schema: schema:
</strong> </strong>
</p> </p>
<p>[A] 27-25, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö4</p> <p>[A] 27-25, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö4</p>
<p>[B] 24-23, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p> <p>[B] 24-23, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p>
<p>[C] 22-19, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p> <p>[C] 22-19, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p>
<p>[D] 18-17, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p> <p>[D] 18-17, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p>
<p>[E] 16-15, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p> <p>[E] 16-15, minst 1 poäng på komponenterna U1-U12 samt Ö1-Ö3</p>
<p>[Fx] minst 1 poäng på åtta av komponenterna U1-U12 och minst två poäng sammanlagt på <p>[Fx] minst 1 poäng på åtta av komponenterna U1-U12 och minst två poäng sammanlagt på
komponenterna Ö1-Ö3 komponenterna Ö1-Ö3
</p> </p>
</div>
</div> </div>
</div> </div>
</div>
<div wicket:id="master15Container"> <div wicket:id="master15Container">
<div class="card mb-3 bg-light"> <div class="card mb-3 bg-light">
<div class="card-body p-2"> <div class="card-body p-2">
<p> <p>
<strong> <strong>
Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande
schema: schema:
</strong> </strong>
</p> </p>
<p>[A] 28-26, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö4, minst 2 poäng på U8-U9</p> <p>[A] 28-26, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö4, minst 2 poäng på U8-U9</p>
<p>[B] 25-24, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p> <p>[B] 25-24, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p>
<p>[C] 23-22, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p> <p>[C] 23-22, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p>
<p>[D] 21-20, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p> <p>[D] 21-20, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p>
<p>[E] 19-18, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p> <p>[E] 19-18, minst 1 poäng på komponenterna U1-U7, U10-U13 samt Ö1-Ö3, minst 2 poäng på U8-U9</p>
<p>[Fx] minst 1 poäng på tio av komponenterna U1-U13 och minst två poäng sammanlagt på komponenterna <p>[Fx] minst 1 poäng på tio av komponenterna U1-U13 och minst två poäng sammanlagt på komponenterna
Ö1-Ö3</p> Ö1-Ö3</p>
</div>
</div> </div>
</div> </div>
</div>
<div wicket:id="master30Container"> <div wicket:id="master30Container">
<div class="card mb-3 bg-light"> <div class="card mb-3 bg-light">
<div class="card-body p-2"> <div class="card-body p-2">
<p> <p>
<strong> <strong>
Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande Omvandling från poäng till betyg för ett kandidatarbete görs enligt följande
schema: schema:
</strong> </strong>
</p> </p>
<p>[A] 32-30, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö4, minst 2 poäng på U5-U9, U13</p> <p>[A] 32-30, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö4, minst 2 poäng på U5-U9, U13</p>
<p>[B] 29-28, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p> <p>[B] 29-28, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p>
<p>[C] 27-26, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p> <p>[C] 27-26, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p>
<p>[D] 25-24, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p> <p>[D] 25-24, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p>
<p>[E] 23-22, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p> <p>[E] 23-22, minst 1 poäng på komponenterna U1-U4, U10-U12, Ö1-Ö3, minst 2 poäng på U5-U9, U13</p>
<p>[Fx] minst 1 poäng på tolv av komponenterna U1-U13 och minst två poäng sammanlagt på komponenterna <p>[Fx] minst 1 poäng på tolv av komponenterna U1-U13 och minst två poäng sammanlagt på komponenterna
Ö1-Ö3</p> Ö1-Ö3</p>
</div>
</div> </div>
</div> </div>
</div>
</wicket:container> </wicket:container>
</wicket:panel> </wicket:panel>
</body> </body>

@ -1,9 +1,19 @@
package se.su.dsv.scipro.grading; package se.su.dsv.scipro.grading;
import jakarta.inject.Inject;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel; import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.report.GradeLimit;
import se.su.dsv.scipro.report.GradingReportTemplate;
import se.su.dsv.scipro.system.DegreeType; import se.su.dsv.scipro.system.DegreeType;
public class TemplatePanel extends Panel { public class TemplatePanel extends Panel {
@ -12,28 +22,74 @@ public class TemplatePanel extends Panel {
public static final String MASTER_15_CONTAINER = "master15Container"; public static final String MASTER_15_CONTAINER = "master15Container";
public static final String MASTER_30_CONTAINER = "master30Container"; public static final String MASTER_30_CONTAINER = "master30Container";
private enum GradingCategory {
UP_TO_2016,
BETWEEN_2017_202410,
FROM_202411;
public static GradingCategory fromProjectStartDate(LocalDate projectStartDate) {
int year = projectStartDate.getYear();
int month = projectStartDate.getMonthValue();
if (year < 2017) {
return UP_TO_2016;
} else {
return (year <= 2024 && month < 11) ? BETWEEN_2017_202410 : FROM_202411;
}
}
}
@Inject
private GradingReportTemplateService gradingReportTemplateService;
public TemplatePanel(String id, final IModel<Project> projectIModel) { public TemplatePanel(String id, final IModel<Project> projectIModel) {
super(id, projectIModel); super(id, projectIModel);
final DegreeType degreeType = projectIModel.getObject().getProjectType().getDegreeType(); Project project = projectIModel.getObject();
final DegreeType degreeType = project.getProjectType().getDegreeType();
IModel<GradingReportTemplate> grtModel = LoadableDetachableModel.of(() ->
gradingReportTemplateService.getCurrentTemplate(
projectIModel.getObject().getProjectType(),
projectIModel.getObject().getStartDate()
)
);
final WebMarkupContainer upto16 = new WebMarkupContainer("upto2016") { final WebMarkupContainer upto16 = new WebMarkupContainer("upto2016") {
@Override @Override
protected void onConfigure() { protected void onConfigure() {
super.onConfigure(); super.onConfigure();
setVisibilityAllowed(getYear(projectIModel.getObject()) < 2017); setVisibilityAllowed(
grtModel.getObject() == null &&
GradingCategory.fromProjectStartDate(projectIModel.getObject().getStartDate()) ==
GradingCategory.UP_TO_2016
);
} }
}; };
addTemplates(degreeType, upto16); addTemplates(degreeType, upto16);
add(upto16); add(upto16);
final WebMarkupContainer from2017 = new WebMarkupContainer("from2017") {
final WebMarkupContainer between2017_202410 = new WebMarkupContainer("between2017_202410") {
@Override @Override
protected void onConfigure() { protected void onConfigure() {
super.onConfigure(); super.onConfigure();
setVisibilityAllowed(getYear(projectIModel.getObject()) >= 2017); setVisibilityAllowed(
grtModel.getObject() == null &&
GradingCategory.fromProjectStartDate(projectIModel.getObject().getStartDate()) ==
GradingCategory.BETWEEN_2017_202410
);
} }
}; };
addTemplates(degreeType, from2017); addTemplates(degreeType, between2017_202410);
add(from2017); add(between2017_202410);
final GradeLimitsContainer from202411 = new GradeLimitsContainer("from202411", grtModel) {
@Override
protected void onConfigure() {
super.onConfigure();
setVisibilityAllowed(grtModel.getObject() != null);
}
};
add(from202411);
} }
private void addTemplates(final DegreeType degreeType, final WebMarkupContainer container) { private void addTemplates(final DegreeType degreeType, final WebMarkupContainer container) {
@ -66,7 +122,40 @@ public class TemplatePanel extends Panel {
); );
} }
private int getYear(final Project project) { private List<GradeWithPointRange> getGradeWithPointRanges(GradingReportTemplate grt) {
return project.getStartDate().getYear(); GradeLimit[] gradeLimits = grt.getGradeLimits().toArray(GradeLimit[]::new);
List<GradeWithPointRange> list = new ArrayList<>();
for (int i = 0; i < gradeLimits.length; i++) {
GradeLimit gradeLimit = gradeLimits[i];
int lowerLimit = gradeLimit.getLowerLimit();
int upperLimit = i == 0 ? grt.getMaxPoints() : gradeLimits[i - 1].getLowerLimit() - 1;
list.add(new GradeWithPointRange(gradeLimit.getGrade(), lowerLimit, upperLimit));
}
return list;
}
private record GradeWithPointRange(String grade, int lowerLimit, int upperLimit) {}
private class GradeLimitsContainer extends WebMarkupContainer {
public GradeLimitsContainer(String id, IModel<GradingReportTemplate> grtModel) {
super(id, grtModel);
IModel<List<GradeWithPointRange>> list = grtModel.map(TemplatePanel.this::getGradeWithPointRanges);
add(
new ListView<>("gradewithlimits_list", list) {
@Override
protected void populateItem(ListItem<GradeWithPointRange> item) {
IModel<String> s = item
.getModel()
.map(gpr -> "[" + gpr.grade() + "] " + gpr.lowerLimit() + " - " + gpr.upperLimit());
item.add(new Label("gradewithlimits", s));
}
}
);
}
} }
} }

@ -7,7 +7,9 @@ import static org.mockito.Mockito.mock;
import com.google.common.eventbus.EventBus; import com.google.common.eventbus.EventBus;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.time.Clock; import java.time.Clock;
import java.util.*; import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.wicket.Component; import org.apache.wicket.Component;
import org.apache.wicket.Page; import org.apache.wicket.Page;
import org.apache.wicket.Session; import org.apache.wicket.Session;
@ -53,6 +55,7 @@ import se.su.dsv.scipro.gdpr.Reporter;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings; import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService; import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
import se.su.dsv.scipro.grading.ExaminerTimelineService; import se.su.dsv.scipro.grading.ExaminerTimelineService;
import se.su.dsv.scipro.grading.GradingReportTemplateService;
import se.su.dsv.scipro.grading.GradingService; import se.su.dsv.scipro.grading.GradingService;
import se.su.dsv.scipro.grading.NationalSubjectCategoryService; import se.su.dsv.scipro.grading.NationalSubjectCategoryService;
import se.su.dsv.scipro.grading.PublicationMetadata; import se.su.dsv.scipro.grading.PublicationMetadata;
@ -306,6 +309,9 @@ public abstract class SciProTest {
@Mock @Mock
protected GradingReportService gradingReportService; protected GradingReportService gradingReportService;
@Mock
protected GradingReportTemplateService gradingReportTemplateService;
@Mock @Mock
protected OppositionReportService oppositionReportService; protected OppositionReportService oppositionReportService;

@ -8,8 +8,10 @@ import java.time.LocalDate;
import java.time.Month; import java.time.Month;
import org.apache.wicket.model.Model; import org.apache.wicket.model.Model;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import se.su.dsv.scipro.SciProTest; import se.su.dsv.scipro.SciProTest;
import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.report.GradingReportTemplate;
import se.su.dsv.scipro.system.DegreeType; import se.su.dsv.scipro.system.DegreeType;
import se.su.dsv.scipro.system.ProjectType; import se.su.dsv.scipro.system.ProjectType;
@ -18,54 +20,56 @@ public class TemplatePanelTest extends SciProTest {
public static final ProjectType BACHELOR = new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor"); public static final ProjectType BACHELOR = new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor");
public static final ProjectType MASTER = new ProjectType(DegreeType.MASTER, "Master", "Master"); public static final ProjectType MASTER = new ProjectType(DegreeType.MASTER, "Master", "Master");
public static final ProjectType MAGISTER = new ProjectType(DegreeType.MAGISTER, "Magister", "Magister"); public static final ProjectType MAGISTER = new ProjectType(DegreeType.MAGISTER, "Magister", "Magister");
private static final String key = "between2017_202410";
private TemplatePanel panel; private TemplatePanel panel;
@Test @Test
public void bachelor_container_visible_if_bachelor_project() { public void bachelor_container_visible_if_bachelor_project() {
startPanel(BACHELOR, 15); startPanel(BACHELOR, 15);
tester.assertVisible(path(panel, "from2017", BACHELOR_CONTAINER)); tester.assertVisible(path(panel, key, BACHELOR_CONTAINER));
} }
@Test @Test
public void bachelor_container_invisible_if_not_bachelor_project() { public void bachelor_container_invisible_if_not_bachelor_project() {
startPanel(MASTER, 15); startPanel(MASTER, 15);
tester.assertInvisible(path(panel, "from2017", BACHELOR_CONTAINER)); tester.assertInvisible(path(panel, key, BACHELOR_CONTAINER));
} }
@Test @Test
public void magister_container_visible_if_magister_project() { public void magister_container_visible_if_magister_project() {
startPanel(MAGISTER, 15); startPanel(MAGISTER, 15);
tester.assertVisible(path(panel, "from2017", MASTER_15_CONTAINER)); tester.assertVisible(path(panel, key, MASTER_15_CONTAINER));
} }
@Test @Test
public void magister_container_invisible_if_not_magister() { public void magister_container_invisible_if_not_magister() {
startPanel(BACHELOR, 15); startPanel(BACHELOR, 15);
tester.assertInvisible(path(panel, "from2017", MASTER_15_CONTAINER)); tester.assertInvisible(path(panel, key, MASTER_15_CONTAINER));
} }
@Test @Test
public void magister_container_invisible_if_wrong_credits() { public void magister_container_invisible_if_wrong_credits() {
startPanel(MASTER, 30); startPanel(MASTER, 30);
tester.assertInvisible(path(panel, "from2017", MASTER_15_CONTAINER)); tester.assertInvisible(path(panel, key, MASTER_15_CONTAINER));
} }
@Test @Test
public void master_30_container_visible_if_master_30_project() { public void master_30_container_visible_if_master_30_project() {
startPanel(MASTER, 30); startPanel(MASTER, 30);
tester.assertVisible(path(panel, "from2017", MASTER_30_CONTAINER)); tester.assertVisible(path(panel, key, MASTER_30_CONTAINER));
} }
@Test @Test
public void master_30_container_invisible_if_not_master() { public void master_30_container_invisible_if_not_master() {
startPanel(BACHELOR, 30); startPanel(BACHELOR, 30);
tester.assertInvisible(path(panel, "from2017", MASTER_30_CONTAINER)); tester.assertInvisible(path(panel, key, MASTER_30_CONTAINER));
} }
@Test @Test
public void master_30_container_visible_if_master_and_0_credits() { public void master_30_container_visible_if_master_and_0_credits() {
startPanel(MASTER, 0); startPanel(MASTER, 0);
tester.assertVisible(path(panel, "from2017", MASTER_30_CONTAINER)); tester.assertVisible(path(panel, key, MASTER_30_CONTAINER));
} }
private void startPanel(ProjectType projectType, int credits) { private void startPanel(ProjectType projectType, int credits) {
@ -76,6 +80,11 @@ public class TemplatePanelTest extends SciProTest {
.build(); .build();
project.setStartDate(LocalDate.of(2017, Month.MAY, 29)); project.setStartDate(LocalDate.of(2017, Month.MAY, 29));
project.setCredits(credits); project.setCredits(credits);
Mockito.lenient()
.when(gradingReportTemplateService.getCurrentTemplate(project.getProjectType()))
.thenReturn(new GradingReportTemplate(projectType, LocalDate.of(2024, Month.JANUARY, 1)));
panel = tester.startComponentInPage(new TemplatePanel("id", Model.of(project))); panel = tester.startComponentInPage(new TemplatePanel("id", Model.of(project)));
} }
} }