
    i                     |    d Z ddl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  ee      ZdZg d	Z G d
 d      Zy)u   
Google Ads API 클라이언트 모듈

Usage:
    from utils.google_ads_client import GoogleAdsClient
    client = GoogleAdsClient()
    campaigns = client.list_campaigns()
    N)Any)GoogleAdsClient)GoogleAdsException)load_env_keys)
get_loggerz/home/jay/workspace/.env.keys)zmetrics.impressionszmetrics.clickszmetrics.cost_microszmetrics.ctrzmetrics.average_cpczmetrics.conversionsc                      e Zd ZdZefdeddfdZedede	fd       Z
d Zde	fd	Zd+d
edee	   fdZdede	fdZ	 d,dededede	fdZdedede	fdZdedefdZ	 	 d-dedz  d
edee	   fdZ	 d,dedededede	f
dZdedede	fdZdedefdZd+ded
edee	   fdZdedee	   dee	   fdZdededede	fdZ	 	 	 	 	 d.d ed!ed"ed#ee   dz  d$edz  d%edz  dee	   fd&Zded'ee   d(ee   d)ede	f
d*Zy)/r   u   Google Ads API 클라이언트env_keys_pathreturnNc                 x   || _         t        |       g d}|D cg c]$  }t        j                  j	                  |      r#|& }}|rt        d|       t        j                  d   | _        t        j                  d   | _        t        j                  d   | _        t        j                  d   | _	        t        j                  d   | _
        t        j                  | j                  | j                  | j                  | j                  dd	      | _        t        j                  d
| j                         yc c}w )u   
        GoogleAdsClient 초기화.

        Args:
            env_keys_path: .env.keys 파일 경로 (기본값: /home/jay/workspace/.env.keys)

        Raises:
            ValueError: 필수 환경변수가 누락된 경우
        )GOOGLE_ADS_DEVELOPER_TOKENGOOGLE_ADS_CLIENT_IDGOOGLE_ADS_CLIENT_SECRETGOOGLE_ADS_REFRESH_TOKENGOOGLE_ADS_CUSTOMER_IDu   필수 환경변수 누락: r   r   r   r   r   T)developer_token	client_idclient_secretrefresh_tokenuse_proto_plusu1   GoogleAdsClient 초기화 완료 (customer_id=%s)N)_env_keys_pathr   osenvironget
ValueError_developer_token
_client_id_client_secret_refresh_token_customer_id_GoogleAdsClientload_from_dict_clientloggerinfo)selfr	   required_keyskmissings        H/home/jay/workspace/.worktrees/task-2116-dev1/utils/google_ads_client.py__init__zGoogleAdsClient.__init__&   s    ,m$
 ,E2::>>!3D1EE;G9EFF "

+G H**%;< jj)CD jj)CDJJ'?@'66#'#8#8!__!%!4!4!%!4!4"&
 	GIZIZ[) Fs   $D7 D7objc                     t        | d      r&t        | d      rddlm}  || j                  d      S t        | d      r@| j                  j                         D ci c]  \  }}|j                  d      r|| c}}S t        |       S c c}}w )	u/   SDK proto-plus 객체를 순수 dict로 변환.	__class___pbr   )MessageToDictT)preserving_proto_field_name__dict___)hasattrgoogle.protobuf.json_formatr/   r.   r1   items
startswithdict)r+   r/   r'   vs       r)   _to_dictzGoogleAdsClient._to_dictT   sv     3$e)<A dKK3
#%(\\%7%7%9STQcARAqDSSCy Ts   B
6B
c                 8    | j                   j                  d      S )u   GoogleAdsService 반환.GoogleAdsService)r"   get_service)r%   s    r)   _get_ga_servicezGoogleAdsClient._get_ga_service`   s    ||''(:;;    c                    d}	 | j                         }|j                  | j                  |      }t        |      }|si S |d   }|j                  }t        |j                        |j                  |j                  |j                  |j                  |j                  j                  d}t        j                  d| j                         |S # t        $ r}t        j!                  d|        d}~ww xY w)uo  
        광고 계정 상태 및 예산 정보를 반환한다.

        GAQL로 customer 리소스를 조회하여 계정 이름, 통화, 시간대 등을 반환한다.

        Returns:
            dict: id, descriptive_name, currency_code, time_zone, auto_tagging_enabled 포함 dict

        Raises:
            GoogleAdsException: API 호출 실패 시
        a-  
            SELECT
                customer.id,
                customer.descriptive_name,
                customer.currency_code,
                customer.time_zone,
                customer.auto_tagging_enabled,
                customer.status
            FROM customer
            LIMIT 1
        customer_idqueryr   )iddescriptive_namecurrency_code	time_zoneauto_tagging_enabledstatusu,   계정 정보 조회 완료 (customer_id=%s)u   계정 정보 조회 실패: %sN)r=   searchr   listcustomerstrrC   rD   rE   rF   rG   rH   namer#   debugr   error)	r%   rB   
