
    
 i4                     0   d Z ddlZddlZddlZddlmZm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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&  ejN                  e(      Z)de*fdZ+de*fdZ, G d d      Z-y)uq  이미지 생성 서비스 — 공용 유틸리티 + list/get/review/primary/upload/compose_prompts/validate.

레퍼런스/씬 이미지 생성 파이프라인은 Phase 3b.2 / 3b.3 / 3b.5 / 3b.5b 완료 후
ReferenceImageService / SceneImageService로 완전히 이관되었다. 본 모듈은 공용
helper shim과 엔드포인트용 보조 메서드만 유지한다.
    N)datetimetimezonePath)AnyDictListOptional)Session)settings)AppError)t)ActivityLogger)EpisodeEntityCanonGenerationTrace
ImageAssetProjectSettings
SceneStill)GenerationTracker)ImageComposerService)ImageReviewService)ImageUploadServicereturnc                  d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     P/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/image_service.py_nowr#   %   s    <<%//11r!   c                  <    t        t        j                               S r   )struuiduuid4r    r!   r"   _new_idr(   )   s    tzz|r!   c                   (   e Zd ZdZdedededdfdZdedefd	Zd/dedefd
Z		 	 	 	 	 d0de
e   de
e   de
e   de
e   de
e   deeeef      fdZededeeef   fd       Ze	 d/deeef   dede
d   deeef   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	 	 	 	 d1ded ede
e   de
e   de
e   de
e   deeef   fd!Zdedeeef   fd"Z	 	 d2d#ed$edeeeef      fd%Zdeeef   fd&Zdedeeeef      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 	 d/dede
e   deeef   fd*Z!deeef   fd+Z"	 	 	 d3d,e
e   d-e
e   de
e   deeef   fd.Z#y)4ImageServiceu&   이미지 생성 및 관리 서비스.db
project_idactor_idr   Nc                    || _         t        |      | _        || _        || _        t        |||| j                        | _        t        |||| j                        | _        t        |||| j                        | _
        y )N)r+   r,   r-   activity_logger)_dbr   _logger_project_id	_actor_idr   _review_svcr   _composer_svcr   _upload_svc)selfr+   r,   r-   s       r"   __init__zImageService.__init__0   s     %b)%!-! LL	
 2! LL	
 .! LL	
r!   
episode_idc                 
   | j                   j                  t              j                  t        j                  |k(  t        j
                  | j                  k(        j                         }|st        dt        d      d      |S )Nzepisode.not_found  codemessagestatus_code)
r0   queryr   filteridr,   r2   firstr   r   )r7   r9   eps      r"   _get_episodezImageService._get_episodeM   sl    HHNN7#VGJJ*,g.@.@DDTDT.TUUW 	
 (-. 
 	r!   c           	         | j                   j                  t              j                  t        j                  | j
                  k(        j                         }|rg|j                  r[t        j                  |j                        }d|j                  dd       d|j                  dd       d|j                  dd       dS |rd	d
lm}  |t        j                        | j
                  z  dz  dz  |z  dz  dz  }|j                         rft        j                  |j!                  d            j                  di       }|j                  dd      }|j                  dd      }|s|r	d| d| dS y)uP   프로젝트 세계관을 T2I 프롬프트 앞에 붙일 컨텍스트로 변환.z)Photorealistic cinematic still. Setting: era z, regionz	. Avoid: 
must_avoid.r   r   checkpointsepisodesvisual_world_ruleszmanifest.jsonzutf-8)encodingdatazPhotorealistic cinematic still.)r0   r@   r   rA   r,   r2   rC   style_rules_jsonjsonloadsgetpathlibr   r   projects_direxists	read_text)	r7   r9   pssrr   cpvwrrG   rI   s	            r"   _get_style_contextzImageService._get_style_context[   sf    XX^^O,33&&$*:*::<<AEG 	"%%B//0BFF5,-Rx0C/D E&&b12!5 $X**+d.>.>> ",-/9:&')89 
 yy{jjw!?@DDVRPggeR(2.&Fse2fXUVWW0r!   
asset_type	entity_idstill_id
outlook_idc                 D   | j                   j                  t              j                  t        j                  | j
                  k(        }|r"|j                  t        j                  |k(        }|r"|j                  t        j                  |k(        }|r"|j                  t        j                  |k(        }|r"|j                  t        j                  |k(        }|r2|j                  t        j                  j                  d| d            }|j                  t        j                  j                               j                         }i }g }	|D ]  }
