
    mj.                         d Z ddlZddlZddlmZ ddlmZmZmZm	Z	 ddl
mZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZmZmZ ddlmZ ddlmZ  ej:                  e      Z G d d      Z y)u  ImageReviewService — 이미지 리뷰/재생성/primary/검증 서비스 (W5 F23 Phase 2).

ImageService facade(image_service.py)에서 review/primary/validation 관련 8개 공개 메서드를
literal lift. facade는 얇은 delegate만 유지.

이관된 메서드:
- get_image, update_review, regenerate_image, regenerate_needs_fix
- set_primary_image
- _create_validator, validate_image, get_validation
    N)Path)AnyDictListOptional)Session)settings)AppError)t)ActivityLogger)EntityCanon
ImageAsset
SceneStill)ImageValidator)image_to_dictc                      e Zd ZdZdededededdf
dZd	edefd
Z		 dd	ededede
e   def
dZ	 dd	ede
e   ddfdZ	 ddede
e   defdZ	 dd	ede
e   deeef   fdZde
e   fdZ	 dd	ede
e   deeef   fdZd	edeeef   fdZy)ImageReviewServiceu4   이미지 리뷰/재생성/primary/검증 서비스.db
project_idactor_idactivity_loggerreturnNc                 <    || _         || _        || _        || _        y N)_db_project_id	_actor_id_logger)selfr   r   r   r   s        W/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/image_review_service.py__init__zImageReviewService.__init__!   s!     %!&    image_idc                 
   | j                   j                  t              j                  t        j                  |k(  t        j
                  | j                  k(        j                         }|st        dt        d      d      |S )zGet a single image asset by ID.image.not_found  codemessagestatus_code)
r   queryr   filteridr   r   firstr
   r   )r   r#   imgs      r    	get_imagezImageReviewService.get_image-   sn     HHNN:&VJMMX-z/D/DHXHX/XYUW 	
 &+, 
 
r"   statusnotesipc           
          |dvrt        dt        d      d      | j                  |      }||_        ||_        | j
                  j                          | j                  j                  | j                  dd|| j                  ||d|       |S )	z!Update review status of an image.)approved	needs_fixzimage.invalid_review_status  r'   zimage.reviewimage)r1   r2   r   actionresource_typeresource_idr   detail
ip_address)r
   r   r0   r1   review_notesr   commitr   logr   r   )r   r#   r1   r2   r3   r/   s         r    update_reviewz ImageReviewService.update_review<   s     22278  nnX&
 ^^!! ''$u5 	 	
 
r"   c                     | j                  |      }d|_        | j                  j                          | j                  j                  | j                  dd|| j                  |       y)zMark an image for regeneration.regeneratingzimage.regenerater8   )r   r:   r;   r<   r   r>   N)r0   r1   r   r@   r   rA   r   r   )r   r#   r3   r/   s       r    regenerate_imagez#ImageReviewService.regenerate_image\   sZ     nnX&#
^^%! '' 	 	
r"   
episode_idc                    | j                   j                  t              j                  t        j                  | j
                  k(  t        j                  |k(  t        j                  dk(        j                         }|st        dt        d      d      |D ]	  }d|_         | j                   j                          | j                  j                  | j                  dd|| j
                  dt        |      i|	       t        |      S )
z:Mark all needs_fix images for an episode for regeneration.r6   zimage.no_needs_fixr7   r'   rD   zimage.regenerate_batchepisodecountr9   )r   r+   r   r,   r   r   rF   r1   allr
   r   r@   r   rA   r   len)r   rF   r3   imagesr/   s        r    regenerate_needs_fixz'ImageReviewService.regenerate_needs_fixo   s     HHNN:&V%%)9)99%%3!![0
 SU 	 )./  C'CJ ^^+#"''S[) 	 	
 6{r"   c           
         | j                  |      }ddl}|j                  xs d}|j                  d      xs |j                  d      }d}|r'|j	                  d|      }|r|j                  d      nd}|j                  r| j                  j                  t              j                  t        j                  | j                  k(  t        j                  |j                  k(  t        j                  |k7        j                         }	|	D ]W  }