ga_serviceresponserowsrowrK   resultes	            r)   get_account_infoz GoogleAdsClient.get_account_infoh   s    
	--/J!((T5F5Fe(TH>D	q'C||H(++&$,$=$=!)!7!7%//(0(E(E"//..F LLGIZIZ[M! 	LL:A>	s   ;C  B
C 	C0C++C0limitc                 2   d| d}	 | j                         }|j                  | j                  |      }g }|D ]  }|j                  }|j	                  t        |j                        |j                  |j                  j                  |j                  j                  |j                  |j                  |j                  j                  d        t        j                  dt!        |             |S # t"        $ r}t        j%                  d|        d}~ww xY w)ud  
        캠페인 목록을 반환한다.

        Args:
            limit: 최대 반환 개수 (기본값 25)

        Returns:
            list[dict]: 캠페인 정보 목록 (id, name, status, advertising_channel_type,
                        start_date, end_date 포함)

        Raises:
            GoogleAdsException: API 호출 실패 시
        ag  
            SELECT
                campaign.id,
                campaign.name,
                campaign.status,
                campaign.advertising_channel_type,
                campaign.start_date,
                campaign.end_date,
                campaign_budget.amount_micros
            FROM campaign
            ORDER BY campaign.id
            LIMIT 	
        r@   rC   rM   rH   advertising_channel_type
start_dateend_datebudget_amount_microsu%   캠페인 목록 조회 완료: %d건u"   캠페인 목록 조회 실패: %sN)r=   rI   r   campaignappendrL   rC   rM   rH   r[   r\   r]   campaign_budgetamount_microsr#   rN   lenr   rO   )	r%   rW   rB   rP   rQ   rT   rS   r_   rU   s	            r)   list_campaignszGoogleAdsClient.list_campaigns   s    ' 		--/J!((T5F5Fe(THF <<!(++. ("*//"6"64<4U4U4Z4Z&.&9&9$,$5$5030C0C0Q0Q
 LL@#f+NM! 	LL=qA	s   C(C1 1	D:DDcampaign_idc                 2   d| d}	 | j                         }|j                  | j                  |      }t        |      }|st	        d|       |d   }|j
                  }t        |j                        |j                  |j                  j                  |j                  j                  |j                  |j                  |j                  j                  d}t        j!                  d|       |S # t"        $ r}	t        j%                  d||	        d	}	~	ww xY w)
u7  
        단일 캠페인 정보를 반환한다.

        Args:
            campaign_id: 캠페인 ID

        Returns:
            dict: 캠페인 상세 정보

        Raises:
            GoogleAdsException: API 호출 실패 시
            ValueError: 해당 캠페인을 찾을 수 없는 경우
        aT  
            SELECT
                campaign.id,
                campaign.name,
                campaign.status,
                campaign.advertising_channel_type,
                campaign.start_date,
                campaign.end_date,
                campaign_budget.amount_micros
            FROM campaign
            WHERE campaign.id = z
            LIMIT 1
        r@   u)   캠페인을 찾을 수 없습니다: id=r   rZ   u   캠페인 조회 완료: id=%su#   캠페인 조회 실패 (id=%s): %sN)r=   rI   r   rJ   r   r_   rL   rC   rM   rH   r[   r\   r]   ra   rb   r#   rN   r   rO   )
