def test_batch_error (self): fobj = self.find_group(self.gid) con0 = GCContact(fobj) con0.set_firstname('Namo Narayananaya') gce0 = con0.get_gce() con = GCContact(fobj) con.set_firstname('Ayeshwarya') con.set_birthday('abcd"ef') # con.set_anniv('1978-05-31 %s est n il y a %d ans') # con.set_birthday('1980-08-10') gce = con.get_gce() feed = self.pimdb.new_feed() feed.add_insert(entry=gce0, batch_id_string="DeadBeef") feed.add_insert(entry=gce0, batch_id_string="DeadBeef") feed.add_insert(entry=gce, batch_id_string="DeadBeef") b = BatchState(1, feed, op='insert', sync_tag="asynk:testgcex:ex") print 'Request: ', utils.pretty_xml(str(feed)) rr = self.pimdb.exec_batch(feed) print 'Response: ', utils.pretty_xml(str(rr)) for entry in rr.entry: print entry.batch_status if entry.batch_status: print 'Code: ',entry.batch_status.code print 'Reason: ', entry.batch_status.reason else: self.handle_interrupted_feed(feed, str(rr))
def new_item(self, item): """Add the specified item to the folder.""" if item.__class__.__name__ == 'GCContact': con = item else: con = GCContact(self, con=item) eid = con.save() return eid
def new_item (self, item): """Add the specified item to the folder.""" if item.__class__.__name__ == 'GCContact': con = item else: con = GCContact(self, con=item) eid = con.save() return eid
def test_create_contact(self, f=None): if not f: f = self.gout c = GCContact(f) c.set_name("ScrewBall Joseph") cid = c.save() if cid: print "Successfully added contact. ID: ", cid else: print "D" "oh. Failed."
def test_create_contact(self, f=None): if not f: f = self.gout c = GCContact(f) c.set_name("ScrewBall Joseph") cid = c.save() if cid: print 'Successfully added contact. ID: ', cid else: print 'D' 'oh. Failed.'
def batch_create(self, src_sl, src_dbid, items): """See the documentation in folder.Folder""" my_dbid = self.get_dbid() c = self.get_config() pname = src_sl.get_pname() src_sync_tag = c.make_sync_label(src_sl.get_pname(), src_dbid) dst_sync_tag = c.make_sync_label(src_sl.get_pname(), my_dbid) f = self.get_db().new_feed() stats = BatchState(1, f, 'insert', sync_tag=dst_sync_tag) success = True for item in items: con_itemid = item.get_itemid_from_synctags(pname, 'gc') gc = GCContact(self, con=item, con_itemid=con_itemid) bid = item.get_itemid() gc.update_sync_tags(src_sync_tag, bid) gce = gc.get_gce() stats.add_con(bid, new=gc, orig=item) f.add_insert(entry=gce, batch_id_string=bid) stats.incr_cnt() if stats.get_cnt() % self.get_batch_size() == 0: # Feeds have to be less than 1MB. We can push this some # more. FIXME. logging.debug( 'Uploading new batch # %02d to Google. ' + 'Count: %3d. Size: %6.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) succ, cons = stats.process_batch_response(rf) success = success and succ f = self.get_db().new_feed() stats = BatchState(stats.get_bnum() + 1, f, 'insert', sync_tag=dst_sync_tag) # Upload any leftovers if stats.get_cnt() > 0: logging.debug('New Batch # %02d. Count: %3d. Size: %5.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) succ, cons = stats.process_batch_response(rf) success = success and succ return success
def batch_create (self, src_sl, src_dbid, items): """See the documentation in folder.Folder""" my_dbid = self.get_dbid() c = self.get_config() pname = src_sl.get_pname() src_sync_tag = c.make_sync_label(src_sl.get_pname(), src_dbid) dst_sync_tag = c.make_sync_label(src_sl.get_pname(), my_dbid) f = self.get_db().new_feed() stats = BatchState(1, f, 'insert', sync_tag=dst_sync_tag) success = True for item in items: con_itemid = item.get_itemid_from_synctags(pname, 'gc') gc = GCContact(self, con=item, con_itemid=con_itemid) bid = item.get_itemid() gc.update_sync_tags(src_sync_tag, bid) gce = gc.get_gce() stats.add_con(bid, new=gc, orig=item) f.add_insert(entry=gce, batch_id_string=bid) stats.incr_cnt() if stats.get_cnt() % self.get_batch_size() == 0: # Feeds have to be less than 1MB. We can push this some # more. FIXME. logging.debug('Uploading new batch # %02d to Google. ' + 'Count: %3d. Size: %6.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) succ, cons = stats.process_batch_response(rf) success = success and succ f = self.get_db().new_feed() stats = BatchState(stats.get_bnum()+1, f, 'insert', sync_tag=dst_sync_tag) # Upload any leftovers if stats.get_cnt() > 0: logging.debug('New Batch # %02d. Count: %3d. Size: %5.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) succ, cons = stats.process_batch_response(rf) success = success and succ return success
def test_print_item(self, gcid): from contact_gc import GCContact f = self.find_group(self.gid) gce = f.get_gdc().GetContact(gcid) g = GCContact(f, gce=gce) print g
def find_items (self, itemids): """See documentation in folder.Folder""" ## Note that it is more efficient to do a single call to fetch all the ## entire and then build GCContact objects, than call find_item ## iteratively... res, ces = self._fetch_gc_entries(itemids) ret = [GCContact(self, gce=ce) for ce in ces] return ret
def _fetch_gc_entries (self, gcids): """gcids is a list of google contact ids to retrieve contact entries for. Returns a list of ContactEntries""" f = self.get_db().new_feed() stats = BatchState(1, f, 'query', sync_tag=None) ret = [] success = True for gcid in gcids: gcid = GCContact.normalize_gcid(gcid) ce = gdata.contacts.data.ContactEntry() ce.id = atom.data.Id(text=gcid) stats.add_con(gcid, ce, orig=None) f.add_query(entry=ce, batch_id_string=gcid) stats.incr_cnt() if stats.get_cnt() % self.get_batch_size() == 0: # Feeds have to be less than 1MB. We can push this some # more logging.debug('Qry Batch # %02d. Count: %3d. Size: %6.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) suc, ces = stats.process_batch_response(rf) success = success and suc [ret.append(x) for x in ces] f = self.get_db().new_feed() s = BatchState(stats.get_bnum()+1, f, 'query', sync_tag=None) stats = s # Process any leftovers if stats.get_cnt() > 0: logging.debug('Qry Batch # %02d. Count: %3d. Size: %5.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) suc, ces = stats.process_batch_response(rf) success = success and suc [ret.append(x) for x in ces] return success, ret
def get_folder_contacts(self, f, cnt=0): """A thought out version of this routine will eventually go as a method of GCFolder class..""" logging.info('Querying Google for status of Contact Entries...') updated_min = f.get_config().get_last_sync_stop('gc', 'ol') feed = f._get_group_feed(updated_min=updated_min, showdeleted='false') logging.info('Response recieved from Google. Processing...') if not feed.entry: logging.info('No entries in feed.') return contacts = [] for i, entry in enumerate(feed.entry): c = GCContact(f, gce=entry) contacts.append(c) return contacts
else: ## We could just print a more detailed error for all ## cases. Should do some time FIXME. logging.error('Sync failed for bid %s: %s: %s', bid, err_str, entry.id) else: if op == 'query': con = entry # We could build and return array for all cases, but # why waste memory... cons.append(con) elif op in ['insert', 'update']: con = self.get_con(bid) orig = self.get_orig(bid) gcid = utils.get_link_rel(entry.link, 'edit') gcid = GCContact.normalize_gcid(gcid) orig.update_sync_tags(self.sync_tag, gcid) cons.append(orig) t = None if op == 'insert': t = 'created' elif op == 'update': t = 'updated' if t: logging.info('Successfully %s gmail entry for %30s (%s)', t, con.get_name(), orig.get_itemid()) return success, cons
def prep_sync_lists (self, destid, sl, updated_min=None, cnt=0): """See the documentation in folder.Folder""" pname = sl.get_pname() conf = self.get_config() logging.info('Querying Google for status of Contact Entries...') stag = conf.make_sync_label(pname, destid) ## Sort the DBIds so dest1 has the 'lower' ID db1 = self.get_db().get_dbid() if db1 > destid: db2 = db1 db1 = destid else: db2 = destid if not updated_min: updated_min = conf.get_last_sync_stop(pname) feed = self._get_group_feed(updated_min=updated_min, showdeleted='false') logging.info('Response recieved from Google. Processing...') if not feed.entry: logging.info('No entries in feed.') return skip = 0 etag_cnt = 0 for i, entry in enumerate(feed.entry): gcid = utils.get_link_rel(entry.link, 'edit') gcid = GCContact.normalize_gcid(gcid) olid = get_udp_by_key(entry.user_defined_field, stag) etag = entry.etag epd = entry.deleted name = None if entry.name.full_name: name = entry.name.full_name.text elif entry.name.family_name: name = entry.name.family_name.text elif entry.name.given_name: name = entry.name.given_name.text if epd: if olid: sl.add_del(gcid, olid) else: # Deleted before it got synched. Get on with life skip += 1 continue else: if olid: logging.debug('Modified Google Contact: %20s %s', name, gcid) sl.add_mod(gcid, olid) else: logging.debug('New Google Contact: %20s %s', name, gcid) sl.add_new(gcid) if etag: sl.add_etag(gcid, etag) etag_cnt += 1 else: sl.add_entry(gcid) logging.debug('num with etags : %5d', etag_cnt) logging.debug('num del bef sync : %5d', skip) logging.info('Note: Stats for Google Contacts are only for the ' 'changeset since the last synk. In particular the total ' 'count is NOT the total number of contacts in your folder!')
def create_gc_contact (asynk, uinps): gc = asynk.get_db('gc') gcfid = uinps.gcfid gcf, t = gc.find_folder(gcfid) con = GCContact(gcf) con.set_name('Sri Venkata Sri Rama Subramanya Anjeneya Annapurna Sharma') con.set_prefix('Mr.') con.set_nickname('Karra') # con.set_gender('Male') con.add_phone_mob(('Mobile', '+91 90084 88997')) con.add_notes('And so it goes...') # FIXME: We should do a more exhaustive sort of contact, with multiple # entries of each type of possible entry and so on... return con.save()
def _refresh_contacts (self): feed = self._get_group_feed() for gce in feed.entry: gc = GCContact(self, gce=gce) self.add_contact(gc)
def batch_update (self, sync_list, src_dbid, items): """See the documentation in folder.Folder""" # Updates and deletes on google require not just the entryid but also # its correct etag which is a version identifier. This is to ensure # two apps do not overwrite each other's work without even knowing # about it. So we need to approach this in two steps: (a) Fetch the # ContactEntries for all the items we are interested in. the returned # entry objects have all the required info, including the latest # etag. (b) Modify the same entry with the local updates and send it # back my_dbid = self.get_dbid() c = self.get_config() pname = sync_list.get_pname() src_sync_tag = c.make_sync_label(pname, src_dbid) dst_sync_tag = c.make_sync_label(pname, my_dbid) tags = [item.get_sync_tags(dst_sync_tag)[0] for item in items] gcids = [val for (tag, val) in tags] logging.debug('Refreshing etags for modified entries...') success, ces = self._fetch_gc_entries(gcids) etags = [copy.deepcopy(ce.etag) for ce in ces] f = self.get_db().new_feed() stats = BatchState(1, f, 'update', sync_tag=dst_sync_tag) for item, etag in zip(items, etags): gc = GCContact(self, con=item) bid = item.get_itemid() gc.update_sync_tags(src_sync_tag, bid) gce = gc.get_gce() gce.etag = etag stats.add_con(bid, new=gc, orig=item) f.add_update(entry=gce, batch_id_string=bid) stats.incr_cnt() if stats.get_cnt() % self.get_batch_size() == 0: # Feeds have to be less than 1MB. We can push this some # more. FIXME. logging.debug('Uploading mod batch # %02d to Google. ' + 'Count: %3d. Size: %6.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) succ, cons = stats.process_batch_response(rf) success = success and succ f = self.get_db().new_feed() stats = BatchState(stats.get_bnum()+1, f, 'update', sync_tag=dst_sync_tag) # Upload any leftovers if stats.get_cnt() > 0: logging.debug('Mod Batch # %02d. Count: %3d. Size: %5.2fK', stats.get_bnum(), stats.get_cnt(), stats.get_size()) rf = self.get_db().exec_batch(f) succ, cons = stats.process_batch_response(rf) success = success and succ return success
def process_batch_response(self, resp): """resp is the response feed obtained from a batch operation to google. This routine will walk through the batch response entries, and make note in the outlook database for succesful sync, or handle errors appropriately. Returns a tuple (success, cons) where success is a boolean to know if all the entries had successful operation, and an array of contact items from the batch operation""" op = self.get_operation() cons = [] success = True for entry in resp.entry: bid = entry.batch_id.text if entry.batch_id else None if not entry.batch_status: # There is something seriously wrong with this request. self.handle_interrupted_feed(str(resp)) success = False continue code = int(entry.batch_status.code) reason = entry.batch_status.reason if code != SYNC_OK and code != SYNC_CREATED: # FIXME this code path needs to be tested properly err = sync_status_str(code) err_str = '' if err is None else ('Code: %s' % err) err_str = 'Reason: %s. %s' % (reason, err_str) success = False if op == 'insert' or op == 'update': try: name = self.get_con(bid).get_disp_name() except Exception, e: name = "WTH!" logging.error('Upload to Google failed for: %s: %s', name, err_str) elif op == 'Writeback olid': logging.error('Could not complete sync for: %s: %s: %s', bid, err_str, entry.id) else: ## We could just print a more detailed error for all ## cases. Should do some time FIXME. logging.error('Sync failed for bid %s: %s: %s', bid, err_str, entry.id) else: if op == 'query': con = entry # We could build and return array for all cases, but # why waste memory... cons.append(con) elif op in ['insert', 'update']: con = self.get_con(bid) orig = self.get_orig(bid) gcid = utils.get_link_rel(entry.link, 'edit') gcid = GCContact.normalize_gcid(gcid) orig.update_sync_tags(self.sync_tag, gcid) cons.append(orig) t = None if op == 'insert': t = 'created' elif op == 'update': t = 'updated' if t: logging.info( 'Successfully %s gmail entry for %30s (%s)', t, con.get_disp_name(), orig.get_itemid())
def process_batch_response (self, resp): """resp is the response feed obtained from a batch operation to google. This routine will walk through the batch response entries, and make note in the outlook database for succesful sync, or handle errors appropriately. Returns a tuple (success, cons) where success is a boolean to know if all the entries had successful operation, and an array of contact items from the batch operation""" op = self.get_operation() cons = [] success = True for entry in resp.entry: bid = entry.batch_id.text if entry.batch_id else None if not entry.batch_status: # There is something seriously wrong with this request. self.handle_interrupted_feed(str(resp)) success = False continue code = int(entry.batch_status.code) reason = entry.batch_status.reason if code != SYNC_OK and code != SYNC_CREATED: # FIXME this code path needs to be tested properly err = sync_status_str(code) err_str = '' if err is None else ('Code: %s' % err) err_str = 'Reason: %s. %s' % (reason, err_str) success = False if op == 'insert' or op == 'update': try: name = self.get_con(bid).get_disp_name() except Exception, e: name = "WTH!" logging.error('Upload to Google failed for: %s: %s', name, err_str) elif op == 'Writeback olid': logging.error('Could not complete sync for: %s: %s: %s', bid, err_str, entry.id) else: ## We could just print a more detailed error for all ## cases. Should do some time FIXME. logging.error('Sync failed for bid %s: %s: %s', bid, err_str, entry.id) else: if op == 'query': con = entry # We could build and return array for all cases, but # why waste memory... cons.append(con) elif op in ['insert', 'update']: con = self.get_con(bid) orig = self.get_orig(bid) gcid = utils.get_link_rel(entry.link, 'edit') gcid = GCContact.normalize_gcid(gcid) orig.update_sync_tags(self.sync_tag, gcid) cons.append(orig) t = None if op == 'insert': t = 'created' elif op == 'update': t = 'updated' if t: logging.info('Successfully %s gmail entry for %30s (%s)', t, con.get_disp_name(), orig.get_itemid())
def test_batch_error(self): fobj = self.find_group(self.gid) con0 = GCContact(fobj) con0.set_firstname('Namo Narayananaya') gce0 = con0.get_gce() con = GCContact(fobj) con.set_firstname('Ayeshwarya') con.set_birthday('abcd"ef') # con.set_anniv('1978-05-31 %s est n il y a %d ans') # con.set_birthday('1980-08-10') gce = con.get_gce() feed = self.pimdb.new_feed() feed.add_insert(entry=gce0, batch_id_string="DeadBeef") feed.add_insert(entry=gce0, batch_id_string="DeadBeef") feed.add_insert(entry=gce, batch_id_string="DeadBeef") b = BatchState(1, feed, op='insert', sync_tag="asynk:testgcex:ex") print 'Request: ', utils.pretty_xml(str(feed)) rr = self.pimdb.exec_batch(feed) print 'Response: ', utils.pretty_xml(str(rr)) for entry in rr.entry: print entry.batch_status if entry.batch_status: print 'Code: ', entry.batch_status.code print 'Reason: ', entry.batch_status.reason else: self.handle_interrupted_feed(feed, str(rr))
def prep_sync_lists (self, destid, sl, updated_min=None, cnt=0): """See the documentation in folder.Folder""" pname = sl.get_pname() conf = self.get_config() pdb1id = conf.get_profile_db1(pname) oldi = conf.get_itemids(pname) newi = self.get_itemids(pname, destid) kss = newi.keys() for x, y in oldi.iteritems(): if not x in kss and not y in kss: logging.debug('Del Google Contact: %s:%s', x, y) if pdb1id == self.get_dbid(): sl.add_del(x, y) else: sl.add_del(y,x) logging.info('Querying Google for status of Contact Entries...') stag = conf.make_sync_label(pname, destid) ## FIXME: The following commented out code appears very fishy. I am ## not able to recall why these two have to be used in sorted order. I ## am pretty sure there was some sense behind it, but as of now db1 ## and db2 are not really being used; so the code works even without ## this "sorted" behaviour... Hm, this really should go, but I am ## being conservative here and leving the stuff commented out so we ## can come back to it later if required. # ## Sort the DBIds so dest1 has the 'lower' ID # db1 = self.get_db().get_dbid() # if db1 > destid: # db2 = db1 # db1 = destid # else: # db2 = destid if not updated_min: updated_min = conf.get_last_sync_stop(pname) # FIXME: We are fetching the group feed a second time. Ideally we # shoul dbe able to everything we want with the feed already fetched # above. This has a performance implication for groups with a large # number of items. Will fix this once functionality is validated. feed = self._get_group_feed(updated_min=updated_min, showdeleted='false') logging.info('Response recieved from Google. Processing...') if not feed.entry: logging.info('No entries in feed.') for x in kss: sl.add_unmod(x) return skip = 0 etag_cnt = 0 for i, entry in enumerate(feed.entry): gcid = utils.get_link_rel(entry.link, 'edit') gcid = GCContact.normalize_gcid(gcid) olid = get_udp_by_key(entry.user_defined_field, stag) etag = entry.etag epd = entry.deleted name = None if entry.name: if entry.name.full_name: name = entry.name.full_name.text elif entry.name.family_name: name = entry.name.family_name.text elif entry.name.given_name: name = entry.name.given_name.text if epd: if olid: pass # We will trust our own delete logic... # sl.add_del(gcid) else: # Deleted before it got synched. Get on with life skip += 1 continue else: if olid: logging.debug('Modified Google Contact: %20s %s', name, gcid) sl.add_mod(gcid, olid) else: logging.debug('New Google Contact: %20s %s', name, gcid) sl.add_new(gcid) if etag: sl.add_etag(gcid, etag) etag_cnt += 1 else: sl.add_entry(gcid) for x in kss: if not x in sl.get_news() and not x in sl.get_mods(): sl.add_unmod(x) logging.debug('Total Contacts : %5d', len(newi)) logging.debug('num with etags : %5d', etag_cnt) logging.debug('num del bef sync : %5d', skip)
def find_item (self, itemid): gce = self.get_gdc().GetContact(itemid) gc = GCContact(self, gce=gce) return gc