feat: add substract with image reference

This commit is contained in:
AlanSilvaaa
2026-05-26 14:58:56 -04:00
parent b4950cee35
commit 1681aca057
8 changed files with 234 additions and 0 deletions

View File

@@ -4,6 +4,24 @@ Test quick examples for each endpoint:
## Grade 1 ## Grade 1
### Subtract with image reference
```bash
curl -X POST "http://127.0.0.1:8000/math/grade_1/subtract_with_image_reference" \
-H "Content-Type: application/json" \
-d '{
"object_name": "peces",
"initial_quantity": 5,
"removed_quantity": 2,
"actor_name": "Diego",
"figure": {
"id": "fish",
"name": "Fish",
"image_path": "/images/fish.png"
}
}'
```
### Join corresponding sums ### Join corresponding sums
```bash ```bash

View File

@@ -5,6 +5,9 @@ from app.problems.grade_1.join_corresponding_sums import join_corresponding_sums
from app.problems.grade_1.join_pictures_with_quantity import ( from app.problems.grade_1.join_pictures_with_quantity import (
join_pictures_with_quantity, join_pictures_with_quantity,
) )
from app.problems.grade_1.subtract_with_image_reference import (
subtract_with_image_reference,
)
from app.problems.grade_1.sum_with_image_reference import sum_with_image_reference from app.problems.grade_1.sum_with_image_reference import sum_with_image_reference
from app.problems.grade_1.where_are_more_items import where_are_more_items from app.problems.grade_1.where_are_more_items import where_are_more_items
@@ -12,6 +15,7 @@ __all__ = [
"compose_and_decompose_numbers", "compose_and_decompose_numbers",
"join_corresponding_sums", "join_corresponding_sums",
"join_pictures_with_quantity", "join_pictures_with_quantity",
"subtract_with_image_reference",
"sum_with_image_reference", "sum_with_image_reference",
"where_are_more_items", "where_are_more_items",
] ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

View File

@@ -0,0 +1,49 @@
from app.schemas.grade_1.subtract_with_image_reference import (
FigureGroup,
PictureAsset,
SubtractWithImageReferenceProblem,
SubtractionEquation,
)
def subtract_with_image_reference(
object_name: str,
initial_quantity: int,
removed_quantity: int,
figure: dict,
actor_name: str = "Diego",
) -> dict:
"""Generate a subtraction story problem with remaining and removed groups."""
if removed_quantity > initial_quantity:
raise ValueError("removed_quantity must be less than or equal to initial_quantity")
selected_figure = PictureAsset.model_validate(figure)
remaining_quantity = initial_quantity - removed_quantity
question = (
f"Hay {initial_quantity} {object_name}. "
f"{actor_name} sacó {removed_quantity} {object_name}. "
f"¿Cuántos {object_name} quedan?"
)
problem = SubtractWithImageReferenceProblem(
question=question,
object_name=object_name,
actor_name=actor_name,
figure=selected_figure,
remaining_group=FigureGroup(
label="quedan",
quantity=remaining_quantity,
picture=selected_figure,
),
subtracted_group=FigureGroup(
label="sacó",
quantity=removed_quantity,
picture=selected_figure,
),
equation=SubtractionEquation(
initial_quantity=initial_quantity,
removed_quantity=removed_quantity,
remaining_quantity=remaining_quantity,
),
)
return problem.model_dump()

View File