| j!                  |
      }|
j                  r|
j                  |vrd| j                   j                  t"              j                  t"        j$                  |
j                  k(        j'                         ||
j                  <   | j)                  ||
|j+                  |
j                              }|	j-                  |        |	S )zList images, optionally filtered by type, episode, entity, or still.

        outlook_id: when provided, returns images whose prompt_used contains
        'outlook_id:{outlook_id}' (composite images referencing this outlook).
        z%outlook_id:%)r0   r@   r   rA   r,   r2   r^   r9   r_   r`   prompt_usedlikeorder_by
created_atdescall_image_to_dictr   rB   rC   _enrich_image_dictrT   append)r7   r9   r^   r_   r`   ra   r@   imagesstill_cacheresultsimgds               r"   list_imageszImageService.list_imagesy   s    z*11*2G2G4K[K[2[\LL!6!6*!DEELL!6!6*!DEELL!5!5!BCELL!4!4!@AELL!7!7!<!<|J<WX=Y!Z[E
 5 5 : : <=AAC 8:C##C(A||<<{2z2
 =>  -
 ++AsKOOCLL4QRNN1  r!   rp   c                     ddl m}  ||       S )u8   Phase 3b.2 shim → image_service_helpers.image_to_dict.r   )image_to_dict)"app.services.image_service_helpersrt   )rp   rt   s     r"   rj   zImageService._image_to_dict   s     	ES!!r!   rq   stillr   c                    |j                   r\	 t        j                  |j                         }|j                  d      | d<   |j                  d      | d<   |j                  d      | d<   | j                  d|j                         |r|j                  xs dj                         }|j                  xs |j                  xs d	j                         }|j                  xs d	}|j                  d
      r |j                  d
d      j                         n|j                         }||k(  | d<   ||k(  | d<   d}|dk(  rd}n|dk(  rd}|rt        ||d      nd| d<   | S # t        j                  t
        f$ r Y w xY w)zGAdd parsed angle/color fields and recommendation info to an image dict.
horizontalangle_horizontalverticalangle_verticalzoom
angle_zoomcolor_promptrH   originalvariant_is_recommendedselected_for_pdfN	variant_avariation_a_reason	variant_bvariation_b_reasonrecommendation_reason)angle_appliedrR   rS   rT   JSONDecodeError	TypeError
setdefaultcolor_appliedrecommended_variantupperselected_variantvariant_type
startswithreplacegetattr)	rq   rp   rv   anglerecselvtvt_keyreason_attrs	            r"   rk   zImageService._enrich_image_dict   sr    

3#4#45(-		,(?$%&+ii
&;"#"'))F"3,
 	
^S%6%67,,299;C))TU-F-FT*[[]C""0jB;===;TRZZ
B/557Z\ZbZbZdF#&&=A%(F]A ! K[ 2{"2NYT)J_cA%&- (()4 s   AE E65E6image_idc                 8    | j                   j                  |      S r   )r4   	get_imager7   r   s     r"   r   zImageService.get_image   s    ))(33r!   statusnotesipc                 @    | j                   j                  ||||      S N)r   )r4   update_review)r7   r   r   r   r   s        r"   r   zImageService.update_review   s$     --h"-MMr!   c                 <    | j                   j                  ||      S r   )r4   regenerate_imager7   r   r   s      r"   r   zImageService.regenerate_image   s     
 00b0AAr!   c                 <    | j                   j                  ||      S r   )r4   regenerate_needs_fix)r7   r9   r   s      r"   r   z!ImageService.regenerate_needs_fix   s     
 44ZB4GGr!   c                 <    | j                   j                  ||      S r   )r4   set_primary_imager   s      r"   r   zImageService.set_primary_image   s     
 11(r1BBr!   
file_bytesfilenamec                 D    | j                   j                  ||||||      S )N)r   r   r_   r`   r9   r   )r6   upload_custom_image)r7   r   r   r_   r`   r9   r   s          r"   r   z ImageService.upload_custom_image   s4     33!! 4 
 	