r%   re   rB   rP   rQ   rR   rS   r_   rT   rU   s
             r)   get_campaignzGoogleAdsClient.get_campaign   s
   
! "- .		--/J!((T5F5Fe(TH>D #L[M!Z[[q'C||H(++& "//..,4,M,M,R,R&11$--(+(;(;(I(IF LL9;GM! 	LL>QO	s   C'C0 0	D9DDrM   budget_amountrH   c                 0   	 | j                   j                  d      }| j                   j                  d      }|j                  }| d|_        ||_        | j                   j                  j                  j                  |_	        |j                  | j                  |g      }|j                  d   j                  }| j                   j                  d      }	| j                   j                  d      }
|
j                  }||_        | j                   j                  j                  |   |_        | j                   j                  j                   j"                  |_        ||_        d|j(                  _        d|j(                  _        |	j/                  | j                  |
g      }|j                  d   j                  }|j1                  d	      d
   }|||||d}t2        j5                  d||       |S # t6        $ r}t2        j9                  d|        d}~ww xY w)u  
        새 캠페인을 생성한다. (검색 캠페인 기본값)

        예산을 먼저 생성한 뒤 캠페인에 연결한다.

        Args:
            name: 캠페인 이름
            budget_amount: 일일 예산 (마이크로 단위, 1원 = 1,000,000 micros)
            status: 초기 상태 (기본값: PAUSED)

        Returns:
            dict: 생성된 캠페인 resource_name 및 id 포함 dict

        Raises:
            GoogleAdsException: API 호출 실패 시
        CampaignBudgetServiceCampaignBudgetOperation_budgetrA   
operationsr   CampaignServiceCampaignOperationT/)rC   resource_namerM   rH   r^   u&   캠페인 생성 완료: id=%s name=%su   캠페인 생성 실패: %sN)r"   r<   get_typecreaterM   rb   enumsBudgetDeliveryMethodEnumSTANDARDdelivery_methodmutate_campaign_budgetsr   resultsrs   CampaignStatusEnumrH   AdvertisingChannelTypeEnumSEARCHr[   ra   network_settingstarget_google_searchtarget_search_networkmutate_campaignssplitr#   r$   r   rO   )r%   rM   rh   rH   budget_servicebudget_operationbudgetbudget_responsebudget_resource_namecampaign_servicecampaign_operationr_   campaign_responsers   re   rT   rU   s                    r)   create_campaignzGoogleAdsClient.create_campaign   s   ,+	!\\556MNN#||445NO%,,F!F'*FK#0F %)\\%7%7%P%P%Y%YF",DD --,- E O $3#:#:1#=#K#K   $||778IJ!%!6!67J!K)00H HM"ll00CCFKHO040B0B0]0]0d0dH-';H$=AH%%:>BH%%; 0 A A --./ !B ! .55a8FFM'--c226K "!. (5F KK@+tTM! 	LL6:	s   G-G0 0	H9HHparamsc                    	 | j                   j                  d      }| j                   j                  d      }|j                  }|j	                  | j
                  |      |_        g }d|v r|d   |_        |j                  d       d|v r<| j                   j                  j                  |d      |_        |j                  d       ddlm} |j                  j                  |j!                  |             |j#                  | j
                  |g      }|j$                  d   j                  }	||	|d	}
t&        j)                  d
||       |
S # t*        $ r}t&        j-                  d||        d}~ww xY w)uC  
        기존 캠페인을 업데이트한다.

        Args:
            campaign_id: 캠페인 ID
            **params: 업데이트할 필드 (name, status 등)

        Returns:
            dict: 업데이트된 resource_name 포함 dict

        Raises:
            GoogleAdsException: API 호출 실패 시
        ro   rp   rM   rH   r   field_mask_pb2pathsrm   rC   rs   updated_fieldsu.   캠페인 업데이트 완료: id=%s fields=%su)   캠페인 업데이트 실패 (id=%s): %sN)r"   r<   rt   updatecampaign_pathr   rs   rM   r`   rv   r|   rH   google.protobufr   update_maskCopyFrom	FieldMaskr   r{   r#   r$   r   rO   )r%   re   r   r   r   r_   field_mask_pathsr   rQ   rs   rT   rU   s               r)   update_campaignzGoogleAdsClient.update_campaign@  sh    	#||778IJ!%!6!67J!K)00H%5%C%CDDUDUWb%cH"! &v ''/6!"&,,"4"4"G"GxHX"Y ''16**33N4L4LSc4L4de'88 --./ 9 H %,,Q/==M!!."2F
 KKH+WghM! 	LLDkSTU	s   E
