
    \w5i'                        d 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 e G d d	             Z G d
 d      Z G d d      Zy)z*Google Sheets client for reading KPI data.    )annotations)	dataclass)Path)Any)Credentials)buildc                  L    e Zd ZU dZded<   ded<   ded<   dddZedd	       Zy
)	SheetDatazContainer for sheet data.z	list[str]headerszlist[dict[str, Any]]rowszlist[list[Any]]rawc                    i }| j                   D ]]  }|j                  |      }|si }|j                         D ]$  \  }}||k(  r| j                  |      }| |||<   & ||t	        |      <   _ |S )zConvert to period-based dictionary.

        Returns:
            Dictionary mapping period to column values.
            Example: {"2024-01": {"leads": 1000, "revenue": 50000}}
        )r   getitems_parse_numberstr)	selfperiod_columnresultrowperiodvalueskeyvalueparseds	            f/var/www/tkim.planitai.co.jp/gemegg/20251207-make-pdf-report/project/planitai-kpi/src/sheets/client.pyto_period_dictzSheetData.to_period_dict   s     /199 	)CWW]+F')F!iik )
U-'++E2%"(F3K) #)F3v;	)     c                    | | dk(  ry	 t        |       j                  dd      j                  dd      j                         }t        |      S # t        t
        f$ r Y yw xY w)zParse value to number.N ,%)r   replacestripfloat
ValueError	TypeError)r   cleans     r   r   zSheetData._parse_number/   sd     =ERK	J&&sB/77R@FFHE<I& 		s   AA A A N)r   )r   r   returndict[str, dict[str, float]])r   r   r)   float | None)__name__
__module____qualname____doc____annotations__r   staticmethodr    r   r   r
   r
      s0    #
	4 
 
r   r
   c                      e Zd ZdZdgZd
dZedd       ZddZ	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ		 	 	 	 	 	 	 	 ddZ
edd	       Zy)GoogleSheetsClientzGoogle Sheets API client.z5https://www.googleapis.com/auth/spreadsheets.readonlyc                2    t        |      | _        d| _        y)zInitialize with service account credentials.

        Args:
            credentials_path: Path to service account JSON file.
        N)r   credentials_path_service)r   r6   s     r   __init__zGoogleSheetsClient.__init__B   s     !%%5 6r   c                    | j                   Gt        j                  t        | j                        | j
                        }t        dd|      | _         | j                   S )zLazy-load the Sheets service.)scopessheetsv4)credentials)r7   r   from_service_account_filer   r6   SCOPESr   )r   credss     r   servicezGoogleSheetsClient.serviceK   sN     == 99D))*4;;E "(DeDDM}}r   c                   | j                   j                         j                         j                  ||      j	                         }|j                  dg       }|st        g g g       S |d   D cg c]  }t        |      j                          }}g }|dd D ]D  }i }	t        |      D ]!  \  }
}|
t        |      k  r	||
   |	|<   d|	|<   # |j                  |	       F t        |||      S c c}w )zRead data from a sheet range.

        Args:
            sheet_id: Google Sheets ID.
            range_name: Range in A1 notation (e.g., "Sheet1!A1:Z100").

        Returns:
            SheetData containing headers, rows, and raw data.
        )spreadsheetIdranger   )r   r   r   r      N)rA   spreadsheetsr   r   executer
   r   r$   	enumeratelenappend)r   sheet_id
range_namer   r   hr   r   
row_valuesrow_dictiheaders               r   
read_rangezGoogleSheetsClient.read_rangeU   s     LL%%'VXSxzS:WY	 	 Hb)Rbb99+1!95a3q6<<>55%' * 	"J')H&w/ ,	6s:&'1!}HV$'+HV$	,
 KK!	" t@@ 6s   1 C/Nc                    |r| j                  |||xs i       S | j                  ||      }|j                  |      }i }	|r#| j                  ||      }
|
j                  |      }	||	fS )a  Get KPI actual and target data.

        Args:
            sheet_id: Google Sheets ID.
            data_range: Range for actual data.
            target_range: Optional range for target data.
            period_column: Column name for period.
            pivot_format: If True, parse as pivot table format.
            pivot_config: Configuration for pivot format parsing.

        Returns:
            Tuple of (actual_data, target_data) as period-based dictionaries.
        )get_kpi_data_pivotrR   r   )r   rK   
