1818from calcs .pharmacokinetics import concentration_at
1919
2020from .calc_bridge import doses_for_user
21- from .constants import Brand , DrinkType
21+ from .constants import Brand , DRINK_ICON_KEYS , DrinkType
2222from .models import CaffeineLog , Drink , SleepLog
2323from .presets import find_preset , size_labels
2424
@@ -47,6 +47,7 @@ class Meta:
4747 "brand_display" ,
4848 "name" ,
4949 "size" ,
50+ "icon_key" ,
5051 "caffeine_mg" ,
5152 "is_favorite" ,
5253 "is_recent" ,
@@ -127,6 +128,11 @@ def validate(self, attrs):
127128 if brand != Brand .CUSTOM :
128129 attrs ["custom_brand_name" ] = ""
129130
131+ icon_key = attrs .get ("icon_key" , getattr (self .instance , "icon_key" , "" ))
132+ if icon_key :
133+ drink_type = attrs .get ("type" , getattr (self .instance , "type" , DrinkType .COFFEE ))
134+ _validate_icon_key (icon_key , drink_type )
135+
130136 labels = size_labels (brand )
131137 if not labels :
132138 return attrs
@@ -165,6 +171,7 @@ def create(self, validated_data):
165171 "type" : validated_data .get ("type" , DrinkType .COFFEE ),
166172 "caffeine_mg" : validated_data ["caffeine_mg" ],
167173 "is_favorite" : validated_data .get ("is_favorite" , False ),
174+ "icon_key" : validated_data .get ("icon_key" , "" ),
168175 },
169176 )
170177
@@ -202,6 +209,7 @@ class DrinkFromPresetSerializer(serializers.Serializer):
202209 brand = serializers .CharField ()
203210 name = serializers .CharField ()
204211 size = serializers .CharField (required = False , allow_blank = True )
212+ icon_key = serializers .CharField (required = False , allow_blank = True , default = "" )
205213 is_favorite = serializers .BooleanField (default = False )
206214
207215 def validate (self , attrs ):
@@ -211,7 +219,7 @@ def validate(self, attrs):
211219 다시 조회하면 카탈로그를 두 번 읽게 되므로 여기서 한 번만 읽는다.
212220
213221 Args:
214- attrs (dict): brand, name, size, is_favorite
222+ attrs (dict): brand, name, size, icon_key, is_favorite
215223
216224 Returns:
217225 dict: _preset 키가 추가된 attrs
@@ -229,6 +237,8 @@ def validate(self, attrs):
229237 raise serializers .ValidationError (
230238 {"size" : "해당 사이즈의 카페인 정보가 아직 등록되지 않았습니다." }
231239 )
240+ if attrs .get ("icon_key" ):
241+ _validate_icon_key (attrs ["icon_key" ], preset ["type" ])
232242
233243 attrs ["_preset" ] = preset
234244 return attrs
@@ -237,7 +247,7 @@ def create(self, validated_data):
237247 """검증된 프리셋 값으로 Drink를 생성한다.
238248
239249 type, caffeine_mg는 전부 프리셋에서 가져오고, 클라이언트가 정할 수
240- 있는 값은 is_favorite뿐이다.
250+ 있는 값은 icon_key· is_favorite뿐이다.
241251
242252 Args:
243253 validated_data (dict): _preset이 포함된 검증 결과
@@ -256,6 +266,7 @@ def create(self, validated_data):
256266 "type" : preset ["type" ],
257267 "caffeine_mg" : preset ["caffeine_mg" ],
258268 "is_favorite" : validated_data ["is_favorite" ],
269+ "icon_key" : validated_data .get ("icon_key" , "" ),
259270 },
260271 )
261272
@@ -497,6 +508,23 @@ def update(self, instance, validated_data):
497508 return super ().update (instance , validated_data )
498509
499510
511+ def _validate_icon_key (icon_key , drink_type ):
512+ """icon_key가 drink_type에서 고를 수 있는 아이콘인지 확인한다.
513+
514+ Args:
515+ icon_key (str): static/images/icons/<icon_key>.svg
516+ drink_type (str): constants.DrinkType 값
517+
518+ Raises:
519+ serializers.ValidationError: 해당 종류에 없는 아이콘 키인 경우
520+ """
521+ valid_keys = DRINK_ICON_KEYS .get (drink_type , [])
522+ if icon_key not in valid_keys :
523+ raise serializers .ValidationError (
524+ {"icon_key" : f"해당 종류에서 고를 수 없는 아이콘입니다. 가능: { ', ' .join (valid_keys )} " }
525+ )
526+
527+
500528@transaction .atomic
501529def _create_or_revive (user , brand , custom_brand_name , name , size , defaults ):
502530 """중복 음료를 새로 만들지 않고 되살리거나 갱신한다.
@@ -516,7 +544,7 @@ def _create_or_revive(user, brand, custom_brand_name, name, size, defaults):
516544 custom_brand_name (str): brand=custom일 때의 자유 입력 브랜드명. 그 외엔 빈 문자열
517545 name (str): 음료명
518546 size (str): 사이즈 라벨. 사이즈가 없으면 빈 문자열
519- defaults (dict): type, caffeine_mg, is_favorite
547+ defaults (dict): type, caffeine_mg, is_favorite, icon_key
520548
521549 Returns:
522550 Drink: 생성/복구/재사용된 인스턴스
@@ -538,6 +566,7 @@ def _create_or_revive(user, brand, custom_brand_name, name, size, defaults):
538566 existing .is_active = True
539567 existing .type = defaults ["type" ]
540568 existing .caffeine_mg = defaults ["caffeine_mg" ]
569+ existing .icon_key = defaults .get ("icon_key" , "" )
541570
542571 # 이미 활성 상태라면 즐겨찾기 요청만 반영한다.
543572 # 과거 로그가 참조하는 caffeine_mg를 임의로 덮어쓰지 않기 위해서다.
0 commit comments