質問編集履歴

3

誤字

2021/10/23 04:05

投稿

YYYuto
YYYuto

スコア0

test CHANGED
File without changes
test CHANGED
@@ -1,6 +1,6 @@
1
1
  ### 前提・実現したいこと
2
2
 
3
- x写真をOCRして読み取った文字からカレンダーに予定を追加するというアプリを作っています。
3
+ 写真をOCRして読み取った文字からカレンダーに予定を追加するというアプリを作っています。
4
4
 
5
5
  しかし、カレンダーに予定を追加する関数が動いてくれません。
6
6
 

2

初心者アイコン

2021/10/23 04:05

投稿

YYYuto
YYYuto

スコア0

test CHANGED
File without changes
test CHANGED
@@ -1,6 +1,6 @@
1
1
  ### 前提・実現したいこと
2
2
 
3
- 写真をOCRして読み取った文字からカレンダーに予定を追加するというアプリを作っています。
3
+ x写真をOCRして読み取った文字からカレンダーに予定を追加するというアプリを作っています。
4
4
 
5
5
  しかし、カレンダーに予定を追加する関数が動いてくれません。
6
6
 
@@ -14,386 +14,386 @@
14
14
 
15
15
 
16
16
 
17
+ ```x
18
+
19
+ 127.0.0.1 - - [23/Oct/2021 12:27:46] "POST /post_img_test HTTP/1.1" 500 -
20
+
21
+ Traceback (most recent call last):
22
+
23
+ File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 2088, in __call__
24
+
25
+ return self.wsgi_app(environ, start_response)
26
+
27
+ File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 2073, in wsgi_app
28
+
29
+ response = self.handle_exception(e)
30
+
31
+ File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 2070, in wsgi_app
32
+
33
+ response = self.full_dispatch_request()
34
+
35
+ File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 1515, in full_dispatch_request
36
+
37
+ rv = self.handle_user_exception(e)
38
+
39
+ File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 1513, in full_dispatch_request
40
+
41
+ rv = self.dispatch_request()
42
+
43
+ File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 1499, in dispatch_request
44
+
45
+ return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
46
+
47
+ File "/Users/yuto_shinohara/Documents/python/textdata/camender/server.py", line 76, in index
48
+
49
+ googlecalenderattach(txt)
50
+
51
+ File "/Users/yuto_shinohara/Documents/python/textdata/camender/server.py", line 128, in googlecalenderattach
52
+
53
+ creds = pickle.load(token)
54
+
55
+ EOFError: Ran out of input
56
+
17
57
  ```
18
58
 
