feat: add substract with image reference
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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 |
49
app/problems/grade_1/subtract_with_image_reference.py
Normal file
49
app/problems/grade_1/subtract_with_image_reference.py
Normal 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()
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
40
app/schemas/grade_1/subtract_with_image_reference.py
Normal file
40
app/schemas/grade_1/subtract_with_image_reference.py
Normal 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)
|
||||||
95
tests/test_subtract_with_image_reference_endpoint.py
Normal file
95
tests/test_subtract_with_image_reference_endpoint.py
Normal 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()
|
||||||
Reference in New Issue
Block a user