Skip to content

Commit 5f200e8

Browse files
authored
CDR: Add self-answerable questions (#793)
### Description Please explain the changes you made here. ### Checklist - [ ] Created tests which fail without the change (if possible) - [ ] All tests passing - [ ] Extended the documentation, if necessary
1 parent 0c89f4a commit 5f200e8

File tree

7 files changed

+162
-17
lines changed

7 files changed

+162
-17
lines changed

app/modules/cdr/cruds_cdr.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,20 @@ def create_customdata_field(db: AsyncSession, datafield: models_cdr.CustomDataFi
867867
db.add(datafield)
868868

869869

870+
async def update_customdata_field(
871+
db: AsyncSession,
872+
field_id: UUID,
873+
datafield: schemas_cdr.CustomDataFieldBase,
874+
):
875+
await db.execute(
876+
update(models_cdr.CustomDataField)
877+
.where(
878+
models_cdr.CustomDataField.id == field_id,
879+
)
880+
.values(**datafield.model_dump(exclude_none=True)),
881+
)
882+
883+
870884
async def get_customdata_field(
871885
db: AsyncSession,
872886
field_id: UUID,

app/modules/cdr/endpoints_cdr.py

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3020,13 +3020,56 @@ async def create_custom_data_field(
30203020
id=uuid4(),
30213021
product_id=product_id,
30223022
name=custom_data_field.name,
3023+
can_user_answer=custom_data_field.can_user_answer,
30233024
)
30243025

30253026
cruds_cdr.create_customdata_field(db, db_data)
30263027
await db.flush()
30273028
return db_data
30283029

30293030

3031+
@module.router.patch(
3032+
"/cdr/sellers/{seller_id}/products/{product_id}/data/{field_id}/",
3033+
status_code=204,
3034+
)
3035+
async def update_custom_data_field(
3036+
seller_id: UUID,
3037+
product_id: UUID,
3038+
field_id: UUID,
3039+
custom_data_field: schemas_cdr.CustomDataFieldBase,
3040+
db: AsyncSession = Depends(get_db),
3041+
user: models_users.CoreUser = Depends(is_user_a_member),
3042+
):
3043+
await is_user_in_a_seller_group(
3044+
seller_id,
3045+
user,
3046+
db=db,
3047+
)
3048+
await check_request_consistency(db=db, seller_id=seller_id, product_id=product_id)
3049+
db_datafield = await cruds_cdr.get_customdata_field(db=db, field_id=field_id)
3050+
if db_datafield is None:
3051+
raise HTTPException(
3052+
status_code=404,
3053+
detail="Field not found.",
3054+
)
3055+
if db_datafield.product_id != product_id:
3056+
raise HTTPException(
3057+
status_code=403,
3058+
detail="Field does not belong to this product.",
3059+
)
3060+
3061+
datafield = schemas_cdr.CustomDataFieldBase(
3062+
name=custom_data_field.name,
3063+
can_user_answer=custom_data_field.can_user_answer,
3064+
)
3065+
3066+
await cruds_cdr.update_customdata_field(
3067+
db,
3068+
field_id=field_id,
3069+
datafield=datafield,
3070+
)
3071+
3072+
30303073
@module.router.delete(
30313074
"/cdr/sellers/{seller_id}/products/{product_id}/data/{field_id}/",
30323075
status_code=204,
@@ -3105,18 +3148,29 @@ async def create_custom_data(
31053148
db: AsyncSession = Depends(get_db),
31063149
user: models_users.CoreUser = Depends(is_user_a_member),
31073150
):
3108-
await is_user_in_a_seller_group(
3109-
seller_id,
3110-
user,
3111-
db=db,
3112-
)
31133151
await check_request_consistency(db=db, seller_id=seller_id, product_id=product_id)
31143152
db_field = await cruds_cdr.get_customdata_field(db=db, field_id=field_id)
31153153
if db_field is None:
31163154
raise HTTPException(
31173155
status_code=404,
31183156
detail="Field not found.",
31193157
)
3158+
if not (
3159+
is_user_member_of_any_group(user, [GroupType.admin_cdr])
3160+
or seller_id
3161+
in [
3162+
s.id
3163+
for s in await cruds_cdr.get_sellers_by_group_ids(
3164+
db=db,
3165+
group_ids=[g.id for g in user.groups],
3166+
)
3167+
]
3168+
) and not (db_field.can_user_answer and user_id == user.id):
3169+
raise HTTPException(
3170+
status_code=403,
3171+
detail="You are not authorized to add data for this field.",
3172+
)
3173+
31203174
if db_field.product_id != product_id:
31213175
raise HTTPException(
31223176
status_code=403,
@@ -3147,18 +3201,28 @@ async def update_custom_data(
31473201
db: AsyncSession = Depends(get_db),
31483202
user: models_users.CoreUser = Depends(is_user_a_member),
31493203
):
3150-
await is_user_in_a_seller_group(
3151-
seller_id,
3152-
user,
3153-
db=db,
3154-
)
31553204
await check_request_consistency(db=db, seller_id=seller_id, product_id=product_id)
31563205
db_data = await cruds_cdr.get_customdata(db=db, field_id=field_id, user_id=user_id)
31573206
if db_data is None:
31583207
raise HTTPException(
31593208
status_code=404,
31603209
detail="Field Data not found.",
31613210
)
3211+
if not (
3212+
is_user_member_of_any_group(user, [GroupType.admin_cdr])
3213+
or seller_id
3214+
in [
3215+
s.id
3216+
for s in await cruds_cdr.get_sellers_by_group_ids(
3217+
db=db,
3218+
group_ids=[g.id for g in user.groups],
3219+
)
3220+
]
3221+
) and not (db_data.field.can_user_answer and user_id == user.id):
3222+
raise HTTPException(
3223+
status_code=403,
3224+
detail="You are not authorized to edit data for this field.",
3225+
)
31623226
if db_data.field.product_id != product_id:
31633227
raise HTTPException(
31643228
status_code=403,
@@ -3185,18 +3249,28 @@ async def delete_customdata(
31853249
db: AsyncSession = Depends(get_db),
31863250
user: models_users.CoreUser = Depends(is_user_a_member),
31873251
):
3188-
await is_user_in_a_seller_group(
3189-
seller_id,
3190-
user,
3191-
db=db,
3192-
)
31933252
await check_request_consistency(db=db, seller_id=seller_id, product_id=product_id)
31943253
db_data = await cruds_cdr.get_customdata(db=db, field_id=field_id, user_id=user_id)
31953254
if db_data is None:
31963255
raise HTTPException(
31973256
status_code=404,
31983257
detail="Field Data not found.",
31993258
)
3259+
if not (
3260+
is_user_member_of_any_group(user, [GroupType.admin_cdr])
3261+
or seller_id
3262+
in [
3263+
s.id
3264+
for s in await cruds_cdr.get_sellers_by_group_ids(
3265+
db=db,
3266+
group_ids=[g.id for g in user.groups],
3267+
)
3268+
]
3269+
) and not (db_data.field.can_user_answer and user_id == user.id):
3270+
raise HTTPException(
3271+
status_code=403,
3272+
detail="You are not authorized to delete data for this field.",
3273+
)
32003274
if db_data.field.product_id != product_id:
32013275
raise HTTPException(
32023276
status_code=403,

app/modules/cdr/models_cdr.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ class CustomDataField(Base):
305305
ForeignKey("cdr_product.id"),
306306
)
307307
name: Mapped[str]
308+
can_user_answer: Mapped[bool]
308309

309310

310311
class CustomData(Base):

app/modules/cdr/schemas_cdr.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ class UpdateUserWSMessageModel(WSMessageModel):
301301

302302
class CustomDataFieldBase(BaseModel):
303303
name: str
304+
can_user_answer: bool
304305

305306

306307
class CustomDataFieldComplete(CustomDataFieldBase):
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""auto-answerable customdata
2+
3+
Create Date: 2025-08-20 14:06:53.519826
4+
"""
5+
6+
from collections.abc import Sequence
7+
from typing import TYPE_CHECKING
8+
9+
if TYPE_CHECKING:
10+
from pytest_alembic import MigrationContext
11+
12+
import sqlalchemy as sa
13+
from alembic import op
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = "06c94803745b"
17+
down_revision: str | None = "1294f07e0c02"
18+
branch_labels: str | Sequence[str] | None = None
19+
depends_on: str | Sequence[str] | None = None
20+
21+
22+
def upgrade() -> None:
23+
op.add_column(
24+
"cdr_customdata_field",
25+
sa.Column("can_user_answer", sa.Boolean(), nullable=True),
26+
)
27+
op.execute("UPDATE cdr_customdata_field SET can_user_answer = false")
28+
op.alter_column("cdr_customdata_field", "can_user_answer", nullable=False)
29+
30+
31+
def downgrade() -> None:
32+
op.drop_column("cdr_customdata_field", "can_user_answer")
33+
34+
35+
def pre_test_upgrade(
36+
alembic_runner: "MigrationContext",
37+
alembic_connection: sa.Connection,
38+
) -> None:
39+
pass
40+
41+
42+
def test_upgrade(
43+
alembic_runner: "MigrationContext",
44+
alembic_connection: sa.Connection,
45+
) -> None:
46+
pass

tests/test_cdr.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2569,7 +2569,7 @@ async def test_validate_purchase(client: TestClient):
25692569
def test_create_customdata_field(client: TestClient):
25702570
response = client.post(
25712571
f"/cdr/sellers/{seller.id}/products/{product.id}/data/",
2572-
json={"name": "Chambre"},
2572+
json={"name": "Chambre", "can_user_answer": False},
25732573
headers={"Authorization": f"Bearer {token_bde}"},
25742574
)
25752575
assert response.status_code == 201
@@ -2578,7 +2578,7 @@ def test_create_customdata_field(client: TestClient):
25782578
def test_create_customdata_field_user(client: TestClient):
25792579
response = client.post(
25802580
f"/cdr/sellers/{seller.id}/products/{product.id}/data/",
2581-
json={"name": "Chambre"},
2581+
json={"name": "Chambre", "can_user_answer": False},
25822582
headers={"Authorization": f"Bearer {token_user}"},
25832583
)
25842584
assert response.status_code == 403
@@ -2589,6 +2589,7 @@ async def test_delete_customdata_field(client: TestClient):
25892589
id=uuid.uuid4(),
25902590
product_id=product.id,
25912591
name="Supprime",
2592+
can_user_answer=False,
25922593
)
25932594
await add_object_to_db(field)
25942595

@@ -2610,6 +2611,7 @@ async def test_create_customdata(client: TestClient):
26102611
id=uuid.uuid4(),
26112612
product_id=product.id,
26122613
name="Field",
2614+
can_user_answer=False,
26132615
)
26142616
await add_object_to_db(customdata_field)
26152617
response = client.post(
@@ -2625,6 +2627,7 @@ async def test_create_customdata_user(client: TestClient):
26252627
id=uuid.uuid4(),
26262628
product_id=product.id,
26272629
name="Field",
2630+
can_user_answer=False,
26282631
)
26292632
await add_object_to_db(customdata_field)
26302633
response = client.post(
@@ -2640,6 +2643,7 @@ async def test_update_customdata(client: TestClient):
26402643
id=uuid.uuid4(),
26412644
product_id=product.id,
26422645
name="Edit",
2646+
can_user_answer=False,
26432647
)
26442648
await add_object_to_db(field)
26452649
customdata = models_cdr.CustomData(
@@ -2669,6 +2673,7 @@ async def test_update_customdata_user(client: TestClient):
26692673
id=uuid.uuid4(),
26702674
product_id=product.id,
26712675
name="Edit",
2676+
can_user_answer=False,
26722677
)
26732678
await add_object_to_db(field)
26742679
customdata = models_cdr.CustomData(
@@ -2691,6 +2696,7 @@ async def test_delete_customdata(client: TestClient):
26912696
id=uuid.uuid4(),
26922697
product_id=product.id,
26932698
name="Supprime",
2699+
can_user_answer=False,
26942700
)
26952701
await add_object_to_db(field)
26962702
customdata = models_cdr.CustomData(
@@ -2718,6 +2724,7 @@ async def test_customdata_deletion_on_purchase_deletion(client: TestClient):
27182724
id=uuid.uuid4(),
27192725
product_id=product.id,
27202726
name="Supprime",
2727+
can_user_answer=False,
27212728
)
27222729
await add_object_to_db(field)
27232730
customdata = models_cdr.CustomData(

tests/test_cdr_result.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,15 @@ async def init_objects():
141141
id=uuid.uuid4(),
142142
product_id=product1.id,
143143
name="Champ 1",
144+
can_user_answer=False,
144145
)
145146

146147
global customdata_field2
147148
customdata_field2 = models_cdr.CustomDataField(
148149
id=uuid.uuid4(),
149150
product_id=product2.id,
150151
name="Champ 2",
152+
can_user_answer=False,
151153
)
152154

153155
global product1_variant1

0 commit comments

Comments
 (0)