19
- 127.0.0.1 - - [23/Oct/2021 12:27:46] "POST /post_img_test HTTP/1.1" 500 -
20
-
21
- Traceback (most recent call last):
22
-
23
- File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 2088, in __call__
24
-
25
- return self.wsgi_app(environ, start_response)
26
-
27
- File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 2073, in wsgi_app
28
-
29
- response = self.handle_exception(e)
30
-
31
- File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 2070, in wsgi_app
32
-
33
- response = self.full_dispatch_request()
34
-
35
- File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 1515, in full_dispatch_request
36
-
37
- rv = self.handle_user_exception(e)
38
-
39
- File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 1513, in full_dispatch_request
40
-
41
- rv = self.dispatch_request()
42
-
43
- File "/Users/yuto_shinohara/.pyenv/versions/3.8.6/lib/python3.8/site-packages/flask/app.py", line 1499, in dispatch_request
44
-
45
- return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
46
-
47
- File "/Users/yuto_shinohara/Documents/python/textdata/camender/server.py", line 76, in index
48
-
49
- googlecalenderattach(txt)
50
-
51
- File "/Users/yuto_shinohara/Documents/python/textdata/camender/server.py", line 128, in googlecalenderattach
52
-
53
- creds = pickle.load(token)
54
-
55
- EOFError: Ran out of input
59
+
60
+
61
+ ### 該当のソースコード
62
+
63
+
64
+
65
+ ```python
66
+
67
+ from __future__ import print_function
68
+
69
+ from flask import Flask, jsonify, request
70
+
71
+ from PIL import Image
72
+
73
+ import json
74
+
75
+ import base64
76
+
77
+ from io import BytesIO
78
+
79
+ import matplotlib.pyplot as plt
80
+
81
+ import copy
82
+
83
+ import numpy as np
84
+
85
+ import cv2
86
+
87
+ import pyocr
88
+
89
+ import pyocr.builders
90
+
91
+ import datetime
92
+
93
+ import os.path
94
+
95
+ from googleapiclient.discovery import build
96
+
97
+ from google_auth_oauthlib.flow import InstalledAppFlow
98
+
99
+ from google.auth.transport.requests import Request
100
+
101
+ from google.oauth2.credentials import Credentials
102
+
103
+ import pickle
104
+
105
+
106
+
107
+
108
+
109
+ tools = pyocr.get_available_tools()
110
+
111
+ if len(tools) == 0:
112
+
113
+ print("No OCR tool found")
114
+
115
+ sys.exit(1)
116
+
117
+ tool = tools[0]
118
+
119
+ print("Will use tool '%s'" % (tool.get_name()))
120
+
121
+ langs = tool.get_available_languages()
122
+
123
+ print("Available languages: %s" % ", ".join(langs))
124
+
125
+
126
+
127
+ app = Flask(__name__)
128
+
129
+
130
+
131
+ SCOPES = ['https://www.googleapis.com/auth/calendar']
132
+
133
+ @app.route("/post_img_test", methods=["GET", "POST"])
134
+
135
+ def index():
136
+
137
+ # json_data = request.get_json() # POSTされたjsonを取得
138
+
139
+ # dict_data = json.loads(json_data) # jsonを辞書に変換
140
+
141
+ img_stream = base64.b64decode(request.form["img"])
142
+
143
+ cols = int(request.form["COLS"])
144
+
145
+ rows = int(request.form["ROWS"])
146
+
147
+
148
+
149
+ img_array = np.asarray(bytearray(img_stream),dtype=np.uint8)
150
+
151
+ img = cv2.imdecode(img_array,1)
152
+
153
+ h,w,c = img.shape
154
+
155
+ print(h,w,c)
156
+
157
+ img2 = copy.deepcopy(img)
158
+
159
+
160
+
161
+ directions = ["UL","UR","LL","LR"]
162
+
163
+ positions = []
164
+
165
+
166
+
167
+ for dire in directions:
168
+
169
+ pos = request.form[dire].split(",")#場所取得
170
+
171
+ print(pos)
172
+
173
+ positions.append([int(float(pos[0]))+w//2, h//2-int(float(pos[1]))])
174
+
175
+ cv2.circle(img,positions[-1],10,(255,255,255),thickness=-1)#場所チェック
176
+
177
+
178
+
179
+ cv2.imwrite('result_checking.png',img)
180
+
181
+
182
+
183
+ #射影変換後のサイズ
184
+
185
+ after_w = max(positions,key=lambda x:x[0])[0]-min(positions,key=lambda x:x[0])[0]
186
+
187
+ after_h = max(positions,key=lambda x:x[1])[1]-min(positions,key=lambda x:x[1])[1]
188
+
189
+
190
+
191
+ #射影変換
192
+
193
+ pts=np.float32([[0,0],[after_w,0],[0,after_h],[after_w,after_h]])
194
+
195
+ M = cv2.getPerspectiveTransform(np.float32(positions),pts)
196
+
197
+ dst = cv2.warpPerspective(img2,M,(after_w,after_h))
198
+
199
+
200
+
201
+ #射影変換を保存
202
+
203
+ cv2.imwrite('result.png',dst)
204
+
205
+
206
+
207
+ imgs = chuncks(dst,rows,cols)
208
+
209
+ for i in range(len(imgs)):
210
+
211
+ imgs[i] = noizclear(imgs[i])
212
+
213
+ global txt
214
+
215
+ txt = tool.image_to_string(cv2pil(imgs[i]),lang='jpn',builder=pyocr.builders.TextBuilder(tesseract_layout=6))
216
+
217
+ googlecalenderattach(txt)
218
+
219
+ return 'ok'
220
+
221
+ #画像のノイズを消す。
222
+
223
+ def noizclear(img):
224
+
225
+ resize_to = (img.shape[0]*10,img.shape[1]*10) # Rescale the image more than 300 dpi
226
+
227
+ block_size = 147 # Block size for adaptive thresholding. This should be positive and odd value more than 1.
228
+
229
+ C = 31 # The constant to subtract from mean or weighted mean
230
+
231
+ threshold = 140 # The threshold for simple thresholding
232
+
233
+ open_ksize = (5, 5) # The kernel size to remove white patch in black region
234
+
235
+ close_ksize = (3, 3) # The kernel size to remove black patch in white region
236
+
237
+ blur_ksize = (5, 5) # The Gaussian kernel size to set blur. This should be positive and odd value
238
+
239
+ #img = cv2.imread('result.png')
240
+
241
+ img_generated = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
242
+
243
+ img_resized = cv2.resize(img_generated, resize_to, interpolation = cv2.INTER_LINEAR)
244
+
245
+ img_adaptive = cv2.adaptiveThreshold(img_resized, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, block_size, C)
246
+
247
+ img_morph_open = cv2.morphologyEx(img_adaptive, cv2.MORPH_OPEN, np.ones(open_ksize, np.uint8))
248
+
249
+ img_morph_close = cv2.morphologyEx(img_morph_open, cv2.MORPH_CLOSE, np.ones(close_ksize, np.uint8))
250
+
251
+ result, img_OTSU = cv2.threshold(img_resized, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
252
+
253
+ img_blur = cv2.GaussianBlur(img_OTSU, blur_ksize, cv2.BORDER_CONSTANT)
254
+
255
+ result, img_OTSU2 = cv2.threshold(img_blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
256
+
257
+ img_bitwise = cv2.bitwise_or(img_OTSU2, img_morph_close)
258
+
259
+ return img_bitwise
260
+
261
+ #画像を切り分ける。
262
+
263
+ def chuncks(dst,rows,cols):
264
+
265
+ chunk_lst = []
266
+
267
+ for row_img in np.array_split(dst,rows,axis=0):
268
+
269
+ for chunk in np.array_split(row_img,cols,axis=1):
270
+
271
+ chunk_lst.append(chunk)
272
+
273
+ print(len(chunk_lst))
274
+
275
+
276
+
277
+ return chunk_lst
278
+
279
+
280
+
281
+ def cv2pil(image):
282
+
283
+ ''' OpenCV型 -> PIL型 '''
284
+
285
+ new_image = image.copy()
286
+
287
+ if new_image.ndim == 2: # モノクロ
288
+
289
+ pass
290
+
291
+ elif new_image.shape[2] == 3: # カラー
292
+
293
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB)
294
+
295
+ elif new_image.shape[2] == 4: # 透過
296
+
297
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_BGRA2RGBA)
298
+
299
+ new_image = Image.fromarray(new_image)
300
+
301
+ return new_image
302
+
303
+ return jsonify(response)
304
+
305
+ #googleカレンダーに追加
306
+
307
+ def googlecalenderattach(txt):
308
+
309
+ creds = None
310
+
311
+ # The file token.pickle stores the user's access and refresh tokens, and is
312
+
313
+ # created automatically when the authorization flow completes for the first
314
+
315
+ # time.
316
+
317
+ if os.path.exists('token.pickle'):
318
+
319
+ with open('token.pickle', 'rb') as token:
320
+
321
+ creds = pickle.load(token)
322
+
323
+ # If there are no (valid) credentials available, let the user log in.
324
+
325
+ if not creds or not creds.valid:
326
+
327
+ if creds and creds.expired and creds.refresh_token:
328
+
329
+ creds.refresh(Request())
330
+
331
+ else:
332
+
333
+ flow = InstalledAppFlow.from_client_secrets_file(
334
+
335
+ 'credentials.json', SCOPES)
336
+
337
+ creds = flow.run_local_server(port=0)
338
+
339
+ # Save the credentials for the next run
340
+
341
+ with open('token.pickle', 'wb') as token:
342
+
343
+ pickle.dump(creds, token)
344
+
345
+
346
+
347
+ service = build('calendar', 'v3', credentials=creds)
348
+
349
+
350
+
351
+ event = {
352
+
353
+ 'summary': 'a',
354
+
355
+ 'start': {
356
+
357
+ 'date': datetime.date(2021,11,30).strftime(r'%Y-%m-%d'),
358
+
359
+ 'timeZone': 'Japan',
360
+
361
+ },
362
+
363
+ 'end': {
364
+
365
+ 'date': datetime.date(2021,11,30).strftime(r'%Y-%m-%d'),
366
+
367
+ 'timeZone': 'Japan',
368
+
369
+ },
370
+
371
+ }
372
+
373
+ event = service.events().insert(calendarId='自分のカレンダーIDを入力',body=event).execute()
374
+
375
+ print (event['id'])
376
+
377
+
378
+
379
+
380
+
381
+
382
+
383
+
384
+
385
+ if __name__ == "__main__":
386
+
387
+ app.debug = True
388
+
389
+ app.run(host='127.0.0.1',port=5000)
390
+
391
+
56
392
 
57
393
  ```