data_rangetarget_ranger   pivot_formatpivot_configactual_sheetactual_datatarget_datatarget_sheets              r   get_kpi_datazGoogleSheetsClient.get_kpi_datay   sy    , **8ZASQSTT x<"11-@ 46??8\BL&55mDKK''r   c                   | j                  ||      }|j                  }|si i fS |j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }	|j                  d	d      }
|j                  d
d      }|j                  dd      }|j                  dd      }|j                  di       }g }g }t        |      |kD  r||   }|
}|t        |      k  r}|t        |      k  r||   nd}|rRt	        |      j                         r9|j                  t	        |      j                                |j                  |       ||z  }|t        |      k  r}|si i fS |D ci c]  }|i  }}|D ci c]  }|i  }}t        |t        |            D ]  }||   }t        |      |	k  r||	   rt	        ||	         j                         nd}|s=|j                  ||      }t        t        ||            D ]K  \  }\  }}||z   }||z   }| j                  ||      }| j                  ||      } ||||   |<   | D| ||   |<   M  ||fS c c}w c c}w )u  Parse pivot table format sheet.

        Expected format:
        - Row with periods (e.g., 2025-04, 2025-05, ...)
        - Row with headers (e.g., 予算, 実績, 差異, 達成率) repeated for each period
        - Data rows with item names and values

        Args:
            sheet_id: Google Sheets ID.
            data_range: Range to read.
            config: Pivot configuration with:
                - period_row: Row index for periods (0-based), default 3
                - header_row: Row index for headers (0-based), default 4
                - data_start_row: Row index where data starts, default 5
                - item_col: Column index for item names, default 1
                - data_start_col: Column index where data starts, default 4
                - cols_per_period: Number of columns per period, default 4
                - budget_col_offset: Offset for budget column within period, default 0
                - actual_col_offset: Offset for actual column within period, default 1
                - item_mapping: Mapping of sheet item names to KPI IDs

        Returns:
            Tuple of (actual_data, target_data) as period-based dictionaries.
        
period_row   
header_row   data_start_row   item_colrE   data_start_colcols_per_periodbudget_col_offsetr   actual_col_offsetitem_mappingr    )rR   r   r   rI   r   r$   rJ   rD   rH   zip_parse_cell_value)!r   rK   rU   config
sheet_datar   r_   ra   rc   re   rf   rg   rh   ri   rj   periodsperiod_colsperiod_row_datacol
period_valprZ   r[   row_idxr   	item_namekpi_idrP   r   
budget_col
actual_col
budget_val
actual_vals!                                    r   rT   z%GoogleSheetsClient.get_kpi_data_pivot   s   < __Xz:
nnr6M ZZa0
ZZa0
$4a8::j!,$4a8 **%6:"JJ':A>"JJ':A>zz."5  !#s8j !*oO CO,,583;O5O_S1UW
#j/"7"7"9NN3z?#8#8#:;&&s+& O,, r6M DK3KaArE3K3KCJ3KaArE3K3K ^SX6 	=Gg,C3x8#69(mCM*002I "%%i;F %.c';.G$H 
= =FC #44
 #44
!33CD
!33CD
)2<K'/)2<K'/
=	=2 K''; 4L3Ks   5
I
I#c                    |t        |       k\  ry| |   }||dk(  ry	 t        |      j                  dd      j                  dd      j                         }t	        |      S # t
        t        f$ r Y yw xY w)zParse a cell value to float.Nr    r!   r"   )rI   r   r#   r$   r%   r&   r'   )r   rr   r   r(   s       r   rl   z$GoogleSheetsClient._parse_cell_value   s{     #c(?C=ERK	J&&sB/77R@FFHE<I& 		s   AA" "A43A4)r6   z
str | Path)r)   r   )rK   r   rL   r   r)   r
   )Nr   FN)rK   r   rU   r   rV   
str | Noner   r   rW   boolrX   zdict | Noner)   ?tuple[dict[str, dict[str, float]], dict[str, dict[str, float]]])rK   r   rU   r   rm   dictr)   r   )r   listrr   intr)   r+   )r,   r-   r.   r/   r?   r8   propertyrA   rR   r]   rT   r1   rl   r2   r   r   r4   r4   =   s    #EFF  "AP $(%"$(#(#( #( !	#(
 #( #( "#( 
I#(J^(^( ^( 	^(
 
I^(@  r   r4   c                  J    e Zd ZdZdddZd	dZd
dZ	 	 d	 	 	 	 	 	 	 	 	 ddZy)MockSheetsClientz+Mock client for testing without Google API.Nc                (    |xs i | _         i | _        y)z#Initialize with optional mock data.Ndatatargetsr   r   s     r   r8   zMockSheetsClient.__init__  s    JB	46r   c                    || _         y)zSet mock actual data.N)r   r   s     r   set_datazMockSheetsClient.set_data  s	    	r   c                    || _         y)zSet mock target data.N)r   )r   r   s     r   set_targetszMockSheetsClient.set_targets  s	    r   c                2    | j                   | j                  fS )zReturn mock data.r   )r   rK   rU   rV   r   s        r   r]   zMockSheetsClient.get_kpi_data  s     yy$,,&&r   )N)r   z"dict[str, dict[str, float]] | None)r   r*   r)   None)r   r*   r)   r   )Nr   )
rK   r   rU   r   rV   r}   r   r   r)   r   )r,   r-   r.   r/   r8   r   r   r]   r2   r   r   r   r     sR    57
 $(%'' ' !	'
 ' 
I'r   r   N)r/   
__future__r   dataclassesr   pathlibr   typingr   google.oauth2.service_accountr   googleapiclient.discoveryr   r
   r4   r   r2   r   r   <module>r      sK    0 " !   5 + , , ,^O Od' 'r   