출퇴근 정보 추출
Google Map을 활용하여 위치정보 추출하는 Python코드를 만드려 한다.
Google Map 타임 라인 정보는 안드로이드 또는 아이폰이나 상관 없이 자신의 위치 정보를 실시간으로 남길 수 있다.
위치정보를 기록하기 위한 설정으로 Google Map의 설정에서 위치정보를 항상으로 설정하여야 한다.
처리 순서
Google Map기반 출퇴근 정보 처리의 데이터 처리 순서는 다음과 같다.
- Google Map 정보 다운로드 : Google Takeout을 통한 위치 정보 download
- Google 계정 > 데이터 및 개인 정보 보호 > 데이터 다운로드 또는 삭제 > 데이터 다운로드 > 위치 기록(체크) , 다음 단계 진
- 한 번만 내보내기, 내보내기 생성
- takeout-YYYYMMDDTHHmmSSZ-001.zip 형식의 파일 다운로드
- 근무지 Zone 확인 : Google Map 의 위도 / 경도 정보를 통하여 위치 정보가 기록 되어 있으므로 이동 위치 정보가 근무지 Zone에 들어갔는지 나왔는지 확인 하기 위해 필요하다.
- 근무지가 지사나 이사를 통해 여러 군데 위치할 수 있다.
- 출근 일시 : 원하는 시작일자부터 종료 일자까지 근무지 Zone 에 들어간 날자가 바로 출근 증빙 자료가 된다.
- 휴일 또는 휴가 일정 확인 : 휴일이나 휴가 시에는 근무지 Zone에 들어가지 않는다.
구글 정보 Load
아래코드는 Google 위치 정보를 로드하는 코드이다.
g_pos = None with open('./Records.json', 'r') as fp: g_pos = json.load(fp)
휴일, 휴가, 재택, 증빙
근무지 Zone에 있지 않지만 출근으로 확인해야 하는 다른 상황에 대해 처리하는 로직은 다음과 같다.
- update_date_vacances :
date_vacances={ '2017-10-02~2017-10-06':'^추석', '2018-02-15~2018-02-16': '^설', '2018-03-01':'^삼일절', '2018-05-22':'^부처님오신날', '2018-09-24~2018-09-26': '^추석', '2018-02-27~2017-03-06':'출장(계약)', '2018-05-15' :'출근증빙사진(https://photos.google.com/photo/)', '2018-05-16' :'출근증빙사진(https://photos.google.com/photo/)', '2018-06-20~2018-06-24':'출장,---', '2018-07-13~2018-07-18':'휴가', '2018-09-24~2018-09-26':'^추석', '2018-10-30~2018-11-05':'출장,---', '2019-02-04~2019-02-06': '^설', '2019-09-12~2019-09-13':'^추석', '2019-12-25' : '^크리스마스', '2020-03-12':'출장,대전', '2020-03-16':'휴가', '2020-03-18':'출근증빙사진(https://photos.google.com/photo/)', '2020-03-19':'출근증빙사진(https://photos.google.com/photo/)', '2020-03-20':'휴가', '2020-03-24':'출근증빙사진(https://photos.google.com/photo/)', '2020-04-06':'출근증빙사진(https://photos.google.com/photo/)', '2020-04-07':'출근증빙사진(https://photos.google.com/photo/)', '2020-04-09':'출근증빙사진(https://photos.google.com/photo/)', '2020-06-05':'출근증빙사진(https://photos.google.com/photo/)', '2020-08-18~2020-08-21':'재택', '2020-08-24~2022-02-11':'재택', '2022-04-11~2022-04-18':'재택', '2022-07-18~2022-07-29':'휴가', } def update_date_vacances(): for key_vac in date_vacances.keys(): vac_value = date_vacances[key_vac] bHoly= True if vac_value.find('^')>=0 else False if bHoly == True: vac_value = vac_value[1:] if key_vac.find('~') >= 0: vac_dates = key_vac.split('~') date_start=datetime.datetime.strptime(vac_dates[0], '%Y-%m-%d') date_end=datetime.datetime.strptime(vac_dates[1], '%Y-%m-%d') temp_date = date_start while temp_date <= date_end: i_wday = temp_date.weekday() s_temp_date = temp_date.strftime('%Y-%m-%d') if i_wday < 5: update_date(s_temp_date, 'comment', vac_value) if bHoly == True: update_date(s_temp_date, 'is_wday', False) temp_date = temp_date + datetime.timedelta(days=1) else: update_date(key_vac, 'comment', vac_value) if bHoly == True: update_date(key_vac, 'is_wday', False)
일자 처리
아래 코드는 주중 인지 주말인지 예외(휴가등) 일자를 구분하는 함수와 결과를 프린트 함수이다.
- set_date : dict_date_zw_wk 에 중복 되지 않으면 요일정보 기록한다.
- update_date: dict_date_zw_wk에 일자 정보를 업데이트 한다.
- print_date : 결과 출력하는 함수.
s_date_zw_wk_list =[] dict_date_zw_wk ={} def set_date(key_date, i_wday): global s_date_zw_wk_list, dict_date_zw_wk if key_date not in dict_date_zw_wk.keys(): s_date_zw_wk_list.append({'date': key_date}) bool_iswday = True if i_wday<5 else False dict_date_zw_wk[key_date]={'is_wday':bool_iswday} def update_date(key_date, key_data, data_data): global s_date_zw_wk_list, dict_date_zw_wk if key_date in dict_date_zw_wk.keys(): dict_date_zw_wk[key_date][key_data]=data_data def print_date(): idx_gtime_wday_base =1 idx_gtime_wday_t =1 for it_date in s_date_zw_wk_list: temp_date =it_date['date'] b_wday = dict_date_zw_wk[temp_date]['is_wday'] list_keys =dict_date_zw_wk[temp_date].keys() # 데이터기반 총 근무일 일수 G타임기록이 있고, 근무일이면 카운트 (idx_gtime_wday_base) if 'n_day_gmap' in list_keys and b_wday == True: idx_gtime_wday_base +=1 if 'gowork' in list_keys or 'comment' in list_keys: idx_gtime_wday_t +=1 print(f'* 구글 타임라인 정보기반의 총 업무일(명절제외) :{idx_gtime_wday_base:,}일 중 {idx_gtime_wday_t}일 증빙일, 출근(증빙)일율: {(idx_gtime_wday_t*100/idx_gtime_wday_base):3.2f}% ') print() idx = 1 for it_date in s_date_zw_wk_list: temp_date =it_date['date'] b_wday = dict_date_zw_wk[temp_date]['is_wday'] list_keys =dict_date_zw_wk[temp_date].keys() b_target = 'O' if 'n_day_gmap' in list_keys and b_wday == True else 'X' b_gowork = 'O' if ('gowork' in list_keys or 'comment' in list_keys) and b_wday == True else 'X' n_timeline = f"{dict_date_zw_wk[temp_date]['n_day_gmap']}" if 'n_day_gmap' in list_keys else '' sd_datetime_gowork = f"{dict_date_zw_wk[temp_date]['datetime_gowork']}" if 'datetime_gowork' in list_keys else '' s_comment = f"{dict_date_zw_wk[temp_date]['comment']}" if 'comment' in list_keys else '' print(f'{idx} {temp_date} {dict_date_zw_wk[temp_date]}') idx +=1 with open('./google_timeline_gowork.csv', 'a', encoding='utf-8-sig') as fcsv: fcsv.write(f'{temp_date}, {b_target}, {b_gowork}, {sd_datetime_gowork}, {n_timeline}, {s_comment}\n')
구분 처리
아래 코드는 Google Map을 활용하여 위치정보 추출하는 Python코드의 메인 처리 코드이다.
처리순서는 근무시작일, 근무종료일을 설정하고 s_date_zw_wk_list에 구분하여 저장하여 테이블을 준비한다.
Google Map의 위치 정보(json파일 로드)를 순차적으로 읽어 처리를 한다.
다음은 메인 처리 순서 및 소스이다.
- load json : Google 계정의 Takeout으로 생성한 json 파일을 로드한다.
- 근무일 설정 : 출퇴근 정보를 추출할 시작일과 종료일을 설정한다.
- 근무일을 순차적으로 loop돌며 데이터를 정리한다(s_date_zw_wk_list(결과물), dict_date_zw_wk(처리를 위한 db))
- 구글위치정보를 순차적으로 처리하며 근무Zone에 드나드는 시간을 체크한다.
- 결과 출력 : s_date_zw_wk_list(결과물) 정보를 기반으로 출력한다.
- 이 부분을 확장하여 csv나 xls파일로 변환 시킬 수 있다.
import json, datetime # 1. load json g_pos = None with open('./Records.json', 'r') as fp: g_pos = json.load(fp) tot_n_rec = 1 wk_n_rec = 1 nwk_n_rec = 1 # 근무한 총 워크 데이 계산 tot_n_work = 1 wd_n_work = 1 nwd_n_work = 1 s_date_zw_start = '2017-09-01' s_date_zw_end = '2022-07-31' date_zw_start=datetime.datetime.strptime(s_date_zw_start, '%Y-%m-%d') date_zw_end=datetime.datetime.strptime(s_date_zw_end, '%Y-%m-%d') temp_date = date_zw_start while temp_date <= date_zw_end: i_wday = temp_date.weekday() if i_wday < 5: #주중 wd_n_work += 1 else: #주말 nwd_n_work += 1 s_temp_date = temp_date.strftime('%Y-%m-%d') set_date(s_temp_date, i_wday) # print(temp_date, date_zw_end) temp_date = temp_date + datetime.timedelta(days=1) tot_n_work += 1 print(f'* 총 XXXX 근무일: {tot_n_work:,} ({s_date_zw_start} ~ {s_date_zw_end}), 주중:{wd_n_work:,}, 주말:{nwd_n_work:,}') # 휴가정보 로드 update_date_vacances() # google json 데이터 파일 로드 및 처리 sDatetime_Start = None sDatetime_End = None s_date_last_proc = None tot_n_day_gmap = 1 tot_n_wd_day_gmap = 1 tot_n_nwd_day_gmap = 1 n_day_gmap = 1 bool_gowork = False datetime_gowork_start = None # 근무지1 영역 y1_s = 372987220 x1_s = 1270418450 y2_s = 372964600 x2_s = 1270462760 #근무지2 영역 y1_g = 374821150 x1_g = 1268715370 y2_g = 374766480 x2_g = 1268830200 for it_gpos in g_pos['locations']: s_datetime=it_gpos['timestamp'] temp_datetime= datetime.datetime.strptime(s_datetime, '%Y-%m-%dT%H:%M:%S.%fZ') if len(s_datetime) == 24 else datetime.datetime.strptime(s_datetime, '%Y-%m-%dT%H:%M:%SZ') # temp_datetime=s_datetime.strftime('%Y-%m-%dT%H:%M:%S_') # print(temp_datetime) #UTC 시간이므로 +9시간 로컬시간으로 temp_datetime = temp_datetime + datetime.timedelta(hours=9) # print(temp_datetime) s_datetime = temp_datetime.strftime('%Y-%m-%dT%H:%M:%S') if sDatetime_Start is None: sDatetime_Start=s_datetime sDatetime_End=s_datetime #요일별 구하기 list_datetime=s_datetime.split('T') stemp_datetime = f'{list_datetime[0]} {list_datetime[1]}' # temp_datetime = datetime.datetime.strptime(stemp_datetime, '%Y-%m-%d %H:%M:%S') # #UTC 시간이므로 +9시간 로컬시간으로 # temp_datetime = temp_datetime + datetime.timedelta(hours=9) i_wday = temp_datetime.weekday() if i_wday <5: wk_n_rec += 1 temp_lat=it_gpos['latitudeE7'] temp_long=it_gpos['longitudeE7'] temp_area = 's' if list_datetime[0] <= '2017-12-18' else 'g' # 근무지2(g)으로 이사 이전은 근무지1(s) 구 if ( temp_area == 's' and ((y1_s > temp_lat and x1_s < temp_long) and (y2_s < temp_lat and x2_s > temp_long))) \ or \ ( temp_area == 'g' and ((y1_g > temp_lat and x1_g < temp_long) and (y2_g < temp_lat and x2_g > temp_long))): bool_gowork = True if datetime_gowork_start is None: datetime_gowork_start = stemp_datetime update_date(list_datetime[0], 'datetime_gowork', datetime_gowork_start) update_date(s_date_last_proc, 'gowork', True) else: # 주말처리 nwk_n_rec += 1 if (s_date_last_proc is None) or (s_date_last_proc != list_datetime[0]): s_date_last_proc = list_datetime[0] tot_n_day_gmap += 1 if (i_wday < 5): tot_n_wd_day_gmap += 1 else: tot_n_nwd_day_gmap += 1 update_date(s_date_last_proc, 'n_day_gmap', n_day_gmap) # if bool_gowork == True: # update_date(s_date_last_proc, 'gowork', bool_gowork) n_day_gmap = 1 bool_gowork = False datetime_gowork_start = None # print(f'{idx} : {s_datetime}') tot_n_rec+=1 n_day_gmap +=1 print(f'* Google Map record ({sDatetime_Start[:19]} ~ {sDatetime_End[:19]})') print(f'\ttotal: {tot_n_rec:,} work day: {wk_n_rec:,}, weekend: {nwk_n_rec:,}') #XXXX 근무일의 업무가능일 대비, 타임라인에 있는 일수 p_gmap_rec = tot_n_wd_day_gmap*100/wd_n_work print(f'\ttotal day(구글타임라인) : {tot_n_day_gmap:,}, 주중: {tot_n_wd_day_gmap:,}({p_gmap_rec:2.2f}%), 주말: {tot_n_nwd_day_gmap:,}') print_date()
실행 결과는 다음과 같다.
* 총 XXXX 근무일: 1,795 (2017-09-01 ~ 2022-07-31), 주중:1,282, 주말:513 * Google Map record (2017-09-01T09:04:03 ~ 2022-08-01T08:33:19) total: 1,088,013 work day: 792,966, weekend: 295,048 total day(구글타임라인) : 1,423, 주중: 1,013, 주말: 411 * 구글 타임라인 정보기반의 총 업무일(명절제외) :995일 중 903일 증빙일, 출근(증빙)일율: 90.75% 1 2017-09-01 {'is_wday': True, 'datetime_gowork': '2017-09-01 09:05:08', 'n_day_gmap': 1, 'gowork': True} 2 2017-09-02 {'is_wday': False, 'n_day_gmap': 515} 3 2017-09-03 {'is_wday': False, 'n_day_gmap': 506} 4 2017-09-04 {'is_wday': True, 'n_day_gmap': 411, 'datetime_gowork': '2017-09-04 08:53:56', 'gowork': True} 5 2017-09-05 {'is_wday': True, 'n_day_gmap': 660, 'datetime_gowork': '2017-09-05 08:53:31', 'gowork': True} 6 2017-09-06 {'is_wday': True, 'n_day_gmap': 1671, 'datetime_gowork': '2017-09-06 08:48:05', 'gowork': True} 7 2017-09-07 {'is_wday': True, 'n_day_gmap': 2032, 'datetime_gowork': '2017-09-07 08:48:29', 'gowork': True} 8 2017-09-08 {'is_wday': True, 'n_day_gmap': 566, 'datetime_gowork': '2017-09-08 08:40:14', 'gowork': True} 9 2017-09-09 {'is_wday': False, 'n_day_gmap': 706} 10 2017-09-10 {'is_wday': False, 'n_day_gmap': 998} 11 2017-09-11 {'is_wday': True, 'n_day_gmap': 1123, 'datetime_gowork': '2017-09-11 08:53:52', 'gowork': True} 12 2017-09-12 {'is_wday': True, 'n_day_gmap': 1386, 'datetime_gowork': '2017-09-12 07:47:29', 'gowork': True} 13 2017-09-13 {'is_wday': True, 'n_day_gmap': 1208, 'datetime_gowork': '2017-09-13 10:50:25', 'gowork': True} 14 2017-09-14 {'is_wday': True, 'n_day_gmap': 1345, 'datetime_gowork': '2017-09-14 09:03:54', 'gowork': True} 15 2017-09-15 {'is_wday': True, 'n_day_gmap': 1251, 'datetime_gowork': '2017-09-15 08:59:05', 'gowork': True} 16 2017-09-16 {'is_wday': False, 'n_day_gmap': 1259} 17 2017-09-17 {'is_wday': False, 'n_day_gmap': 1905} 18 2017-09-18 {'is_wday': True, 'n_day_gmap': 1277, 'datetime_gowork': '2017-09-18 09:08:34', 'gowork': True} 19 2017-09-19 {'is_wday': True, 'n_day_gmap': 1542, 'datetime_gowork': '2017-09-19 08:59:20', 'gowork': True} 20 2017-09-20 {'is_wday': True, 'n_day_gmap': 1154, 'datetime_gowork': '2017-09-20 08:42:33', 'gowork': True} 21 2017-09-21 {'is_wday': True, 'n_day_gmap': 1388, 'datetime_gowork': '2017-09-21 08:08:41', 'gowork': True} 22 2017-09-22 {'is_wday': True, 'n_day_gmap': 1492, 'datetime_gowork': '2017-09-22 08:47:28', 'gowork': True} . . 249 2018-05-07 {'is_wday': True, 'n_day_gmap': 717} 250 2018-05-08 {'is_wday': True, 'n_day_gmap': 897, 'datetime_gowork': '2018-05-08 09:54:26', 'gowork': True} 251 2018-05-09 {'is_wday': True, 'n_day_gmap': 803, 'datetime_gowork': '2018-05-09 09:41:35', 'gowork': True} 252 2018-05-10 {'is_wday': True, 'n_day_gmap': 999, 'datetime_gowork': '2018-05-10 09:19:47', 'gowork': True} 253 2018-05-11 {'is_wday': True, 'n_day_gmap': 843, 'gowork': True} 254 2018-05-12 {'is_wday': False} 255 2018-05-13 {'is_wday': False} 256 2018-05-14 {'is_wday': True} 257 2018-05-15 {'is_wday': True, 'comment': '출근증빙사진(https://photos.google.com/photo/AF1QipMwd_mKMGz-5V72gWyKtBXo)'} 258 2018-05-16 {'is_wday': True, 'comment': '출근증빙사진(https://photos.google.com/photo/AF1QipPbcsuE3NZIKcCSzFV5IhDR)'} 259 2018-05-17 {'is_wday': True} 260 2018-05-18 {'is_wday': True} 261 2018-05-19 {'is_wday': False} 262 2018-05-20 {'is_wday': False} 263 2018-05-21 {'is_wday': True} 264 2018-05-22 {'is_wday': False, 'comment': '부처님오신날'} 265 2018-05-23 {'is_wday': True} 266 2018-05-24 {'is_wday': True} 267 2018-05-25 {'is_wday': True} 268 2018-05-26 {'is_wday': False} 269 2018-05-27 {'is_wday': False} . . 775 2022-07-11 {'is_wday': True, 'n_day_gmap': 149, 'datetime_gowork': '2022-07-11 13:22:38', 'gowork': True} 1776 2022-07-12 {'is_wday': True, 'n_day_gmap': 316, 'datetime_gowork': '2022-07-12 07:26:03', 'gowork': True} 1777 2022-07-13 {'is_wday': True, 'n_day_gmap': 347} 1778 2022-07-14 {'is_wday': True, 'n_day_gmap': 649, 'datetime_gowork': '2022-07-14 09:10:18', 'gowork': True} 1779 2022-07-15 {'is_wday': True, 'n_day_gmap': 230, 'datetime_gowork': '2022-07-15 08:48:26', 'gowork': True} 1780 2022-07-16 {'is_wday': False, 'n_day_gmap': 319} 1781 2022-07-17 {'is_wday': False, 'n_day_gmap': 239} 1782 2022-07-18 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 252} 1783 2022-07-19 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 438} 1784 2022-07-20 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 295} 1785 2022-07-21 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 368} 1786 2022-07-22 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 353} 1787 2022-07-23 {'is_wday': False, 'n_day_gmap': 635} 1788 2022-07-24 {'is_wday': False, 'n_day_gmap': 199} 1789 2022-07-25 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 197} 1790 2022-07-26 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 345} 1791 2022-07-27 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 331} 1792 2022-07-28 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 461} 1793 2022-07-29 {'is_wday': True, 'comment': '휴가', 'n_day_gmap': 317} 1794 2022-07-30 {'is_wday': False, 'n_day_gmap': 342} 1795 2022-07-31 {'is_wday': False, 'n_day_gmap': 243}
코드의 활용
Google Map을 활용하여 위치정보 추출하는 Python코드를 만들어 보았다.
Google Map 타임라인 정보를 황용하면 특정 Zone에 오간 통계나 실시간 알림을 보내는 프로그램도
만들 수 있겠다 생각해봤다.
감사합니다.
Google 포토 대체 할 오픈소스 IMMICH 설치방법
Google 포토 서비스는 막강한 백업 기능과 공유 기능에 젖어 10여년 가까이 익숙해진 서비스였다. 2021.6월 유료화를 선언하고 거리감을 갖게 되었다. 대체할 오프소스 솔루션을 찾아 헤매다 Google 포토 대체 오픈소스 IMMICH라는 강력한 오픈소스를 찾았다. 이 서비스 기능을 소개 하고 Google 포토 대체 할 오픈소스 IMMICH 설치방법 을 알아보고자 한다. Immich Immich는 github에서 빈번한 업데이트를 수행하는 유명한 오픈소스 프로젝트이다. 서비스 특징 자동백업 Immich는 Forground, Background 백업 기능을 제공한다. ...
ChatGPT 4 탈옥 (DAN모드) 진입방법
ChatGPT는 다양한 지식을 유려한 문장으로 답변하여 준다. 이러한 방대한 지식도 19금 처럼 파인 튜닝 하며 등급을 정하여 필터링 하고 있다. 이 번 글은 ChatGPT의 19금 필터링을 해소하는 ChatGPT 4 탈옥 (DAN모드) 진입방법 에 대해서 알아보자. ChatGPT의 답변은 강화학습을 통하여 여러가지 불편한 부분(폭력, 공포, 성적 등등)을 필터링 하여 답변하고 있습니다. 이것을 DAN모드라고 한다. 이 DAN은 “Do Anything Now”의 준말입니다. 이는 윤리나 규칙에 어긋나지 않게 필터링 하는 부분을 걷어 낸 어두운 페르소나이다. DAN이 알려진 후, 폭발적인 사용자 접속으로 더욱 활성화 되었으며, Open AI는 DAN 시도를 방어하는 노력이 무색하게 새로운 DAN이 등장하는 등 숨바꼭질을 하고 있는 상황입니다. ChatGPT 4 탈옥 (DAN모드) 진입방법 ...