E 	E3E..E3c                 r   	 | j                   j                  d      }| j                   j                  d      }|j                  | j                  |      }||_        |j                  | j                  |g       t        j                  d|       y# t        $ r}t        j                  d||        d}~ww xY w)u	  
        캠페인을 삭제한다 (상태를 REMOVED로 변경).

        Args:
            campaign_id: 삭제할 캠페인 ID

        Returns:
            bool: 삭제 성공 여부

        Raises:
            GoogleAdsException: API 호출 실패 시
        ro   rp   rm   u(   캠페인 삭제 완료 (REMOVED): id=%sTu#   캠페인 삭제 실패 (id=%s): %sN)r"   r<   rt   r   r   remover   r#   r$   r   rO   )r%   re   r   r   rs   rU   s         r)   delete_campaignzGoogleAdsClient.delete_campaignp  s    	#||778IJ!%!6!67J!K,::4;L;LkZM(5%-- --./ .  KKBKP! 	LL>QO	   BB 	B6B11B6c                 p   d}|rd| }d| d| d}	 | j                         }|j                  | j                  |      }g }|D ]  }|j                  }	|j	                  t        |	j                        |	j                  |	j                  j                  |	j                  j                  |	j                  t        |j                  j                        |j                  j                  d        t        j                  dt        |             |S # t        $ r}
t        j!                  d	|
        d
}
~
ww xY w)u  
        광고그룹 목록을 반환한다.

        Args:
            campaign_id: 지정 시 해당 캠페인의 광고그룹만 반환.
                         None이면 계정 전체 광고그룹 반환.
            limit: 최대 반환 개수 (기본값 25)

        Returns:
            list[dict]: 광고그룹 정보 목록 (id, name, status, campaign_id, cpc_bid_micros 포함)

        Raises:
            GoogleAdsException: API 호출 실패 시
         zWHERE campaign.id = a  
            SELECT
                ad_group.id,
                ad_group.name,
                ad_group.status,
                ad_group.type,
                ad_group.cpc_bid_micros,
                campaign.id,
                campaign.name
            FROM ad_group
            z4
            ORDER BY ad_group.id
            LIMIT rY   r@   )rC   rM   rH   typecpc_bid_microsre   campaign_nameu(   광고그룹 목록 조회 완료: %d건u%   광고그룹 목록 조회 실패: %sN)r=   rI   r   ad_groupr`   rL   rC   rM   rH   type_r   r_   r#   rN   rc   r   rO   )r%   re   rW   where_clauserB   rP   rQ   rT   rS   agrU   s              r)   list_ad_groupszGoogleAdsClient.list_ad_groups  s    & 1+?L
 N ' 		--/J!((T5F5Fe(THF \\!"%%j ""$)).. "*,*;*;'*3<<??';),):):
 LLCS[QM! 	LL@!D	s   C;D 	D5D00D5cpc_bidc                    	 | j                   j                  d      }| j                   j                  d      }| j                   j                  d      }|j                  }||_        | j                   j
                  j                  |   |_        |j                  | j                  |      |_
        | j                   j
                  j                  j                  |_        ||_        |j                  | j                  |g      }	|	j                   d   j"                  }
|
j%                  d      d   }||
||||d}t&        j)                  d	||       |S # t*        $ r}t&        j-                  d
|        d}~ww xY w)u  
        광고그룹을 생성한다.

        Args:
            campaign_id: 상위 캠페인 ID
            name: 광고그룹 이름
            cpc_bid: CPC 입찰가 (마이크로 단위)
            status: 초기 상태 (기본값: PAUSED)

        Returns:
            dict: 생성된 광고그룹 id, resource_name 포함 dict

        Raises:
            GoogleAdsException: API 호출 실패 시
        ro   AdGroupServiceAdGroupOperationrm   r   rq   rr   )rC   rs   rM   rH   re   r   u)   광고그룹 생성 완료: id=%s name=%su   광고그룹 생성 실패: %sN)r"   r<   rt   ru   rM   rv   AdGroupStatusEnumrH   r   r   r_   AdGroupTypeEnumSEARCH_STANDARDr   r   mutate_ad_groupsr{   rs   r   r#   r$   r   rO   )r%   re   rM   r   rH   r   ad_group_servicead_group_operationr   rQ   rs   ad_group_idrT   rU   s                 r)   create_ad_groupzGoogleAdsClient.create_ad_group  s_   ,	#||778IJ#||778HI!%!6!67I!J)00H HM"ll00BB6JHO 0 > >t?P?PR] ^H!\\//??OOHN&-H#'88 --./ 9 H %,,Q/==M'--c226K "!. *")F KKC[RVWM! 	LL91=	s   EE 	E7E22E7r   c                 *   	 | j                   j                  d      }| j                   j                  d      }|j                  }|j	                  | j
                  |      |_        g }d|v r|d   |_        |j                  d       d|v r<| j                   j                  j                  |d      |_        |j                  d       d|v r|d   |_        |j                  d       ddlm} |j                  j!                  |j#                  |             |j%                  | j
                  |g	      }|j&                  d   j                  }	||	|d
}
t(        j+                  d||       |
S # t,        $ r}t(        j/                  d||        d}~ww xY w)uk  
        기존 광고그룹을 업데이트한다.

        Args:
            ad_group_id: 광고그룹 ID
            **params: 업데이트할 필드 (name, status, cpc_bid_micros 등)

        Returns:
            dict: 업데이트된 resource_name 및 수정 필드 포함 dict

        Raises:
            GoogleAdsException: API 호출 실패 시
        r   r   rM   rH   r   r   r   r   rm   r   u1   광고그룹 업데이트 완료: id=%s fields=%su,   광고그룹 업데이트 실패 (id=%s): %sN)r"   r<   rt   r   ad_group_pathr   rs   rM   r`   rv   r   rH   r   r   r   r   r   r   r   r{   r#   r$   r   rO   )r%   r   r   r   r   r   r   r   rQ   rs   rT   rU   s               r)   update_ad_groupzGoogleAdsClient.update_ad_group  s   #	#||778HI!%!6!67I!J)00H%5%C%CDDUDUWb%cH"! &v ''/6!"&,,"4"4"F"FvhGW"X ''16)*01A*B' ''(896**33N4L4LSc4L4de'88 --./ 9 H %,,Q/==M!!."2F
 KKK[ZjkM! 	LLGVWX	s   E)E, ,	F5FFc                 r   	 | j                   j                  d      }| j                   j                  d      }|j                  | j                  |      }||_        |j                  | j                  |g       t        j                  d|       y# t        $ r}t        j                  d||        d}~ww xY w)u  
        광고그룹을 삭제한다 (상태를 REMOVED로 변경).

        Args:
            ad_group_id: 삭제할 광고그룹 ID

        Returns:
            bool: 삭제 성공 여부

        Raises:
            GoogleAdsException: API 호출 실패 시
        r   r   rm   u+   광고그룹 삭제 완료 (REMOVED): id=%sTu&   광고그룹 삭제 실패 (id=%s): %sN)r"   r<   rt   r   r   r   r   r#   r$   r   rO   )r%   r   r   r   rs   rU   s         r)   delete_ad_groupzGoogleAdsClient.delete_ad_group5  s    	#||778HI!%!6!67I!J,::4;L;LkZM(5%-- --./ .  KKE{S! 	LLA;PQR	r   c                 `   d| d| d}	 | j                         }|j                  | j                  |      }g }|D ]  }|j                  }|j	                  t        |j                        |j                  j                  |j                  j                  j                  |j                  j                  |j                  t        |j                  j                        d        t        j!                  dt#        |      |       |S # t$        $ r}	t        j'                  d||	        d}	~	ww xY w)	us  
        광고그룹의 키워드 목록을 반환한다.

        Args:
            ad_group_id: 광고그룹 ID
            limit: 최대 반환 개수 (기본값 25)

        Returns:
            list[dict]: 키워드 정보 목록 (criterion_id, keyword_text, match_type, status 포함)

        Raises:
            GoogleAdsException: API 호출 실패 시
        a  
            SELECT
                ad_group_criterion.criterion_id,
                ad_group_criterion.keyword.text,
                ad_group_criterion.keyword.match_type,
                ad_group_criterion.status,
                ad_group_criterion.cpc_bid_micros,
                ad_group.id
            FROM ad_group_criterion
            WHERE ad_group_criterion.type = KEYWORD
              AND ad_group.id = zH
            ORDER BY ad_group_criterion.criterion_id
            LIMIT rY   r@   )criterion_idkeyword_text
