diff --git a/athena/athena/models/db_modeling_feedback.py b/athena/athena/models/db_modeling_feedback.py index 142a5991..1fdffd23 100644 --- a/athena/athena/models/db_modeling_feedback.py +++ b/athena/athena/models/db_modeling_feedback.py @@ -1,6 +1,6 @@ from typing import Optional -from sqlalchemy import Column, ForeignKey, JSON +from sqlalchemy import Column, ForeignKey, String from sqlalchemy.orm import relationship from athena.database import Base @@ -11,7 +11,7 @@ class DBModelingFeedback(DBFeedback, Base): __tablename__ = "modeling_feedbacks" - element_ids: Optional[list[str]] = Column(JSON) # type: ignore + reference: Optional[str] = Column(String, nullable=True) # type: ignore exercise_id = Column(BigIntegerWithAutoincrement, ForeignKey("modeling_exercises.id", ondelete="CASCADE"), index=True) submission_id = Column(BigIntegerWithAutoincrement, ForeignKey("modeling_submissions.id", ondelete="CASCADE"), index=True) diff --git a/athena/athena/schemas/modeling_feedback.py b/athena/athena/schemas/modeling_feedback.py index f77322f4..85245c2e 100644 --- a/athena/athena/schemas/modeling_feedback.py +++ b/athena/athena/schemas/modeling_feedback.py @@ -1,11 +1,9 @@ -from typing import Optional, List - +from typing import Optional from pydantic import Field - from .feedback import Feedback class ModelingFeedback(Feedback): """Feedback on a modeling exercise.""" - element_ids: Optional[List[str]] = Field([], description="referenced diagram element IDs", example=["id_1"]) + reference: Optional[str] = Field(None, description="reference to the diagram element", example="ClassAttribute:5a337bdf-da00-4bd0-a6f0-78ba5b84330e") \ No newline at end of file diff --git a/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/apollon_json_transformer.py b/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/apollon_json_transformer.py index 3d67196d..7a487bff 100644 --- a/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/apollon_json_transformer.py +++ b/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/apollon_json_transformer.py @@ -6,7 +6,7 @@ class ApollonJSONTransformer: @staticmethod - def transform_json(model: str) -> tuple[str, dict[str, str], str]: + def transform_json(model: str) -> tuple[str, dict[str, str], str, dict[str, str]]: """ Serialize a given Apollon diagram model to a string representation. This method converts the UML diagram model into a format similar to mermaid syntax, called "apollon". @@ -30,6 +30,8 @@ def transform_json(model: str) -> tuple[str, dict[str, str], str]: **{element['name']: element['id'] for element in parser.get_elements()}, **{relation['name']: relation['id'] for relation in parser.get_relations()} } - - return apollon_representation, names, diagram_type + + id_type_mapping = parser.get_id_to_type_mapping() + + return apollon_representation, names, diagram_type, id_type_mapping \ No newline at end of file diff --git a/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/parser/uml_parser.py b/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/parser/uml_parser.py index 57b9a63a..97d7c43b 100644 --- a/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/parser/uml_parser.py +++ b/modules/modeling/module_modeling_llm/module_modeling_llm/apollon_transformer/parser/uml_parser.py @@ -93,4 +93,12 @@ def get_elements(self) -> List[Element]: return self.elements def get_relations(self) -> List[Relation]: - return self.relations \ No newline at end of file + return self.relations + + def get_id_to_type_mapping(self) -> Dict[str, str]: + id_to_type = {} + for element in self.elements: + id_to_type[element.id] = element.type + for relation in self.relations: + id_to_type[relation.id] = relation.type + return id_to_type \ No newline at end of file diff --git a/modules/modeling/module_modeling_llm/module_modeling_llm/models/assessment_model.py b/modules/modeling/module_modeling_llm/module_modeling_llm/models/assessment_model.py index 9ccc1933..3468f794 100644 --- a/modules/modeling/module_modeling_llm/module_modeling_llm/models/assessment_model.py +++ b/modules/modeling/module_modeling_llm/module_modeling_llm/models/assessment_model.py @@ -4,7 +4,7 @@ class FeedbackModel(BaseModel): title: str = Field(description="Very short title, i.e. feedback category or similar", example="Logic Error") description: str = Field(description="Feedback description") - element_names: Optional[List[str]] = Field(description="Referenced diagram element names, and relations (R) or empty if unreferenced") + element_name: Optional[str] = Field(description="Referenced diagram element, attribute names, and relations (R) or empty if unreferenced") credits: float = Field(0.0, description="Number of points received/deducted") grading_instruction_id: int = Field( description="ID of the grading instruction that was used to generate this feedback" diff --git a/modules/modeling/module_modeling_llm/module_modeling_llm/models/exercise_model.py b/modules/modeling/module_modeling_llm/module_modeling_llm/models/exercise_model.py index 3a6f0672..78a07a19 100644 --- a/modules/modeling/module_modeling_llm/module_modeling_llm/models/exercise_model.py +++ b/modules/modeling/module_modeling_llm/module_modeling_llm/models/exercise_model.py @@ -11,4 +11,5 @@ class ExerciseModel(BaseModel): grading_instructions: Optional[str] = None submission_uml_type: str transformed_example_solution: Optional[str] = None - element_id_mapping: dict[str, str] \ No newline at end of file + element_id_mapping: dict[str, str] + id_type_mapping: dict[str, str] \ No newline at end of file diff --git a/modules/modeling/module_modeling_llm/module_modeling_llm/utils/convert_to_athana_feedback_model.py b/modules/modeling/module_modeling_llm/module_modeling_llm/utils/convert_to_athana_feedback_model.py index 06b4b5a6..dd44dfa7 100644 --- a/modules/modeling/module_modeling_llm/module_modeling_llm/utils/convert_to_athana_feedback_model.py +++ b/modules/modeling/module_modeling_llm/module_modeling_llm/utils/convert_to_athana_feedback_model.py @@ -11,33 +11,40 @@ def convert_to_athana_feedback_model( manual_structured_grading_instructions: Optional[List[GradingCriterion]] = None ) -> List[Feedback]: - grading_instruction_ids = set( + grading_instruction_ids = { grading_instruction.id - for criterion in manual_structured_grading_instructions or [] + for criterion in (manual_structured_grading_instructions or []) for grading_instruction in criterion.structured_grading_instructions - ) + } feedbacks = [] for feedback in feedback_result.feedbacks: # Each feedback has a grading_instruction_id. However we only want to have the grading_instruction_id in the final feedback that are associated with the manual structured grading instructions - grading_instruction_id = feedback.grading_instruction_id if feedback.grading_instruction_id in grading_instruction_ids else None - element_ids = [ - exercise_model.element_id_mapping[element] - for element in (feedback.element_names or []) - if element in exercise_model.element_id_mapping - ] + grading_instruction_id = ( + feedback.grading_instruction_id + if feedback.grading_instruction_id in grading_instruction_ids + else None + ) + + reference: Optional[str] = None + if feedback.element_name: + reference_id = exercise_model.element_id_mapping.get(feedback.element_name) + reference_type = exercise_model.id_type_mapping.get(reference_id) if reference_id else None + + if reference_type and reference_id: + reference = f"{reference_type}:{reference_id}" feedbacks.append(Feedback( exercise_id=exercise_model.exercise_id, submission_id=exercise_model.submission_id, title=feedback.title, description=feedback.description, - element_ids=element_ids, credits=feedback.credits, structured_grading_instruction_id=grading_instruction_id, meta={}, id=None, - is_graded=False + is_graded=False, + reference=reference )) return feedbacks \ No newline at end of file diff --git a/modules/modeling/module_modeling_llm/module_modeling_llm/utils/get_exercise_model.py b/modules/modeling/module_modeling_llm/module_modeling_llm/utils/get_exercise_model.py index a2457f1f..3489be73 100644 --- a/modules/modeling/module_modeling_llm/module_modeling_llm/utils/get_exercise_model.py +++ b/modules/modeling/module_modeling_llm/module_modeling_llm/utils/get_exercise_model.py @@ -7,9 +7,9 @@ def get_exercise_model(exercise: Exercise, submission: Submission) -> ExerciseMo serialized_example_solution = None if exercise.example_solution: - serialized_example_solution, _, _ = ApollonJSONTransformer.transform_json(exercise.example_solution) + serialized_example_solution, _, _, _ = ApollonJSONTransformer.transform_json(exercise.example_solution) - transformed_submission, element_id_mapping, diagram_type = ApollonJSONTransformer.transform_json(submission.model) + transformed_submission, element_id_mapping, diagram_type, id_type_mapping = ApollonJSONTransformer.transform_json(submission.model) return ExerciseModel( submission_id=submission.id, @@ -22,6 +22,7 @@ def get_exercise_model(exercise: Exercise, submission: Submission) -> ExerciseMo submission_uml_type=diagram_type, transformed_example_solution=serialized_example_solution, element_id_mapping=element_id_mapping, + id_type_mapping=id_type_mapping )