Skip to content

Commit

Permalink
Image remapping (#607)
Browse files Browse the repository at this point in the history
  • Loading branch information
duogenesis authored Jan 18, 2025
1 parent 13f4625 commit 5cbd812
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 5 deletions.
49 changes: 44 additions & 5 deletions duotypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from pydantic import (
BaseModel,
EmailStr,
Extra,
RootModel,
conint,
conlist,
constr,
field_validator,
model_validator,
Extra,
)
from datetime import datetime, date
from dateutil.relativedelta import relativedelta
Expand Down Expand Up @@ -39,6 +40,9 @@
MAX_GIF_DIM = 800
MIN_GIF_DIM = 10

MIN_PHOTO_POSITION = 1
MAX_PHOTO_POSITION = 7


def human_readable_size_metric(size_bytes):
# Define suffixes for metric prefixes
Expand Down Expand Up @@ -128,7 +132,7 @@ class Config:
arbitrary_types_allowed = True

class Base64File(BaseModel):
position: conint(ge=1, le=7)
position: conint(ge=MIN_PHOTO_POSITION, le=MAX_PHOTO_POSITION)
base64: str
bytes: bytes
image: Image.Image
Expand Down Expand Up @@ -181,6 +185,31 @@ class Config:
arbitrary_types_allowed = True


class PhotoAssignments(RootModel[Dict[
conint(ge=MIN_PHOTO_POSITION, le=MAX_PHOTO_POSITION),
conint(ge=MIN_PHOTO_POSITION, le=MAX_PHOTO_POSITION),
]]):
@field_validator('root')
def validate(cls, root):
values = list(root.values())

if len(values) != len(set(values)):
raise ValueError('Many photos were assigned to one position')

for k, v in root.items():
if k == v:
raise ValueError("Item can't be assigned to itself")

if not root:
raise ValueError('Must have at least one assignment')

return root

def dict(self, *args, **kwargs):
"""Override to return the dictionary directly."""
return super().dict(*args, **kwargs)['__root__']


class Theme(BaseModel):
title_color: constr(pattern=HEX_COLOR_PATTERN)
body_color: constr(pattern=HEX_COLOR_PATTERN)
Expand Down Expand Up @@ -277,19 +306,29 @@ class Config:


class DeleteOnboardeeInfo(BaseModel):
files: List[conint(ge=1, le=7)]
files: List[conint(ge=MIN_PHOTO_POSITION, le=MAX_PHOTO_POSITION)]


class DeleteProfileInfo(BaseModel):
files: Optional[
conlist(conint(ge=1, le=7), min_length=1, max_length=7)] = None
conlist(
conint(
ge=MIN_PHOTO_POSITION,
le=MAX_PHOTO_POSITION
),
min_length=1,
max_length=MAX_PHOTO_POSITION,
)
] = None

audio_files: Optional[
conlist(conint(ge=-1, le=-1), min_length=1, max_length=1)] = None
conlist(conint(ge=-1, le=-1), min_length=1, max_length=1)] = None


class PatchProfileInfo(BaseModel):
base64_file: Optional[Base64File] = None
base64_audio_file: Optional[Base64AudioFile] = None
photo_assignments: Optional[PhotoAssignments] = None
name: Optional[constr(
min_length=MIN_NAME_LEN,
max_length=MAX_NAME_LEN,
Expand Down
25 changes: 25 additions & 0 deletions service/person/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,31 @@ def patch_profile_info(req: t.PatchProfileInfo, s: t.SessionInfo):
)
SELECT 1
"""
elif field_name == 'photo_assignments':
case_sql = '\n'.join(
f'WHEN position = {int(k)} THEN {int(v)}'
for k, v in field_value.items()
)

# We set the positions to negative indexes first, to avoid violating
# uniqueness constraints
q1 = f"""
UPDATE
photo
SET
position = - (CASE {case_sql} ELSE position END)
WHERE
person_id = %(person_id)s
"""

q2 = """
UPDATE
photo
SET
position = ABS(position)
WHERE
person_id = %(person_id)s
"""
elif field_name == 'name':
q1 = """
UPDATE person
Expand Down
62 changes: 62 additions & 0 deletions test/functionality1/profile-info.sh
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,62 @@ test_photo () {
[[ "$(q "select COUNT(*) from photo")" -eq 0 ]]
}
test_photo_assignments () {
jc DELETE /profile-info -d '{ "files": [1, 2, 3, 4, 5, 6, 7] }'
jc PATCH /profile-info \
-d "{
\"base64_file\": {
\"position\": 1,
\"base64\": \"${img1}\",
\"top\": 0,
\"left\": 0
}
}"
jc PATCH /profile-info \
-d "{
\"base64_file\": {
\"position\": 2,
\"base64\": \"${img1}\",
\"top\": 0,
\"left\": 0
}
}"
wait_for_creation_by_uuid "$(q "select uuid from photo where position = 1")"
wait_for_creation_by_uuid "$(q "select uuid from photo where position = 2")"
# Overwriting an occupied position isn't allowed
! jc PATCH /profile-info \
-d "{ \"photo_assignments\": { \"1\": 2 } }"
[[ "$(q "select COUNT(DISTINCT uuid) from photo")" -eq 2 ]]
# Moving many photos to one position isn't allowed
! jc PATCH /profile-info \
-d "{ \"photo_assignments\": { \"1\": 3, \"2\": 3 } }"
[[ "$(q "select COUNT(DISTINCT uuid) from photo")" -eq 2 ]]
# Moving an unoccupied position to an occupied one isn't allowed
! jc PATCH /profile-info \
-d "{ \"photo_assignments\": { \"3\": 1 } }"
[[ "$(q "select COUNT(DISTINCT uuid) from photo")" -eq 2 ]]
# Files can be swapped
jc PATCH /profile-info \
-d "{ \"photo_assignments\": { \"1\": 2, \"2\": 1 } }"
# Files can be moved to unoccupied positions
jc PATCH /profile-info \
-d "{ \"photo_assignments\": { \"1\": 3, \"2\": 4 } }"
[[ "$(q "select COUNT(DISTINCT uuid) from photo")" -eq 2 ]]
}
test_audio () {
jc PATCH /profile-info \
-d "{ \"base64_audio_file\": { \"base64\": \"${snd1}\" } }"
Expand Down Expand Up @@ -233,6 +289,8 @@ test_verification_loss_ethnicity () {
}
test_verification_loss_photo_changed () {
jc DELETE /profile-info -d '{ "files": [1, 2, 3, 4, 5, 6, 7] }'
jc PATCH /profile-info \
-d "{
\"base64_file\": {
Expand Down Expand Up @@ -272,6 +330,8 @@ test_verification_loss_photo_changed () {
}
test_verification_loss_photo_removed () {
jc DELETE /profile-info -d '{ "files": [1, 2, 3, 4, 5, 6, 7] }'
jc PATCH /profile-info \
-d "{
\"base64_file\": {
Expand Down Expand Up @@ -334,6 +394,8 @@ test_club
test_photo
test_photo_assignments
test_audio
test_theme
Expand Down

0 comments on commit 5cbd812

Please sign in to comment.