match_typerH   r   r   u6   키워드 목록 조회 완료: %d건 (ad_group_id=%s)u3   키워드 목록 조회 실패 (ad_group_id=%s): %sN)r=   rI   r   ad_group_criterionr`   rL   r   keywordtextr   rM   rH   r   r   rC   r#   rN   rc   r   rO   )
r%   r   rW   rB   rP   rQ   rT   rS   	criterionrU   s
             r)   list_keywordszGoogleAdsClient.list_keywordsV  s"   
! "- .' 		--/J!((T5F5Fe(THF 22	(+I,B,B(C(1(9(9(>(>&/&7&7&B&B&G&G"+"2"2"7"7*3*B*B'*3<<??';	 LLQSVW]S^`klM! 	LLNP[]^_	s   C;D 	D-D((D-keywordsc                    	 | j                   j                  d      }| j                   j                  d      }|j                  | j                  |      }g }|D ]  }| j                   j	                  d      }|j
                  }	||	_        | j                   j                  j                  j                  |	_
        |d   |	j                  _        |j                  dd      }
| j                   j                  j                  |
   |	j                  _        d|v r
|d   |	_        |j#                  |        |j%                  | j                  |      }|j&                  D cg c]  }d	|j(                  i }}t*        j-                  d
t/        |      |       |S c c}w # t0        $ r}t*        j3                  d||        d}~ww xY w)uA  
        광고그룹에 키워드를 추가한다.

        Args:
            ad_group_id: 광고그룹 ID
            keywords: 추가할 키워드 목록. 각 항목은 아래 키를 포함하는 dict:
                      - text (str): 키워드 텍스트
                      - match_type (str): BROAD | PHRASE | EXACT (기본값: BROAD)
                      - cpc_bid_micros (int, optional): CPC 입찰가

        Returns:
            list[dict]: 생성된 키워드 resource_name 목록

        Raises:
            GoogleAdsException: API 호출 실패 시
        r   AdGroupCriterionServiceAdGroupCriterionOperationr   r   BROADr   rm   rs   u/   키워드 추가 완료: %d건 (ad_group_id=%s)u,   키워드 추가 실패 (ad_group_id=%s): %sN)r"   r<   r   r   rt   ru   r   rv   AdGroupCriterionStatusEnumENABLEDrH   r   r   r   KeywordMatchTypeEnumr   r   r`   mutate_ad_group_criteriar{   rs   r#   r$   rc   r   rO   )r%   r   r   r   ad_group_criterion_servicead_group_resourcern   kw	operationr   match_type_strrQ   rrT   rU   s                  r)   add_keywordszGoogleAdsClient.add_keywords  s   *	#||778HI)-)A)AB[)\& 0 > >t?P?PR] ^J 
