Exemple #1
0
class DriveMainThread(Thread):
    def __init__(self, folders_to_track, start_sync_at, pyfile, **kwargs):
        super(DriveMainThread, self).__init__(**kwargs)
        self.drive_data = DBInterface(
            os.path.dirname(os.path.abspath(pyfile)) + '/drive_data/',
            'drive_files')
        base_path = os.path.dirname(os.path.abspath(pyfile)) + '/'
        self.updated_folders = set()
        self.start_sync_at = start_sync_at
        self.folders_to_track = folders_to_track
        self.setup_drive()
        self.update_queue = Queue.Queue()
        self.get_tracked_folder_ids(
            folders_to_track,
            os.path.dirname(os.path.abspath(pyfile)) + '/')
        self.tracked_widgets = {}
        self.folders_to_track = [
            base_path + path + '/' for path in folders_to_track
        ]

    def add_drive_widget(self, widget, folder_to_track):
        assert (folder_to_track in self.folders_to_track)
        tracked_widgets = self.tracked_widgets
        if folder_to_track not in tracked_widgets:
            tracked_widgets[folder_to_track] = [widget]
        else:
            tracked_widgets[folder_to_track].append(widget)

    def setup_drive(self):
        credentials = storage.get()
        self.queue = queue = Queue.Queue()
        if credentials is None:
            flow = OAuth2WebServerFlow(CLIENT_ID,
                                       CLIENT_SECRET,
                                       OAUTH_SCOPE,
                                       redirect_uri=REDIRECT_URI)
            authorize_url = flow.step1_get_authorize_url()
            print 'Go to the following link in your browser: ' + authorize_url
            code = raw_input('Enter verification code: ').strip()
            credentials = flow.step2_exchange(code)
            storage.put(credentials)
        http = httplib2.Http()
        http = credentials.authorize(http)
        self.drive_service = drive_service = build('drive', 'v2', http=http)
        self.thread_pool = thread_pool = DriveThreadPool(1, queue, credentials)

    def get_tracked_folder_ids(self, folders_to_track, address):
        drive_data = self.drive_data
        for folder_name in self.folders_to_track:
            q = "title = '{}'".format(folder_name)
            data = self.drive_service.children().list(folderId='root',
                                                      q=q).execute()
            folder_add = address + folder_name + '/'
            self.ensure_dir(folder_add)
            #self.add_folder_observer(self.get_callback, unicode(folder_add))
            for dat in data[u'items']:
                item_id = dat[u'id']
                drive_data.set_entry('folders', item_id, 'file_add',
                                     folder_add)
                drive_data.set_entry('tracked_items', item_id, 'file_add',
                                     folder_add)

    def check_file_in_tracked_folders(self, file_data):
        parents = file_data[u'parents']
        folders = self.drive_data.get_table('folders')

        for parent in parents:
            parent_id = parent[u'id']
            if parent_id in folders:
                return True
        return False

    def run(self):
        while True:
            query_download = self.update_queue.get()
            if query_download:
                self.retrieve_all_changes()
                self.update_queue.task_done()

    def download_folder(self, file_id, file_data):
        parents = file_data[u'parents']
        drive_data = self.drive_data
        #save_data = self.save_data
        folders = drive_data.get_table('folders')
        get_entry = drive_data.get_entry
        append_entry = drive_data.append_entry
        for parent in parents:
            parent_id = parent[u'id']
            if parent_id in folders:
                address = get_entry('folders', parent_id, 'file_add')
                folder_name = file_data[u'title']
                folder_add = address + folder_name + '/'
                self.ensure_dir(folder_add)
                append_entry('folders', file_id, 'file_add', folder_add)
                append_entry('tracked_items', file_id, 'file_add', folder_add)

    def place_address_in_queue(self, address, file_name, file_id,
                               time_since_epoch, download_url):
        drive_data = self.drive_data
        self.ensure_dir(address)
        file_add = address + file_name
        drive_data.append_entry('tracked_items', file_id, 'file_add', file_add)
        do_download = False
        try:
            mod_time = os.stat(file_add).st_mtime
            if time_since_epoch > mod_time:
                do_download = True
            else:
                do_download = False
        except:
            do_download = True
        if do_download:
            self.queue.put(
                (self._download, [download_url, file_add,
                                  time_since_epoch], {}))

    def download_file(self, file_id, file_data):
        mod_date = file_data[u'modifiedDate']
        parents = file_data[u'parents']
        time_since_epoch = self.calculate_time_since_epoch(mod_date)
        drive_data = self.drive_data
        folders = drive_data.get_table('folders')
        get_entry = drive_data.get_entry
        download_url = file_data[u'downloadUrl']
        file_name = file_data[u'title']
        place_address_in_queue = self.place_address_in_queue
        for parent in parents:
            parent_id = parent[u'id']
            if parent_id in folders:
                address = get_entry('folders', parent_id, 'file_add')
                if isinstance(address, list):
                    for add in address:
                        place_address_in_queue(add, file_name, file_id,
                                               time_since_epoch, download_url)
                else:
                    place_address_in_queue(address, file_name, file_id,
                                           time_since_epoch, download_url)

    def retrieve_all_changes(self):
        result = []
        page_token = None
        drive_data = self.drive_data
        largest_change = int(self.get_largest_change_id())
        try:
            start_change_id = str(
                int(drive_data.get_entry('changes', 'last_change', 'id')) + 1)
        except:
            start_change_id = str(int(self.start_sync_at))
        # if self.progress_tracker.start_change_id is None:
        #     self.progress_tracker.start_change_id = int(start_change_id)
        # self.progress_tracker.last_change_id = int(start_change_id)
        if largest_change >= int(start_change_id):
            print('we should parse')
            # Clock.schedule_once(partial(self.retrieve_page_of_changes, None,
            #     start_change_id))
            while True:
                param = {}
                print('grabbing changes')
                if start_change_id is not None:
                    param['startChangeId'] = start_change_id
                if page_token is not None:
                    param['pageToken'] = page_token
                changes = self.drive_service.changes().list(**param).execute()
                self.parse_changes(changes)
                #self.sync()
                page_token = changes.get('nextPageToken')
                if not page_token:
                    print('done breaking')
                    break

    def parse_changes(self, changes):
        drive_data = self.drive_data
        download_folder = self.download_folder
        download_file = self.download_file
        remove_file = self.remove_file
        tracked_items = drive_data.get_table('tracked_items')
        check_file_in_tracked_folders = self.check_file_in_tracked_folders
        get_is_folder = self.get_is_folder
        set_entry = drive_data.set_entry
        get_file = self.get_file
        if len(changes['items']) == 0:
            return None
        for change in changes['items']:
            file_id = change[u'fileId']
            change_id = change[u'id']
            file_data = get_file(file_id)
            is_deleted = change[u'deleted']
            if is_deleted and file_id in tracked_items:
                self.handle_remove_callback(file_id)
                remove_file(file_id)
            if file_data != None:
                if check_file_in_tracked_folders(file_data):
                    if get_is_folder(file_data):
                        download_folder(file_id, file_data)
                    else:
                        download_file(file_id, file_data)
            set_entry('changes', 'last_change', 'id', change_id)
        return change_id

    def handle_remove_callback(self, file_id):
        file_adds = self.drive_data.get_entry('tracked_items', file_id,
                                              'file_add')
        tracked_widgets = self.tracked_widgets
        if file_adds is not None:
            if isinstance(file_adds, list):
                for file_add in file_adds:
                    widgets_for_removal = []
                    widgets_a = widgets_for_removal.append
                    dir_name = str(dirname(file_add) + '/')
                    for wid in tracked_widgets[dir_name]:
                        r_wid = wid()
                        if r_wid is not None:
                            file_callback = r_wid.file_callback
                            Clock.schedule_once(
                                partial(file_callback, file_add, True))
                        else:
                            widgets_a(wid)
                    for wid_ref in widgets_for_removal:
                        tracked_widgets[file_adds].remove(wid_ref)

    def remove_file(self, file_id):
        drive_data = self.drive_data
        to_remove = []
        to_remove_a = to_remove.append
        remove_entry = drive_data.remove_entry
        file_adds = drive_data.get_entry('tracked_items', file_id, 'file_add')
        if file_adds is not None:
            for file_add in file_adds:
                try:
                    os.remove(file_add)
                    to_remove_a(file_add)
                except:
                    continue
            for add in to_remove:
                remove_entry('tracked_items', file_id, 'file_add', add)

    def ensure_dir(self, f):
        d = os.path.dirname(f)
        if not os.path.exists(d):
            os.makedirs(d)

    def calculate_time_since_epoch(self, drive_time):
        date, current_time = drive_time.split('T')
        year, month, day = date.split('-')
        hours, minutes, seconds = current_time.split(':')
        seconds = seconds.split('.')[0]
        py_date = datetime.datetime(int(year),
                                    int(month),
                                    int(day),
                                    hour=int(hours),
                                    minute=int(minutes),
                                    second=int(seconds))
        return time.mktime(py_date.timetuple())

    def get_is_folder(self, file_data):
        return file_data[u'mimeType'] == u'application/vnd.google-apps.folder'

    def get_file(self, fid):
        try:
            return self.drive_service.files().get(fileId=fid).execute()
        except:
            return None

    def list_folder_content(self, fid):
        return self.drive_service.children().list(folderId=fid).execute()

    def get_about_data(self):
        return self.drive_service.about().get().execute()

    def get_largest_change_id(self):
        return self.get_about_data()[u'largestChangeId']

    def get_content_title(self, fid):
        return self.drive_service.files().get(fileId=fid).execute()[u'title']

    def _download(self, queue, drive_service, download_url, name, time):
        if download_url:
            try:
                resp, content = drive_service._http.request(download_url)
            except:
                print 'An error occurred: '
                return
            if resp.status == 200:
                with open(name, 'w') as save_file:
                    save_file.write(content)
                os.utime(name, (os.stat(name).st_atime, time))
                folder_name = os.path.dirname(name) + '/'
                tracked_widgets = self.tracked_widgets
                widgets_for_removal = []
                widget_a = widgets_for_removal.append
                if folder_name in tracked_widgets:
                    for wid in tracked_widgets[folder_name]:
                        r_wid = wid()
                        if r_wid is not None:
                            Clock.schedule_once(
                                partial(r_wid.file_callback, name, False), 15.)
                        else:
                            widget_a(wid)
                    for wid_ref in widgets_for_removal:
                        tracked_widgets[folder_name].remove(wid_ref)
                queue.task_done()
            else:
                print 'An error occurred: %s' % resp
