text: from __future__ import print_function
  
  from collections import OrderedDict
  from typing import List
  
  from apiclient.discovery import Resource, build
  from httplib2 import Http
  from oauth2client import client, file, tools
  
  from judgement_type import JudgementType
  from setting_manager import CALENDAR_META_HASH_BASE, now, public_values, start as _start
  from util import create_hash
  
  CREDENTIAL_JSON = 'credential/{0}_credential.json'
  
  
  # noinspection PyUnresolvedReferences,PyMethodMayBeStatic
  class GoogleCalendar:
      scope = public_values['google_scope']
  
      def __init__(self, calendar_name: str, prefix: str, schedules: dict, common: dict, common_diff: bool):
          self.prefix = prefix
          self.service = self.__create_service(prefix)
          self.target_calendar = self.__get_target(calendar_name)
          self.schedules = schedules
          self.common = common
          self.common_diff = common_diff
  
      def __create_service(self, prefix: str) -> Resource:
          store = file.Storage(CREDENTIAL_JSON.format(prefix))
          creds = store.get()
          if not creds or creds.invalid:
              flow = client.flow_from_clientsecrets('client_secret.json', self.scope)
              flags = tools.argparser.parse_args(args=[])
              creds = tools.run_flow(flow, store, flags)
          return build('calendar', 'v3', http=creds.authorize(Http()))
  
      def __get_target(self, calendar_name: str):
          target = filter(lambda i: (i['summary'] if 'summary' in i else None) == calendar_name,
                          self.service.calendarList().list().execute()['items'])
          result = next(target, None)
          if result is None:
              return self.service.calendars() \
                  .insert(body={'summary': calendar_name, 'timeZone': 'Asia/Tokyo'}) \
                  .execute()
          return result
  
      def update_schedule(self):
          _hash = create_hash(self.schedules)
          if _hash == self.__get_target_hash() and not self.common_diff:
              print("{0}:difference none.".format(self.prefix))
              return
  
          target_id = self.target_calendar['id']
          matching_id = []  # type: List[str]
          for schedule in self.schedules:
              schedule_hash = create_hash(schedule)
              _judgement_status, gcal_event_id = self.__get_judgement_status(target_id, schedule, schedule_hash)
              if _judgement_status != JudgementType.EQUAL_HASH:
                  if _judgement_status == JudgementType.EQUAL_ID:
                      self.__delete_schedule(target_id, gcal_event_id)
                  gcal_event_id = self.__register_schedule(schedule, target_id, schedule_hash)
              matching_id.append(gcal_event_id)
          print('deleted cybozu schedule')
          self.__delete_deleted_cybozu_schedule(target_id, matching_id)
          print('update hash')
          self.__update_target_hash(target_id, _hash)
  
      def __get_target_hash(self):
          import re
          desc = self.target_calendar.get('description')
          if desc is None:
              return
          regex = re.compile(CALENDAR_META_HASH_BASE.format("([A-z0-9]+)"))
          matcher = regex.search(desc)
          return matcher.group(1)
  
      def __update_target_hash(self, target_id: str, _hash: str):
          calendar = self.service.calendars().get(calendarId=target_id).execute()
          calendar['description'] = CALENDAR_META_HASH_BASE.format(_hash)
          self.service.calendars().update(calendarId=target_id, body=calendar).execute()
  
      def __get_judgement_status(self, target_id: str, schedule: dict, schedule_hash: str) -> tuple:
          equal_hash = self.__search_events(target_id, lambda: 'hash=' + schedule_hash)
          if equal_hash:
              return JudgementType.EQUAL_HASH, equal_hash[0]['id']
          equal_id = self.__search_events(target_id, lambda: 'cybozu_id=' + str(schedule['id']))
          if equal_id:
              return JudgementType.EQUAL_ID, equal_id[0]['id']
          return JudgementType.NONE, ''
  
      def __search_events(self, target_id: str, value_func: ()) -> list:
          return self.service.events().list(
              calendarId=target_id,
              timeMin=_start.isoformat(),
              maxResults=10,
              privateExtendedProperty=value_func()
          ).execute() \
              .get('items', [])
  
      def __delete_schedule(self, target_id: str, gcal_event_id: str):
          self.service.events().delete(calendarId=target_id, eventId=gcal_event_id).execute()
  
      def __delete_deleted_cybozu_schedule(self, target_id: str, matching_ids: list):
          events_result = self.service \
              .events() \
              .list(calendarId=target_id,
                    timeMin=_start.isoformat(),
                    singleEvents=True,
                    maxResults=999
                    ) \
              .execute()
          events = events_result.get('items', [])  # type: list
          if events:
              for event in filter(lambda e: e['id'] not in matching_ids, events):
                  self.service.events().delete(calendarId=target_id, eventId=event['id']).execute()
  
      def __register_schedule(self, schedule: dict, target_id: str, schedule_hash: str):
          body = OrderedDict()
          body['summary'] = schedule['title']
          body.update(self.__get_times(schedule))
          for facility in schedule['facilities']:
              if facility in self.common['facility']['place']:
                  body['location'] = self.common['facility']['place'][facility]['name']
                  schedule['facilities'].remove(facility)
                  break
          description = public_values['template'].format(
              schedule['body'],
              self.__resolve_id(self.common['user'], schedule['users']),
              self.__resolve_id(self.common['facility']['other'], schedule['facilities'])
          )
          body['description'] = description
          body['extendedProperties'] = dict(private=dict(hash=schedule_hash, cybozu_id=schedule['id']))
          print(body)
          return self.service.events().insert(calendarId=target_id, body=body).execute()['id']
  
      def __get_times(self, schedule: dict):
          time_key = 'date' if schedule['allDay'] else 'dateTime'
          start = schedule['start']
          end = schedule['end']
          if end is None:  # allDayではありえないはず
              from datetime import datetime, timedelta
              date = datetime.strptime(start, '%Y-%m-%dT%H:%M:%SZ') + timedelta(days=1)
              end = datetime.strftime(
                  datetime(date.year,
                           date.month,
                           date.day, 0, 0,
                           tzinfo=now.tzinfo),
                  '%Y-%m-%dT%H:%M:%S+09:00'
              )
          return {
              'start': {'timeZone': now.tzinfo.__str__(), time_key: start},
              'end': {'timeZone': now.tzinfo.__str__(), time_key: end}
          }
  
      def __resolve_id(self, master: dict, targets: dict):
          result = []
          for target in targets:
              name = master[target]['name']  # type: str
              result.append(str(name.replace('　', ' ')))
          return '\n'.join(result)
  