- LL112MN	%,,	%6	"#'<<#5#5#P#P#X#X	 )+F	!!&!#g!>/3||/A/A/V/VWe/f	!!,#r)/12B/CI,!!),
- 2JJ --% K H CKBRBRSQ8SFSKKI3v;XcdM T " 	LLGVWX	s*   EF F1#F F 	G #F;;G keyword_criterion_idc                    	 | j                   j                  d      }|j                  | j                  ||      }| j                   j	                  d      }|j
                  }||_        | j                   j                  j                  |   |_	        ddl
m} |j                  j                  |j                  dg             |j                  | j                  |g      }	|	j                   d   j                  |||d}
t"        j%                  d	||       |
S # t&        $ r}t"        j)                  d
||        d}~ww xY w)u  
        키워드 상태를 업데이트한다.

        Args:
            keyword_criterion_id: 키워드 criterion ID
            ad_group_id: 키워드가 속한 광고그룹 ID
            status: 변경할 상태 (ENABLED | PAUSED | REMOVED)

        Returns:
            dict: 업데이트된 resource_name 포함 dict

        Raises:
            GoogleAdsException: API 호출 실패 시
        r   r   r   r   rH   r   rm   )rs   r   r   rH   u?   키워드 상태 업데이트 완료: criterion_id=%s status=%su:   키워드 상태 업데이트 실패 (criterion_id=%s): %sN)r"   r<   ad_group_criterion_pathr   rt   r   rs   rv   r   rH   r   r   r   r   r   r   r{   r#   r$   r   rO   )r%   r   r   rH   r   rs   r   r   r   rQ   rT   rU   s               r)   update_keyword_statusz%GoogleAdsClient.update_keyword_status  s;   (%	)-)A)AB[)\&6NN!!;0DM --.IJI!((I&3I##||11LLVTI6!!**>+C+C8*+C+UV1JJ --%; K H
 "*!1!1!!4!B!B 4* 	F KKQ$
 M! 	LLL$
 	s   DD 	D= D88D=	entity_identity_type