|
j                  xs d}|j                  d      xs |j                  d      }|r|r|s>||v sCd|
_        K|rN|rQd|
_        Y |j                   r| j                  j                  t              j                  t        j                  | j                  k(  t        j                   |j                   k(  t        j                  |k7        j                         }	|	D ]	  }
d|
_         d|_        | j                  j#                          | j$                  j'                  | j(                  dd	|| j                  |j                  |j                   d
|       t+        |      S )zISet an image as the primary image for its entity/still, unsetting others.r   N z[composite:z[outlook_id:z;\[(composite:[a-f0-9-]+:[a-f0-9-]+|outlook_id:[a-f0-9-]+)\]   zimage.set_primaryr8   )	entity_idstill_idr9   )r0   reprompt_used
startswithmatchgrouprQ   r   r+   r   r,   r   r   r-   rJ   
is_primaryrR   r@   r   rA   r   r   )r   r#   r3   r/   _re_sppromptis_compositecomposite_keymsiblingssib
sib_promptsib_is_composites                r    set_primary_imagez$ImageReviewService.set_primary_image   s    nnX& 	&B((7\6;L;L^;\[]cdA*+AGGAJM==z*))T-=-==((CMM9MMX-
     __2
#-#8#8#G#p:K`K`aoKp $4$*)D)*%.>%&CN   <<z*))T-=-==''3<<7MMX-
    !"   ^^&! ''!$CLLI 	 	
 S!!r"   c                 \    ddl m}  |       st        j                  d       y t	               S )Nr   has_openai_keyz;Skipping image validation -- OpenAI API key not configured.)app.core.openai_keysre   loggerinfor   )r   re   s     r    _create_validatorz$ImageReviewService._create_validator   s$    7KKUVr"   c                    ddl m}  |       st        dt        d      d      | j	                  |      }t        |j                        }|j                         st        dt        d      d      |j                         }t               }|j                  dk(  ri }|j                  r| j                  j                  t              j                  t        j                   |j                  k(        j#                         }	|	r7|	j$                  |	j&                  |	j(                  xs d	|	j*                  xs d
d}|j-                  ||      }
n{i }g }|j.                  rW| j                  j                  t0              j                  t0        j                   |j.                  k(        j#                         }|r|j2                  xs d	|j4                  xs d	|j6                  xs d	d}	 t9        j:                  |j<                  xs d      }|D ]  }t?        |t@              r%|jC                  d      xs |jC                  dd	      n|}| j                  j                  t              j                  t        j                   |k(        j#                         }	|	s|jE                  |	j$                          	 |jO                  |||      }
|
jC                  dd      }||_(        t9        jR                  |
d      |_*        |dk  r;|
jC                  dg       }|rdjW                  |      nd}d|_,        d| d| |_-        | j                  j]                          | j^                  ja                  | jb                  dd|| jd                  ||
jC                  dd      d|       |
S # t8        jF                  tH        f$ r,}tJ        jM                  d|j.                  |       Y d }~-d }~ww xY w) Nr   rd   zimage.openai_key_missingr7   r'   r%   r&   	referencerO   z{})nameentity_typedescriptionstable_traits)scene_heading
beat_titlestill_frame_promptz[]r-   rQ   uY   visible_entities_json parse failed for still %s: %s — entity_names 없이 검증 계속scoreF)ensure_ascii<   issuesz; zLow validation scorer6   z[auto-validation] score=z: zimage.validater8   passed)rs   rw   r9   )3rf   re   r
   r   r0   r   	file_pathexists
read_bytesr   
asset_typerQ   r   r+   r   r,   r-   r.   rl   rm   rn   ro   validate_reference_imagerR   r   screenplay_scene_headingrq   rr   jsonloadsvisible_entities_json
isinstancedictgetappendJSONDecodeError	TypeErrorrg   warningvalidate_scene_imagevalidation_scoredumpsvalidation_resultjoinr1   r?   r@   r   rA   r   r   )r   r#   r3   re   r/   fpimage_bytes	validatorentity_infoentityresult
scene_infoentity_namesstillvisible_idsveidexcrs   rv   
issue_texts                        r    validate_imagez!ImageReviewService.validate_image   sH   
 	8/45  nnX&#-- yy{&+,  mmo"$	>>[(*,K}}HHNN;/VKNNcmm;<UW 
  &'-'9'9'-'9'9'?R)/)=)=)E	#K 77[QF)+J&(L||HHNN:.VJMMS\\9:UW 
 ).)G)G)M2&+&6&6&<".3.F.F.L""J
&*jj1L1L1TPT&U!,AMWXY[_M`155;#H!%%R2HfgC ${ ;!'#(=!>!& #
  & , 3 3FKK @ "- 33K\ZF

7A&$ $

6 F2:ZZ"-F.46*:PJ$CJ!9%:,OC^^#! ''"fjj5.IJ 	 	
 7 !00)< wLL# s   (B.N" N" "O';!O""O'c                    | j                  |      }||j                  d g dd}|j                  r`	 t        j                  |j                        }|j                  d      |d<   |j                  dg       |d<   |j                  dd      |d<   |S |S # t        j                  $ r,}t        j                  d|j                  |       Y d }~|S d }~ww xY w)NrO   )r#   rs   rw   rv   rn   rw   rv   rn   z/validation_result parse failed for image %s: %s)
r0   r   r   r~   r   r   r   rg   r   r-   )r   r#   r/   r   parsedr   s         r    get_validationz!ImageReviewService.get_validationD  s    nnX& ))"
   _C$9$9:#)::h#7x #)::h#;x (.

="(E}% v '' _PRURXRXZ]^^_s   AB C%!CCr   )__name__
__module____qualname____doc__
OrmSessionstrr   r!   r   r0   r   rB   rE   intrM   r   r   rb   r   ri   r   r    r"   r    r   r      s{   >
'
' 
' 	
'
 (
' 

'# * ( !  	
 SM 
F !

 SM
 
	
, !%% SM% 
	%T !A"A" SMA" 
c3h	A"N 8N#;   !^^ SM^ 
c3h	^@s tCH~ r"   r   )!r   r~   loggingpathlibr   typingr   r   r   r   sqlalchemy.ormr   r   app.core.configr	   app.core.errorsr
   app.i18n.loaderr   app.logging.activity_loggerr   app.models.projectr   r   r   app.modules.image_validatorr   "app.services.image_service_helpersr   	getLoggerr   rg   r   r   r"   r    <module>r      sT   	    , , 0 $ $  6 B B 6 <			8	$w wr"   