@@ -4,6 +4,7 @@ from app.problems.grade_1 import (
compose_and_decompose_numbers, compose_and_decompose_numbers,
join_corresponding_sums, join_corresponding_sums,
join_pictures_with_quantity, join_pictures_with_quantity,
subtract_with_image_reference,
sum_with_image_reference, sum_with_image_reference,
where_are_more_items, where_are_more_items,
) )
@@ -14,6 +15,8 @@ from app.schemas.grade_1 import (
JoinCorrespondingSumsRequest, JoinCorrespondingSumsRequest,
JoinPicturesWithQuantityProblem, JoinPicturesWithQuantityProblem,
JoinPicturesWithQuantityRequest, JoinPicturesWithQuantityRequest,
SubtractWithImageReferenceProblem,
SubtractWithImageReferenceRequest,
SumWithImageReferenceProblem, SumWithImageReferenceProblem,
SumWithImageReferenceRequest, SumWithImageReferenceRequest,
WhereAreMoreItemsProblem, WhereAreMoreItemsProblem,
@@ -23,6 +26,25 @@ from app.schemas.grade_1 import (
router = APIRouter(prefix="/grade_1", tags=["Grade 1"]) router = APIRouter(prefix="/grade_1", tags=["Grade 1"])
@router.post(
"/subtract_with_image_reference",
response_model=SubtractWithImageReferenceProblem,
)
def create_subtract_with_image_reference_problem(
request: SubtractWithImageReferenceRequest,
) -> dict:
try:
return subtract_with_image_reference(
object_name=request.object_name,
initial_quantity=request.initial_quantity,
removed_quantity=request.removed_quantity,
figure=request.figure.model_dump(),
actor_name=request.actor_name,
)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
@router.post( @router.post(
"/join_corresponding_sums", "/join_corresponding_sums",
response_model=JoinCorrespondingSumsProblem, response_model=JoinCorrespondingSumsProblem,

View File

@@ -10,6 +10,10 @@ from app.schemas.grade_1.join_pictures_with_quantity import (
JoinPicturesWithQuantityProblem, JoinPicturesWithQuantityProblem,
JoinPicturesWithQuantityRequest, JoinPicturesWithQuantityRequest,
) )
from app.schemas.grade_1.subtract_with_image_reference import (
SubtractWithImageReferenceProblem,
SubtractWithImageReferenceRequest,
)
from app.schemas.grade_1.sum_with_image_reference import ( from app.schemas.grade_1.sum_with_image_reference import (
SumWithImageReferenceProblem, SumWithImageReferenceProblem,
SumWithImageReferenceRequest, SumWithImageReferenceRequest,
@@ -26,6 +30,8 @@ __all__ = [
"JoinCorrespondingSumsRequest", "JoinCorrespondingSumsRequest",
"JoinPicturesWithQuantityProblem", "JoinPicturesWithQuantityProblem",
"JoinPicturesWithQuantityRequest", "JoinPicturesWithQuantityRequest",
"SubtractWithImageReferenceProblem",
"SubtractWithImageReferenceRequest",
"SumWithImageReferenceProblem", "SumWithImageReferenceProblem",
"SumWithImageReferenceRequest", "SumWithImageReferenceRequest",
"WhereAreMoreItemsProblem", "WhereAreMoreItemsProblem",

View File

@@ -0,0 +1,40 @@
from pydantic import BaseModel, Field, NonNegativeInt, PositiveInt
class PictureAsset(BaseModel):
id: str = Field(min_length=1)
name: str = Field(min_length=1)
image_path: str = Field(min_length=1)
class SubtractionEquation(BaseModel):
initial_quantity: PositiveInt
removed_quantity: PositiveInt
remaining_quantity: NonNegativeInt
symbol: str = "-"
equals_symbol: str = "="
class FigureGroup(BaseModel):
label: str = Field(min_length=1)
quantity: NonNegativeInt
picture: PictureAsset
class SubtractWithImageReferenceProblem(BaseModel):
title: str = "¿Cuántos quedan?"
question: str
object_name: str = Field(min_length=1)
actor_name: str = Field(min_length=1)
figure: PictureAsset
remaining_group: FigureGroup
subtracted_group: FigureGroup
equation: SubtractionEquation
class SubtractWithImageReferenceRequest(BaseModel):
object_name: str = Field(min_length=1)
initial_quantity: PositiveInt
removed_quantity: PositiveInt
figure: PictureAsset
actor_name: str = Field(default="Diego", min_length=1)

View File

@@ -0,0 +1,95 @@
import unittest
from fastapi.testclient import TestClient
from app.main import create_app
class SubtractWithImageReferenceEndpointTest(unittest.TestCase):
def setUp(self) -> None:
self.client = TestClient(create_app())
self.figure = {
"id": "fish",
"name": "Fish",
"image_path": "/images/fish.png",
}
def test_creates_subtraction_problem(self) -> None:
response = self.client.post(
"/math/grade_1/subtract_with_image_reference",
json={
"object_name": "peces",
"initial_quantity": 5,
"removed_quantity": 2,
"actor_name": "Diego",
"figure": self.figure,
},
)
self.assertEqual(response.status_code, 200)
problem = response.json()
self.assertEqual(problem["title"], "¿Cuántos quedan?")
self.assertEqual(
problem["question"],
"Hay 5 peces. Diego sacó 2 peces. ¿Cuántos peces quedan?",
)
self.assertEqual(problem["object_name"], "peces")
self.assertEqual(problem["actor_name"], "Diego")
self.assertEqual(problem["figure"], self.figure)
self.assertEqual(
problem["remaining_group"],
{"label": "quedan", "quantity": 3, "picture": self.figure},
)
self.assertEqual(
problem["subtracted_group"],
{"label": "sacó", "quantity": 2, "picture": self.figure},
)
self.assertEqual(
problem["equation"],
{
"initial_quantity": 5,
"removed_quantity": 2,
"remaining_quantity": 3,
"symbol": "-",
"equals_symbol": "=",
},
)
def test_allows_zero_remaining_figures(self) -> None:
response = self.client.post(
"/math/grade_1/subtract_with_image_reference",
json={
"object_name": "peces",
"initial_quantity": 2,
"removed_quantity": 2,
"figure": self.figure,
},
)
self.assertEqual(response.status_code, 200)
problem = response.json()
self.assertEqual(problem["equation"]["remaining_quantity"], 0)
self.assertEqual(problem["remaining_group"]["quantity"], 0)
self.assertEqual(problem["subtracted_group"]["quantity"], 2)
def test_returns_bad_request_when_removed_quantity_is_too_large(self) -> None:
response = self.client.post(
"/math/grade_1/subtract_with_image_reference",
json={
"object_name": "peces",
"initial_quantity": 2,
"removed_quantity": 5,
"figure": self.figure,
},
)
self.assertEqual(response.status_code, 400)
self.assertEqual(
response.json(),
{"detail": "removed_quantity must be less than or equal to initial_quantity"},
)
if __name__ == "__main__":
unittest.main()