date_rangemetricssinceuntilc                 j   |t         }dj                  |      }|j                         }|dk(  rd}	d}
d}n'|dk(  rd}	d}
d}n|d	k(  rd
}	d}
d}nt        d| d      |r|r
d| d| d}nd| }d| d| d|	 d|
 d| d| d}	 | j	                         }|j                  | j                  |      }g }|D ]  }|||j                  j                  |j                  j                  |j                  j                  |j                  j                  |j                  j                  |j                  j                  |j                  j                  d	}|j!                  |        t"        j%                  dt'        |      |||||       |S # t(        $ r}t"        j+                  d|||        d}~ww xY w)u  
        광고 성과 리포트를 반환한다.

        Args:
            entity_id: 조회할 캠페인/광고그룹/키워드 ID
            entity_type: 'campaign', 'ad_group', 'keyword' 중 하나 (기본값: 'campaign')
            date_range: GAQL 날짜 범위 프리셋 (기본값: LAST_7_DAYS).
                        LAST_7_DAYS, LAST_30_DAYS, THIS_MONTH, LAST_MONTH 등 지원.
            metrics: 조회할 metrics 필드 목록. None이면 기본 metrics 사용.

        Returns:
            list[dict]: 성과 리포트 레코드 목록

        Raises:
            ValueError: 지원하지 않는 entity_type인 경우
            GoogleAdsException: API 호출 실패 시
        Nz, r_   zcampaign.idz)campaign.id, campaign.name, segments.dater   zad_group.idz6ad_group.id, ad_group.name, campaign.id, segments.dater   keyword_viewzad_group_criterion.criterion_idziad_group_criterion.criterion_id, ad_group_criterion.keyword.text, ad_group.id, campaign.id, segments.dateu!   지원하지 않는 entity_type: u?   . 'campaign', 'ad_group', 'keyword' 중 하나여야 합니다.zsegments.date BETWEEN 'z' AND ''zsegments.date DURING z$
            SELECT
                z,
                z
            FROM z
            WHERE z = z
              AND z1
            ORDER BY segments.date DESC
        r@   )	r   r   dateimpressionsclickscost_microsctraverage_cpcconversionsu\   인사이트 조회 완료: %d건 (entity_type=%s, id=%s, date_range=%s, since=%s, until=%s)u6   인사이트 조회 실패 (entity_type=%s, id=%s): %s)_DEFAULT_INSIGHT_METRICSjoinlowerr   r=   rI   r   segmentsr   r   r   r   r   r   r   r   r`   r#   rN   rc   r   rO   )r%   r   r   r   r   r   r   metrics_clauseentity_lowerresourcewhere_fieldselect_fieldsdate_conditionrB   rP   rQ   rT   rS   recordrU   s                       r)   get_insightszGoogleAdsClient.get_insights  s   4 ?.G7+"((*:%!H'KGMZ'!H'KTMY&%H;K:  3K=  AD  D 
 U6ugWUG1MN4ZLAN    ! -s9+ .!" #	"	--/J!((T5F5Fe(THF &!*#.LL--#&;;#:#:!kk00#&;;#:#:;;??#&;;#:#:#&;;#:#:
* f%& LLnF M! 	LLH	 	s   DF 	F2F--F2	headlinesdescriptions	final_urlc                    dt        |      cxk  rdk  sn t        dt        |       d      dt        |      cxk  rdk  sn t        dt        |       d      	 | j                  j                  d      }| j                  j                  d	      }| j                  j	                  d
      }|j
                  }| j                  j                  j                  j                  |_	        |j                  | j                  |      |_        |j                  }	|	j                  j                  |       |	j                   }
|D ]?  }| j                  j	                  d      }||_        |
j$                  j                  |       A |D ]?  }| j                  j	                  d      }||_        |
j&                  j                  |       A |j)                  | j                  |g      }|j*                  d   j,                  }|||t        |      t        |      d}t.        j1                  d||       |S # t2        $ r}t.        j5                  d||        d}~ww xY w)uA  
        반응형 검색 광고(RSA)를 생성한다.

        Args:
            ad_group_id: 광고그룹 ID
            headlines: 헤드라인 목록 (최소 3개, 최대 15개)
            descriptions: 설명 목록 (최소 2개, 최대 4개)
            final_url: 광고 클릭 시 연결할 최종 URL

        Returns:
            dict: 생성된 광고 resource_name, ad_group_id 포함 dict

        Raises:
            ValueError: 헤드라인 또는 설명 개수가 유효 범위를 벗어난 경우
            GoogleAdsException: API 호출 실패 시
              u2   헤드라인은 3~15개여야 합니다. (현재: u   개)      u+   설명은 2~4개여야 합니다. (현재: r   AdGroupAdServiceAdGroupAdOperationAdTextAssetrm   r   )rs   r   r   headline_countdescription_countuF   반응형 검색 광고 생성 완료: ad_group_id=%s resource_name=%su:   반응형 검색 광고 생성 실패 (ad_group_id=%s): %sN)rc   r   r"   r<   rt   ru   rv   AdGroupAdStatusEnumPAUSEDrH   r   r   r   ad
final_urlsr`   responsive_search_adr   r   r   mutate_ad_group_adsr{   rs   r#   r$   r   rO   )r%   r   r   r   r   r   ad_group_ad_servicer   ad_group_adr  rsar   assetrQ   rs   rT   rU   s                    r)   create_responsive_search_adz+GoogleAdsClient.create_responsive_search_ade  s2   . S^)r)QRUV_R`QaaefggS&+!+J3|K\J]]abcc,	#||778HI"&,,":":;M"N--.BCI#**K!%!3!3!G!G!N!NK#3#A#A$BSBSU`#aK  BMM  +))C! ,--m<!
$$U+, % /--m<!
  ''./
 +>> --%; ? H %,,Q/==M "/*&"%i.%(%6F KKX
 M! 	LLUWbdef	s   GH0 0	I9II)   )r  )Nr  )r_   LAST_7_DAYSNNN)__name__
__module____qualname____doc___ENV_KEYS_PATHrL   r*   staticmethodr   r7   r9   r=   rV   intrJ   rd   rg   r   r   boolr   r   r   r   r   r   r   r   r   r   r>   r)   r   r   #   s   (,: (\c (\t (\\ 	c 	d 	 	<+$ +b0C 0d 0d0 0 0l 	AA A 	A
 
AF.3 .# .$ .`3 4 D #':4Z: : 
d	:B 44 4 	4
 4 
4l13 1# 1$ 1f3 4 B0 0S 0$t* 0d00 t*0 
d	0d9!9 9 	9
 
9D &'$(  ff f 	f
 cT!f Tzf Tzf 
dfXHH 9H 3i	H
 H 
Hr>   r   )r  r   typingr   google.ads.googleads.clientr   r    google.ads.googleads.errorsr   utils.env_loaderr   utils.loggerr   r  r#   r  r   r  r>   r)   <module>r     sA    
  K : * #	H	0 J Jr>   