Exemple #2
0
class DriveMainThread(Thread):


    def __init__(self, folders_to_track, start_sync_at, pyfile, **kwargs):
        super(DriveMainThread, self).__init__(**kwargs)
        self.drive_data = DBInterface(os.path.dirname(
            os.path.abspath(pyfile))+'/drive_data/', 'drive_files')
        base_path = os.path.dirname(os.path.abspath(pyfile))+'/'
        self.updated_folders = set()
        self.start_sync_at = start_sync_at
        self.folders_to_track = folders_to_track
        self.setup_drive()
        self.update_queue = Queue.Queue()
        self.get_tracked_folder_ids( 
            folders_to_track, os.path.dirname(
            os.path.abspath(pyfile))+'/')
        self.tracked_widgets = {}
        self.folders_to_track = [
            base_path+path+'/' for path in folders_to_track]

    def add_drive_widget(self, widget, folder_to_track):
        assert(folder_to_track in self.folders_to_track)
        tracked_widgets = self.tracked_widgets
        if folder_to_track not in tracked_widgets:
            tracked_widgets[folder_to_track] = [widget]
        else:
            tracked_widgets[folder_to_track].append(widget)

    def setup_drive(self):
        credentials = storage.get()
        self.queue = queue = Queue.Queue()
        if credentials is None:
            flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE, 
                redirect_uri=REDIRECT_URI)
            authorize_url = flow.step1_get_authorize_url()
            print 'Go to the following link in your browser: ' + authorize_url
            code = raw_input('Enter verification code: ').strip()
            credentials = flow.step2_exchange(code)
            storage.put(credentials)
        http = httplib2.Http()
        http = credentials.authorize(http)
        self.drive_service = drive_service = build('drive', 'v2', http=http)
        self.thread_pool = thread_pool = DriveThreadPool(1, queue, 
            credentials)

    def get_tracked_folder_ids(self, folders_to_track, address):
        drive_data = self.drive_data
        for folder_name in self.folders_to_track:
            q="title = '{}'".format(folder_name)
            data = self.drive_service.children().list(
                folderId='root', q=q).execute()
            folder_add = address+folder_name+'/'
            self.ensure_dir(folder_add)
            #self.add_folder_observer(self.get_callback, unicode(folder_add))
            for dat in data[u'items']:
                item_id = dat[u'id']
                drive_data.set_entry(
                    'folders', item_id, 'file_add', folder_add)
                drive_data.set_entry(
                    'tracked_items', item_id, 'file_add', folder_add)
    

    def check_file_in_tracked_folders(self, file_data):
        parents = file_data[u'parents']
        folders = self.drive_data.get_table('folders')

        for parent in parents:
            parent_id = parent[u'id']
            if parent_id in folders:
                return True
        return False

    def run(self):
        while True:
            query_download = self.update_queue.get()
            if query_download:
                self.retrieve_all_changes()
                self.update_queue.task_done()
       

    def download_folder(self, file_id, file_data):
        parents = file_data[u'parents']
        drive_data = self.drive_data
        #save_data = self.save_data
        folders = drive_data.get_table('folders')
        get_entry = drive_data.get_entry
        append_entry = drive_data.append_entry
        for parent in parents:
            parent_id = parent[u'id']
            if parent_id in folders:
                address = get_entry('folders', parent_id, 'file_add')
                folder_name = file_data[u'title']
                folder_add = address+folder_name+'/'
                self.ensure_dir(folder_add)
                append_entry('folders', file_id, 'file_add', folder_add)
                append_entry('tracked_items', file_id, 'file_add', folder_add)

    def place_address_in_queue(self, address, file_name, file_id, 
        time_since_epoch, download_url):
        drive_data = self.drive_data
        self.ensure_dir(address)
        file_add = address+file_name
        drive_data.append_entry('tracked_items', file_id, 'file_add', 
            file_add)
        do_download = False
        try:
            mod_time = os.stat(file_add).st_mtime
            if time_since_epoch > mod_time:
                do_download = True
            else:
                do_download = False
        except:
            do_download = True
        if do_download:
            self.queue.put((self._download, [download_url, file_add, 
                time_since_epoch], {}))

    def download_file(self, file_id, file_data):
        mod_date = file_data[u'modifiedDate']
        parents = file_data[u'parents']
        time_since_epoch = self.calculate_time_since_epoch(mod_date)
        drive_data = self.drive_data
        folders = drive_data.get_table('folders')
        get_entry = drive_data.get_entry
        download_url = file_data[u'downloadUrl']
        file_name = file_data[u'title']
        place_address_in_queue = self.place_address_in_queue
        for parent in parents:
            parent_id = parent[u'id']
            if parent_id in folders:
                address = get_entry('folders', parent_id, 'file_add')
                if isinstance(address, list):
                    for add in address:
                        place_address_in_queue(add, file_name, file_id,
                            time_since_epoch, download_url)
                else:
                    place_address_in_queue(address, file_name, file_id,
                            time_since_epoch, download_url)

    def retrieve_all_changes(self):
        result = []
        page_token = None
        drive_data = self.drive_data
        largest_change = int(self.get_largest_change_id())
        try:
            start_change_id = str(int(
                drive_data.get_entry('changes', 'last_change', 'id')) + 1)
        except:
            start_change_id = str(int(self.start_sync_at))
        # if self.progress_tracker.start_change_id is None:
        #     self.progress_tracker.start_change_id = int(start_change_id)
        # self.progress_tracker.last_change_id = int(start_change_id)
        if largest_change >= int(start_change_id):
            print('we should parse')
            # Clock.schedule_once(partial(self.retrieve_page_of_changes, None, 
            #     start_change_id))
            while True:
                param = {}
                print('grabbing changes')
                if start_change_id is not None:
                    param['startChangeId'] = start_change_id
                if page_token is not None:
                    param['pageToken'] = page_token
                changes = self.drive_service.changes().list(**param).execute()
                self.parse_changes(changes)
                #self.sync()
                page_token = changes.get('nextPageToken')
                if not page_token:
                    print('done breaking')
                    break

    def parse_changes(self, changes):
        drive_data = self.drive_data
        download_folder = self.download_folder
        download_file = self.download_file
        remove_file = self.remove_file
        tracked_items = drive_data.get_table('tracked_items')
        check_file_in_tracked_folders = self.check_file_in_tracked_folders
        get_is_folder = self.get_is_folder
        set_entry = drive_data.set_entry
        get_file = self.get_file
        if len(changes['items']) == 0:
            return None
        for change in changes['items']:
            file_id = change[u'fileId']
            change_id = change[u'id']
            file_data = get_file(file_id)
            is_deleted = change[u'deleted']
            if is_deleted and file_id in tracked_items:
                self.handle_remove_callback(file_id)
                remove_file(file_id)
            if file_data != None:
                if check_file_in_tracked_folders(file_data):
                    if get_is_folder(file_data):
                        download_folder(file_id, file_data)
                    else:
                        download_file(file_id, file_data)
            set_entry('changes', 'last_change', 'id', change_id)
        return change_id


    def handle_remove_callback(self, file_id):
        file_adds = self.drive_data.get_entry(
            'tracked_items', file_id, 'file_add')
        tracked_widgets = self.tracked_widgets
        if file_adds is not None:
            if isinstance(file_adds, list):
                for file_add in file_adds:
                    widgets_for_removal = []
                    widgets_a = widgets_for_removal.append
                    dir_name = str(dirname(file_add) + '/')
                    for wid in tracked_widgets[dir_name]:
                        r_wid = wid()
                        if r_wid is not None:
                            file_callback = r_wid.file_callback
                            Clock.schedule_once(partial(file_callback, 
                                file_add, True))
                        else:
                            widgets_a(wid)
                    for wid_ref in widgets_for_removal:
                        tracked_widgets[file_adds].remove(wid_ref)

    def remove_file(self, file_id):
        drive_data = self.drive_data
        to_remove = []
        to_remove_a = to_remove.append
        remove_entry = drive_data.remove_entry
        file_adds = drive_data.get_entry('tracked_items', file_id, 'file_add')
        if file_adds is not None:
            for file_add in file_adds:
                try:
                    os.remove(file_add)
                    to_remove_a(file_add)
                except:
                    continue
            for add in to_remove:
                remove_entry('tracked_items', file_id, 'file_add', add)

    def ensure_dir(self, f):
        d = os.path.dirname(f)
        if not os.path.exists(d):
            os.makedirs(d)

    def calculate_time_since_epoch(self, drive_time):
        date, current_time = drive_time.split('T')
        year, month, day = date.split('-')
        hours, minutes, seconds = current_time.split(':')
        seconds = seconds.split('.')[0]
        py_date = datetime.datetime(int(year), int(month), int(day), 
            hour=int(hours), minute=int(minutes), 
            second=int(seconds))
        return time.mktime(py_date.timetuple())

    def get_is_folder(self, file_data):
        return file_data[u'mimeType'] == u'application/vnd.google-apps.folder'

    def get_file(self, fid):
        try:
            return self.drive_service.files().get(fileId=fid).execute()
        except:
            return None

    def list_folder_content(self, fid):
        return self.drive_service.children().list(folderId=fid).execute()

    def get_about_data(self):
        return self.drive_service.about().get().execute()

    def get_largest_change_id(self):
        return self.get_about_data()[u'largestChangeId']
    
    def get_content_title(self, fid):
        return self.drive_service.files().get(fileId=fid).execute()[u'title']

    def _download(self, queue, drive_service, download_url, name, time):
        if download_url:
            try:
                resp, content = drive_service._http.request(download_url)
            except:
                print 'An error occurred: '
                return
            if resp.status == 200:
                with open(name, 'w') as save_file:
                    save_file.write(content)
                os.utime(name, (os.stat(name).st_atime, 
                        time))
                folder_name = os.path.dirname(name) + '/'
                tracked_widgets = self.tracked_widgets
                widgets_for_removal = []
                widget_a = widgets_for_removal.append
                if folder_name in tracked_widgets:
                    for wid in tracked_widgets[folder_name]:
                        r_wid = wid()
                        if r_wid is not None:
                            Clock.schedule_once(partial(r_wid.file_callback,
                                name, False), 15.)
                        else:
                            widget_a(wid)
                    for wid_ref in widgets_for_removal:
                        tracked_widgets[folder_name].remove(wid_ref)
                queue.task_done()
            else:
                print 'An error occurred: %s' % resp