r!   c                 B   | j                   j                  t              j                  t        j                  |k(  t        j
                  | j                  k(        j                         }|st        dt        d      d      | j                   j                  t              j                  t        j
                  | j                  k(  t        j                  |k(        j                  t        j                  j                               j                         }g g g g d}|D ]c  }|j                   xs d}| j#                  |      }| j%                  |||      }||v r||   j'                  |       P|d   j'                  |       e ||j(                  |j*                  |dS )u<   씬의 모든 이미지를 original/A/B 그룹으로 반환.zstill.not_foundr;   r<   )r   r   r   otherr   )r`   r   r   groups)r0   r@   r   rA   rB   r,   r2   rC   r   r   r   r`   rf   rg   rh   ri   r   rj   rk   rl   r   r   )r7   r`   rv   rm   r   rp   r   rq   s           r"   get_still_images_groupedz%ImageService.get_still_images_grouped
  sk    HHNN:&VJMMX-z/D/DHXHX/XYUW 	
 &+,  HHNN:&V%%)9)99##x/ Xj++0023SU 	 02QS^`"aC!!,WB##C(A''36AV|r
!!!$w&&q)  !#(#<#< % 6 6	
 	
r!   offsetlimitc                    | j                   j                  t              j                  t        j                  | j
                  k(        j                  t        j                  j                               j                  |      j                  |      j                         }|D cg c]  }t        j                  |       c}S c c}w )z&Get all generation traces (paginated).)r0   r@   r   rA   r,   r2   rf   rg   rh   r   r   ri   r   _trace_to_dict)r7   r   r   tracestrs        r"   get_generation_tracesz"ImageService.get_generation_traces:  s     HHNN?+VO..$2B2BBCXo005578VF^U5\SU 	 @FFv!004vFFFs   %Cc                 b    t        | j                  | j                        }|j                         S )z$Get aggregated rejection statistics.)r   r0   r2   get_rejection_stats)r7   trackers     r"   r   z ImageService.get_rejection_statsJ  s'    #DHHd.>.>?**,,r!   c                 f    t        | j                  | j                        }|j                  |      S )z&Get traces for a specific image asset.)image_asset_id)r   r0   r2   
get_traces)r7   r   r   s      r"   get_image_traceszImageService.get_image_tracesO  s,    #DHHd.>.>?!!!::r!   c                 <    | j                   j                  ||      S r   )r4   validate_imager   s      r"   r   zImageService.validate_imageX  s     
 ..xB.??r!   c                 8    | j                   j                  |      S r   )r4   get_validationr   s     r"   r   zImageService.get_validation_  s    ..x88r!   c                 <    | j                   j                  ||      S r   )r5   compose_prompts)r7   r`   r   s      r"   r   zImageService.compose_promptsf  s     
 !!11(r1BBr!   c                 6    | j                   j                         S r   )r5   get_composer_prompt)r7   s    r"   r   z ImageService.get_composer_promptm  s    !!5577r!   system_promptuser_promptc                 >    | j                   j                  |||      S )N)r   r   r   )r5   update_composer_prompt)r7   r   r   r   s       r"   r   z#ImageService.update_composer_promptp  s+     !!88'# 9 
 	
r!   r   )NNNNN)NNNN)r   2   )NNN)$__name__
__module____qualname____doc__
OrmSessionr%   r8   r   rE   r]   r
   r	   r   r   rr   staticmethodr   rj   rk   r   r   r   intr   r   bytesr   r   r   r   r   r   r   r   r   r   r    r!   r"   r*   r*   -   s   0

 
 	

 

:s w 1S 1C 1@ %)$(#'"&$((SM( SM( C=	(
 3-( SM( 
d38n	(T "J "4S> " "
  )-#S>## %# 
c3h	# #J4# 4* 4 !NN N 	N
 SMN 
N !BB SMB 
	B !HH SMH 
	H !CC SMC 
c3h	C $("&$( 

 
 C=	

 3-
 SM
 SM
 
c3h
$*
*
 
c3h*
d GG G 
d38n		G -T#s(^ -
; ;d38n1E ; !@@ SM@ 
c3h	@9s 9tCH~ 9 !CC SMC 
c3h	C8T#s(^ 8
 (,%) 	

}

 c]

 SM	


 
c3h

r!   r*   ).r   rR   loggingr&   r   r   rU   r   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   r   r   r   app.modules.generation_trackerr   #app.services.image_composer_servicer   !app.services.image_review_servicer   !app.services.image_upload_servicer   	getLoggerr   loggerr%   r#   r(   r*   r    r!   r"   <module>r      s~       '  , , 0 $ $  6  = D @ @			8	$2c 2 M
 M
r!   