현재 우리 팀에서는 구글 캘린더로 다음 이미지 처럼 휴가를 사용했을 경우 공유하고 있다.
또한, 작업 후 Pull Request하여 코드 리뷰를 하고 있다.
그래서 이 코드 리뷰를 할 리뷰어들을 2명씩 지정하는 스크립트를 간단히 만들어서 사용중이다.
import sys
import random
project_list = ["1", "2", "3"]
all_name_list = ["리뷰어1", "리뷰어2", "리뷰어3", "리뷰어4", "리뷰어5", "리뷰어6", "리뷰어7"]
input_project = input("""
Pr프로젝트 입력하세요 :
- 프로젝트 1 : 1
- 프로젝트 2 : 2
- 프로젝트 3 : 3
""")
if input_project not in project_list:
sys.exit("잘못된 프로젝트 입력 입니다.")
input_name = input(f"""
이름을 입력하세요 :
- 이름 리스트 : "리뷰어1", "리뷰어2", "리뷰어3", "리뷰어4", "리뷰어5", "리뷰어6", "리뷰어7"
""")
if input_name not in all_name_list:
sys.exit("잘못된 이름 입력 입니다.")
first_approve_list = []
second_approve_list = []
if input_project == "1":
first_approve_list = ["리뷰어1", "리뷰어2", "리뷰어3"]
elif input_project == "2":
first_approve_list = ["리뷰어1", "리뷰어4"]
else:
first_approve_list = ["리뷰어1", "리뷰어3"]
second_approve_list = list(set(all_name_list) - set(first_approve_list))
try:
first_approve_list.remove(input_name)
except ValueError:
pass
try:
second_approve_list.remove(input_name)
except ValueError:
pass
print("첫번째 승인자 : {}".format(random.choice(first_approve_list)))
print("두번째 승인자 : {}".format(random.choice(second_approve_list)))
어느 프로젝트를 리뷰받을것인지 정하고 해당 프로젝트에는 고정적으로 리뷰되는 사람들이 first_approve_list
에 지정되어있는 상황이다.
여기에 본인 + 고정리뷰어 제외하고 나머지 사람들 중 랜덤으로 지정하여 총 2명의 리뷰어를 뽑게된다.
하지만 현재는 누군가 휴가일 때 해당 인원을 제외하는 로직이 없다.
그래서 구글캘린더에 있는 정보를 이용하여 당일 휴가인 사람들을 제외하고 리뷰어들을 지정하게끔 만들어본다.
구글 클라우드 콘솔
해당 콘솔에서 라이브러리에 들어간다.
라이브러리에서 Calendar를 검색해준다.
Google Calendar API 를 선택해서 사용설정을 해준다.
사용자 인증 정보 탭에 들어가서 API키를 얻는다.
API 키가 생성완료되었고, 해당 키로 캘린더API만 사용가능하게 제한 사항등을 추가하는 것도 가능하다.
OAuth 클라이언트 ID로 들어가서 클라이언트 ID도 생성한다.
이미지 맨 오른쪽에서 해당 파일을 json형식으로 다운로드 받을 수 있다.
OAuth 동의 화면 또한 절차에 따라서 생성해준다.
캘린더 ID는 캘린더의 설정 및 공유에 들어가서 얻을 수 있다.
위 사이트에서 구글 캘린더 API에 접근하는 쉽고 빠른 방법이 자세히 설명되어있다.
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
토큰 생성 샘플 스크립트
샘플 스크립트
이 스크립트를 실행할 때 위에서 생성한 클라이언트 ID가 필요하다.
같은 폴더에 credentials.json 이라는 이름으로 위치시켜 준다.
위 스크립트를 실행하면 같은 폴더에 token.json이라는 파일이 생성되고 이 파일안에는 API 요청 시
필요한 token이 포함되어있다.
list 샘플 테스트 링크에 들어가면 이미지처럼 바로 테스트도 가능하게 되어있다.
위를 이용해서 포스트맨으로 API요청을 해본 결과이다. summary에 있는 값을 활용하면 될 것으로 보인다.
def get_vacationer_list():
url = f"https://www.googleapis.com/calendar/v3/calendars/{config.calendar_id}/events"
now = datetime.datetime.now(datetime.timezone.utc)
now2 = now + datetime.timedelta(hours=1)
time_min = now.isoformat()
time_max = now2.isoformat()
params = {"key": config.google_api_key, "timeMax": time_max, "timeMin": time_min}
response = requests.get(url, params=params, headers={"Authorization": f"Bearer {config.token}"})
response = response.json()
vacationer_list = []
if response.get("items"):
for item in response["items"]:
vacationer_list.append(item.get("summary"))
return vacationer_list
오늘 날짜 기준으로 휴가인 사람들 목록을 조회해온다.
만약 오늘 휴가자가 리뷰어1, 리뷰어4 연차
+ 리뷰어2 오후반차
라고 한다면
vacationer_list
는 ['리뷰어1, 리뷰어4 연차', '리뷰어2 오후반차']
의 결과 값을 얻을 수 있다.
def convert_reviewer(vacationer_list):
status_list = ["연차", "오후반차", "오전반차", "오전반반차", "오후반반차"]
for i, vacationer in enumerate(vacationer_list):
for vacation in status_list:
if vacationer.find(vacation) != -1:
vacationer_list[i] = vacationer.replace(f" {vacation}", "").split(",")
reviewer_list = list(itertools.chain(*vacationer_list))
return [review.strip() for review in reviewer_list]
해당 summary(캘린더에 등록한 일정내용)에 휴가 상태(연차, 오후반차 등)가 포함되어 있는지 확인을 한다.
일정이 휴가에 대한 일정이 맞다면 앞에 있는 내용들을 휴가자들로 간주하고 리스트에 담는다.
그렇게되면 결과 값이 [['리뷰어1', ' 리뷰어4'], ['리뷰어2']]
이런 형태로 반환된다.
그 다음 리스트들의 원소를 합치는 list(itertools.chain(*vacationer_list))
를 이용하여 하나로 합치고
' 리뷰어4'같은 경우처럼 앞에 공백이 들어가는 것을 방지하기 위해 마지막에 한번 더 strip()으로 공백을 제거해준다.
import itertools
import sys
import random
import requests
import datetime
import config
def get_vacationer_list():
url = f"https://www.googleapis.com/calendar/v3/calendars/{config.calendar_id}/events"
now = datetime.datetime.now(datetime.timezone.utc)
now2 = now + datetime.timedelta(hours=1)
time_min = now.isoformat()
time_max = now2.isoformat()
params = {"key": config.google_api_key, "timeMax": time_max, "timeMin": time_min}
response = requests.get(url, params=params, headers={"Authorization": f"Bearer {config.token}"})
response = response.json()
vacationer_list = []
if response.get("items"):
for item in response["items"]:
vacationer_list.append(item.get("summary"))
return vacationer_list
def convert_reviewer(vacationer_list):
status_list = ["연차", "오후반차", "오전반차", "오전반반차", "오후반반차"]
for i, vacationer in enumerate(vacationer_list):
for vacation in status_list:
if vacationer.find(vacation) != -1:
vacationer_list[i] = vacationer.replace(f" {vacation}", "").split(",")
reviewer_list = list(itertools.chain(*vacationer_list))
return [review.strip() for review in reviewer_list]
vacationer_list = get_vacationer_list()
reviewer_list = convert_reviewer(vacationer_list)
project_list = ["1", "2", "3"]
all_name_list = ["리뷰어1", "리뷰어2", "리뷰어3", "리뷰어4", "리뷰어5", "리뷰어6", "리뷰어7"]
input_project = input("""
Pr프로젝트 입력하세요 :
- 프로젝트 1 : 1
- 프로젝트 2 : 2
- 프로젝트 3 : 3
""")
if input_project not in project_list:
sys.exit("잘못된 프로젝트 입력 입니다.")
input_name = input(f"""
이름을 입력하세요 :
- 이름 리스트 : "리뷰어1", "리뷰어2", "리뷰어3", "리뷰어4", "리뷰어5", "리뷰어6", "리뷰어7"
""")
if input_name not in all_name_list:
sys.exit("잘못된 이름 입력 입니다.")
first_approve_list = []
second_approve_list = []
if input_project == "1":
first_approve_list = ["리뷰어1", "리뷰어2", "리뷰어3"]
elif input_project == "2":
first_approve_list = ["리뷰어1", "리뷰어4"]
else:
first_approve_list = ["리뷰어1", "리뷰어3"]
all_name_list = list(set(all_name_list) - set(reviewer_list))
first_approve_list = list(set(first_approve_list) - set(reviewer_list))
second_approve_list = list(set(all_name_list) - set(first_approve_list))
try:
first_approve_list.remove(input_name)
except ValueError:
pass
try:
second_approve_list.remove(input_name)
except ValueError:
pass
first_reviewer = None
second_reviewer = None
if first_approve_list:
first_reviewer = random.choice(first_approve_list)
second_reviewer = random.choice(second_approve_list)
else:
first_reviewer = random.choice(second_approve_list)
second_approve_list.remove(first_reviewer)
second_reviewer = random.choice(second_approve_list)
print(f"첫번째 승인자 : {first_reviewer}")
print(f"두번째 승인자 : {second_reviewer}")
처음엔 페이지를 크롤링해서 데이터를 얻으면 되지않을까 생각하고 검색해봤는데 마침 OpenAPI가 있어서 이용해보게 되었다.
원래 3시간이면 후다닥 끝나는 줄 알았으나...
현실은 이것저것 걸림돌이 꽤 있었다.
위 코드도 아직 모자란게 산더미이다.
1시간주기로 구글에서 제공해주는 token이 만료가되는데 이걸 계속해서 갱신해야하는 점도 아직 미해결로 남았고
반차나 반반차의 경우에는 시간을 지정하여 해당 시간만 제외하고는 리뷰어로 남을 수 있게 하는 기능도 아직이다.
삽질의 시간에 비해서 가성비는 좋지않다.
구글 콘솔에서부터 개인용도이외에 사용하기엔 무리(보안상 + 번거로움)라는 건 눈치챘으나 이왕 시작해서 끝까지 코딩은 해봤다.
구글 API를 이용하기 위해서는 보안관련해서 체크하고 넘어가는 것이 너무많고
단순 실행 스크립트인데 연동때문에 설치해야되는 패키지도 있는 점이 단점이다.