Kimuksung
Kimuksung 주니어 Data Enginner입니다.

Tableau 자동화하기 with Python

Tableau 자동화하기 with Python

안녕하세요 오늘은 태블로 대시보드 슬랙 자동화하기를 해보려고 합니다.

금일은 대시보드 구성이 되어있다고 가정하고, 파이썬으로 Tableau Cloud 연동 및 이미지 추출하는 부분까지 진행해보았습니다.

결론만 말하면, 파이썬으로 원하는 View의 이미지를 추출은 하였으나 데이터베이스가 연동되어있는 View는 상황에 따라 제대로 못 불러오는 현상이 있습니다. -> view의 project id 가 제대로 맵핑 되지 않는 것을 확인하였습니다.

이문제는 차후에 해결하고 지금까지의 진행 과정에 대해 서술 합니다.

Python과 Tableau를 연동하기 위해 TSC 라이브러리를 사용했습니다.

1. Tableau Token 발급

  • Python에서 연동시키기 위하여 현재 사용하고 있는 로그인 방식은 Api로 지원하지 않는다하여 Token을 사용하였습니다.

Untitled-70

Untitled-71

2. 대시보드 원본 저장 설정

  • 대시보드 접속 시 클라우드 환경에서는 지속적으로 데이터베이스를 로그인하라고 팝업창이 뜹니다.
  • Python과 연동할 때 에러가 발생하기에, 없애주기 위하여 아래와 같이 설정합니다.

Untitled-72 Untitled-73 2023-10-20-3-05-59

3. 사용자 언어 한국어 설정

  • 이미지 추출 후 확인을 하여 보면 한글이 깨져 있는 것을 볼 수 있습니다.
  • 언어가 설정되지 않고, 해당 대시보드의 한글로 설정된 사용자로 변경해주어야 합니다.

Untitled-75 Untitled-76

4. 오류를 잡기 위한 캐싱 해제

  • 위 과정을 해결하기 위해 여러 방법을 변경해보고 시도해보았으나, 계속하여 변화없는 이미지가 출력되었습니다.
  • 처음에 파일을 잘못 부른것으로 착각하였으나, 캐싱되어서 과거의 이미지를 계속 호출한다고 합니다.
  • 아래와 같이 캐싱을 풀어주어서 처리합니다.
1
2
3
4
5
image_req_option = TSC.ImageRequestOptions(
		imageresolution=TSC.ImageRequestOptions.Resolution.High, maxage=1
	)

server.views.populate_image(view_item, req_options=image_req_option)
5. Python 구성

  1. ini 파일 구성

    1
    2
    3
    4
    5
    6
    
     # config.ini
     [Tableau]
     server_url = https://url.tableau.com/
     token_name = token_id
     token_value = token_pw
     site_id = tableau url   /  
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
     from configparser import ConfigParser
     import tableauserverclient as TSC
        
     def readconfig(section, key):
     	config = ConfigParser()
     	config.read("config.ini")
     	return config.get(section, key)
        
     def readtableau():
     	tableau_infos = ["server_url", "token_name", "token_value", "site_id"]
     	return tuple(map(lambda x: readconfig("Tableau", x), tableau_infos))
    
  2. 태블로 정보 추출

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
     def gettableau(server):
     	with server.auth.sign_in(tableau_auth) as a:
     		projects, pagination = server.projects.get()
     		project_metadata = dict()
     		for project in projects:
     			project_metadata[project.name] = project.id
        
     		view_metadata = dict()
     		for view in TSC.Pager(server.views):
     			view_metadata[view.name] = (view.project_id, view.id)
        
     	return project_metadata, view_metadata
    
  3. 이미지 추출

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
     if __name__ == "__main__":
     	server_url, token_name, token_value, site_id = readtableau()
        
     	tableau_auth = TSC.PersonalAccessTokenAuth(token_name, token_value, site_id=site_id)
     	server = TSC.Server(server_url, use_server_version=True)
     	server.auth.sign_in(tableau_auth)
     	project_metadata, view_metadata = gettableau(server)
        
     	image_req_option = TSC.ImageRequestOptions(
     		imageresolution=TSC.ImageRequestOptions.Resolution.High, maxage=1
     	)
        
     	with server.auth.sign_in(tableau_auth) as a:
     		req_views = ['view_name', .. ]
     		req_projects = ['project_name']*len(req_views)
        
     		for i, (view_name, project_name) in enumerate(zip(req_views, req_projects)):
     			try:
     				if view_name in view_metadata:
     					print(f'start {i} dashboard')
     					project_id, view_id = view_metadata[view_name]
     					if project_id == project_metadata[project_name]:
     						view_item = server.views.get_by_id(view_id)
     						server.views.populate_image(view_item, req_options=image_req_option)
     						with open(f'./view_image{i}.png', 'wb') as f:
     							f.write(view_item.image)
     					print(f'end {i} dashboard')
     			except Exception as e:
     				print(f'{view_name} Error / {e}')
        
     	server.auth.sign_out()
    
6. 지속적인 오류 발생

  • 하나의 Project 대시보드에 6개의 View를 구성하였습니다.
  • 위와 같은 방법으로 모든 View를 접속할 때에도 별도의 로그인을 하지 않습니다.
  • Python으로 호출 시, 특정 이미지는 계속하여 불러와지고 나머지는 에러가 발생합니다.
  • 디버깅으로 오류를 찾아가고 있는데 현재 tableau의 project의 id와 name에 존재하는 값과 view의 projectid가 비매칭되는 현상이 나타나는중입니다.
1
2
400074: 잘못된 요청
		 7a0dd161-9fe9-493c-87d9-63beda57f614 이미지를 쿼리하는 동안 문제가 발생했습니다.
1
2
3
4
5
projects, pagination = server.projects.get()
[(project.id, project.name) for project in projects]

all_views, pagination = server.views.get()
[(view.name, view.id, view.project_id) for view in all_views]