58
394
 
59
395
 
60
396
 
61
- ### 該当のソースコード
62
-
63
-
64
-
65
- ```python
66
-
67
- from __future__ import print_function
68
-
69
- from flask import Flask, jsonify, request
70
-
71
- from PIL import Image
72
-
73
- import json
74
-
75
- import base64
76
-
77
- from io import BytesIO
78
-
79
- import matplotlib.pyplot as plt
80
-
81
- import copy
82
-
83
- import numpy as np
84
-
85
- import cv2
86
-
87
- import pyocr
88
-
89
- import pyocr.builders
90
-
91
- import datetime
92
-
93
- import os.path
94
-
95
- from googleapiclient.discovery import build
96
-
97
- from google_auth_oauthlib.flow import InstalledAppFlow
98
-
99
- from google.auth.transport.requests import Request
100
-
101
- from google.oauth2.credentials import Credentials
102
-
103
- import pickle
104
-
105
-
106
-
107
-
108
-
109
- tools = pyocr.get_available_tools()
110
-
111
- if len(tools) == 0:
112
-
113
- print("No OCR tool found")
114
-
115
- sys.exit(1)
116
-
117
- tool = tools[0]
118
-
119
- print("Will use tool '%s'" % (tool.get_name()))
120
-
121
- langs = tool.get_available_languages()
122
-
123
- print("Available languages: %s" % ", ".join(langs))
124
-
125
-
126
-
127
- app = Flask(__name__)
128
-
129
-
130
-
131
- SCOPES = ['https://www.googleapis.com/auth/calendar']
132
-
133
- @app.route("/post_img_test", methods=["GET", "POST"])
134
-
135
- def index():
136
-
137
- # json_data = request.get_json() # POSTされたjsonを取得
138
-
139
- # dict_data = json.loads(json_data) # jsonを辞書に変換
140
-
141
- img_stream = base64.b64decode(request.form["img"])
142
-
143
- cols = int(request.form["COLS"])
144
-
145
- rows = int(request.form["ROWS"])
146
-
147
-
148
-
149
- img_array = np.asarray(bytearray(img_stream),dtype=np.uint8)
150
-
151
- img = cv2.imdecode(img_array,1)
152
-
153
- h,w,c = img.shape
154
-
155
- print(h,w,c)
156
-
157
- img2 = copy.deepcopy(img)
158
-
159
-
160
-
161
- directions = ["UL","UR","LL","LR"]
162
-
163
- positions = []
164
-
165
-
166
-
167
- for dire in directions:
168
-
169
- pos = request.form[dire].split(",")#場所取得
170
-
171
- print(pos)
172
-
173
- positions.append([int(float(pos[0]))+w//2, h//2-int(float(pos[1]))])
174
-
175
- cv2.circle(img,positions[-1],10,(255,255,255),thickness=-1)#場所チェック
176
-
177
-
178
-
179
- cv2.imwrite('result_checking.png',img)
180
-
181
-
182
-
183
- #射影変換後のサイズ
184
-
185
- after_w = max(positions,key=lambda x:x[0])[0]-min(positions,key=lambda x:x[0])[0]
186
-
187
- after_h = max(positions,key=lambda x:x[1])[1]-min(positions,key=lambda x:x[1])[1]
188
-
189
-
190
-
191
- #射影変換
192
-
193
- pts=np.float32([[0,0],[after_w,0],[0,after_h],[after_w,after_h]])
194
-
195
- M = cv2.getPerspectiveTransform(np.float32(positions),pts)
196
-
197
- dst = cv2.warpPerspective(img2,M,(after_w,after_h))
198
-
199
-
200
-
201
- #射影変換を保存
202
-
203
- cv2.imwrite('result.png',dst)
204
-
205
-
206
-
207
- imgs = chuncks(dst,rows,cols)
208
-
209
- for i in range(len(imgs)):
210
-
211
- imgs[i] = noizclear(imgs[i])
212
-
213
- global txt
214
-
215
- txt = tool.image_to_string(cv2pil(imgs[i]),lang='jpn',builder=pyocr.builders.TextBuilder(tesseract_layout=6))
216
-
217
- googlecalenderattach(txt)
218
-
219
- return 'ok'
220
-
221
- #画像のノイズを消す。
222
-
223
- def noizclear(img):
224
-
225
- resize_to = (img.shape[0]*10,img.shape[1]*10) # Rescale the image more than 300 dpi
226
-
227
- block_size = 147 # Block size for adaptive thresholding. This should be positive and odd value more than 1.
228
-
229
- C = 31 # The constant to subtract from mean or weighted mean
230
-
231
- threshold = 140 # The threshold for simple thresholding
232
-
233
- open_ksize = (5, 5) # The kernel size to remove white patch in black region
234
-
235
- close_ksize = (3, 3) # The kernel size to remove black patch in white region
236
-
237
- blur_ksize = (5, 5) # The Gaussian kernel size to set blur. This should be positive and odd value
238
-
239
- #img = cv2.imread('result.png')
240
-
241
- img_generated = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
242
-
243
- img_resized = cv2.resize(img_generated, resize_to, interpolation = cv2.INTER_LINEAR)
244
-
245
- img_adaptive = cv2.adaptiveThreshold(img_resized, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, block_size, C)
246
-
247
- img_morph_open = cv2.morphologyEx(img_adaptive, cv2.MORPH_OPEN, np.ones(open_ksize, np.uint8))
248
-
249
- img_morph_close = cv2.morphologyEx(img_morph_open, cv2.MORPH_CLOSE, np.ones(close_ksize, np.uint8))
250
-
251
- result, img_OTSU = cv2.threshold(img_resized, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
252
-
253
- img_blur = cv2.GaussianBlur(img_OTSU, blur_ksize, cv2.BORDER_CONSTANT)
254
-
255
- result, img_OTSU2 = cv2.threshold(img_blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
256
-
257
- img_bitwise = cv2.bitwise_or(img_OTSU2, img_morph_close)
258
-
259
- return img_bitwise
260
-
261
- #画像を切り分ける。
262
-
263
- def chuncks(dst,rows,cols):
264
-
265
- chunk_lst = []
266
-
267
- for row_img in np.array_split(dst,rows,axis=0):
268
-
269
- for chunk in np.array_split(row_img,cols,axis=1):
270
-
271
- chunk_lst.append(chunk)
272
-
273
- print(len(chunk_lst))
274
-
275
-
276
-
277
- return chunk_lst
278
-
279
-
280
-
281
- def cv2pil(image):
282
-
283
- ''' OpenCV型 -> PIL型 '''
284
-
285
- new_image = image.copy()
286
-
287
- if new_image.ndim == 2: # モノクロ
288
-
289
- pass
290
-
291
- elif new_image.shape[2] == 3: # カラー
292
-
293
- new_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB)
294
-
295
- elif new_image.shape[2] == 4: # 透過
296
-
297
- new_image = cv2.cvtColor(new_image, cv2.COLOR_BGRA2RGBA)
298
-
299
- new_image = Image.fromarray(new_image)
300
-
301
- return new_image
302
-
303
- return jsonify(response)
304
-
305
- #googleカレンダーに追加
306
-
307
- def googlecalenderattach(txt):
308
-
309
- creds = None
310
-
311
- # The file token.pickle stores the user's access and refresh tokens, and is
312
-
313
- # created automatically when the authorization flow completes for the first
314
-
315
- # time.
316
-
317
- if os.path.exists('token.pickle'):
318
-
319
- with open('token.pickle', 'rb') as token:
320
-
321
- creds = pickle.load(token)
322
-
323
- # If there are no (valid) credentials available, let the user log in.
324
-
325
- if not creds or not creds.valid:
326
-
327
- if creds and creds.expired and creds.refresh_token:
328
-
329
- creds.refresh(Request())
330
-
331
- else:
332
-
333
- flow = InstalledAppFlow.from_client_secrets_file(
334
-
335
- 'credentials.json', SCOPES)
336
-
337
- creds = flow.run_local_server(port=0)
338
-
339
- # Save the credentials for the next run
340
-
341
- with open('token.pickle', 'wb') as token:
342
-
343
- pickle.dump(creds, token)
344
-
345
-
346
-
347
- service = build('calendar', 'v3', credentials=creds)
348
-
349
-
350
-
351
- event = {
352
-
353
- 'summary': 'a',
354
-
355
- 'start': {
356
-
357
- 'date': datetime.date(2021,11,30).strftime(r'%Y-%m-%d'),
358
-
359
- 'timeZone': 'Japan',
360
-
361
- },
362
-
363
- 'end': {
364
-
365
- 'date': datetime.date(2021,11,30).strftime(r'%Y-%m-%d'),
366
-
367
- 'timeZone': 'Japan',
368
-
369
- },
370
-
371
- }
372
-
373
- event = service.events().insert(calendarId='自分のカレンダーIDを入力',body=event).execute()
374
-
375
- print (event['id'])
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
- if __name__ == "__main__":
386
-
387
- app.debug = True
388
-
389
- app.run(host='127.0.0.1',port=5000)
390
-
391
-
392
-
393
- ```
394
-
395
-
396
-
397
397
  ### 試したこと
398
398
 
399
399
  調べたところ、解決できそうな情報は見つかりませんでした。

1

補足情報を追加

2021/10/23 04:03

投稿

YYYuto
YYYuto

スコア0

test CHANGED
@@ -1 +1 @@
1
- pythonのpickle
1
+ pythonのpickleのEOFErrorで困っています。
test CHANGED
@@ -405,3 +405,5 @@
405
405
  python 3.8.6
406
406
 
407
407
  Google Calender Apiを使っています.
408
+
409
+ https://developers.google.com/calendar/api/quickstart/python?hl=ja