<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -62,6 +62,40 @@ def NumberSuffixesMatch(num1, num2):
   return num1[-7:] == num2[-7:]
 
 
+def PhoneNumberListContainsNumber(number_list, number):
+  &quot;&quot;&quot;Searches number_list for number.  Returns True if found.
+
+  Args:
+    number_list: list of gdata PhoneNumber
+    number: string phone number.
+    
+  Returns: bool.
+  &quot;&quot;&quot;
+  for phone_number in number_list:
+    google_number = phone_number.text
+    if NumberSuffixesMatch(google_number, number):
+      return True
+  return False
+
+
+def GroupListContainsGroup(group_list, group):
+  &quot;&quot;&quot;Returns true if group found in group_list.
+
+  Args:
+    group_list: array of GroupMembershipInfo
+    group: a GroupMembershipInfo
+    
+  Returns: bool.
+  &quot;&quot;&quot;
+  assert group
+
+  sought_href = group.href
+  for potential_match in group_list:
+    if potential_match.href == sought_href:
+      return True
+  return False
+
+
 def FindEntryToMergeInto(contact, feed):
   &quot;&quot;&quot;Finds Entry (or None) in feed to merge contact into.&quot;&quot;&quot;
   contact_name = contact[&quot;name&quot;]
@@ -70,12 +104,11 @@ def FindEntryToMergeInto(contact, feed):
        entry.title.text == contact_name:
       return entry
       
-    for phone_number in entry.phone_number:
-      google_number = phone_number.text
-      for number_rec in contact[&quot;numbers&quot;]:
-        contact_number = number_rec[&quot;number&quot;]
-        if NumberSuffixesMatch(google_number, contact_number):
-          return entry
+    for number_rec in contact[&quot;numbers&quot;]:
+      contact_number = number_rec[&quot;number&quot;]
+      if PhoneNumberListContainsNumber(entry.phone_number,
+                                       contact_number):
+        return entry
 
   return None
 
@@ -92,21 +125,52 @@ def PhoneRelType(text):
 
 
 def NewContactEntry(contact, group=None):
-  &quot;&quot;&quot;Make a new GData Contact Entry from a submitted contact dict.&quot;&quot;&quot;
+  &quot;&quot;&quot;Make a new GData Contact Entry from a submitted contact dict.
+
+  Returns: The new, populated cgdata.contacts.ContactEntry object.
+  &quot;&quot;&quot;
   new_entry = gdata.contacts.ContactEntry()
+  UpdateContactEntry(new_entry, contact, group)
+  return new_entry
+
+
+def UpdateContactEntry(merge_entry, contact, group=None):
+  &quot;&quot;&quot;Merge user-submitted contact data into GData entry.
+
+  Args:
+    merge_entry: The gdata.contacts.ContactEntry() object to
+      merge into:
+    contact: Input contact dictionary from user w/ fields
+    group: optional GroupMembershipInfo to put user in
+
+  Returns: List of changes (terse English each).  If no changes,
+    returns the empty list, which is false in boolean context.
+  &quot;&quot;&quot;
+  changes = []
+
   if contact[&quot;name&quot;]:
-    new_entry.title = atom.Title(text=contact[&quot;name&quot;])
+    # TODO: promote short names (e.g. &quot;Brad&quot; or &quot;Brad F.&quot;) to
+    # full names (&quot;Brad Fitzpatrick&quot;).  For now, never modify
+    # the name.
+    if not merge_entry.title or not merge_entry.title.text:
+      changes.append(&quot;set name: %s&quot; % contact[&quot;name&quot;])
+      merge_entry.title = atom.Title(text=contact[&quot;name&quot;])
 
   for number_rec in contact[&quot;numbers&quot;]:
-    new_entry.phone_number.append(gdata.contacts.PhoneNumber(
-        rel=PhoneRelType(number_rec[&quot;type&quot;]),
-        text=number_rec[&quot;number&quot;]))
-
-  if group:
-    new_entry.group_membership_info.append(group)
+    if not PhoneNumberListContainsNumber(merge_entry.phone_number,
+                                         number_rec[&quot;number&quot;]):
+      changes.append(&quot;adding number: %s&quot; % number_rec[&quot;number&quot;])
+      merge_entry.phone_number.append(gdata.contacts.PhoneNumber(
+          rel=PhoneRelType(number_rec[&quot;type&quot;]),
+          text=number_rec[&quot;number&quot;]))
 
-  return new_entry
+  if group and not GroupListContainsGroup(merge_entry.group_membership_info,
+                                          group):
+    changes.append(&quot;adding to group.&quot;)
+    merge_entry.group_membership_info.append(group)
 
+  return changes
+  
 
 class Updater(object):
   &quot;&quot;&quot;Queues up updates and flushes them to gdata batch as needed.&quot;&quot;&quot;
@@ -266,7 +330,8 @@ class MergeGoogle(webapp.RequestHandler):
 
     # Process Groups
     groups_feed = client.Get(&quot;http://www.google.com/m8/feeds/groups/default/full&quot;)
-    groups_feed = gdata.contacts.GroupsFeedFromString(str(groups_feed))
+    groups_feed = gdata.contacts.GroupsFeedFromString(str(groups_feed))  # TODO: unicode safety
+    #  TODO: something like: groups_feed.ToString().decode(&quot;utf-8&quot;) ??
     group_name = {}  # id -&gt; name
     group_id = {}    # name -&gt; id
     for group in groups_feed.entry:
@@ -292,11 +357,6 @@ class MergeGoogle(webapp.RequestHandler):
 
     updater = Updater(client=client);
 
-      #new_entry.email.append(gdata.contacts.Email(
-      #    rel='http://schemas.google.com/g/2005#work', 
-      #    address='TESTTEST@gmail.com'))
-      #new_entry.content = atom.Content(text='Test Notes')
-
     group = gdata.contacts.GroupMembershipInfo(href=unicode(group_id[dest_group_name]))
 
     for contact in contacts:
@@ -306,10 +366,17 @@ class MergeGoogle(webapp.RequestHandler):
         out(&quot;&lt;p&gt;&lt;b&gt;%s&lt;/b&gt; %s&lt;/p&gt;&quot; % (number[&quot;type&quot;], number[&quot;number&quot;]))
       merge_entry = FindEntryToMergeInto(contact, feed)
       if merge_entry:
-        out(&quot;&lt;p&gt;&lt;b&gt;Action: merge into: &lt;/b&gt; %s&lt;/p&gt;&quot; % cgi.escape(
-            merge_entry.ToString().decode(&quot;utf-8&quot;)))
-        #UpdateContactEntry(merge_entry, contact)
-        #updater.AddUpdate(merge_entry)
+        changes = UpdateContactEntry(merge_entry, contact, group=group)
+        if changes:
+          out(&quot;&lt;p&gt;&lt;b&gt;Action: merge&lt;/b&gt; into existing Google contact, &lt;i&gt;%s&lt;/i&gt;&lt;/p&gt;&quot; % cgi.escape(
+              merge_entry.title.text.decode(&quot;utf-8&quot;)))
+          out(&quot;&lt;p&gt;&lt;b&gt;Applying changes: &lt;/b&gt;&lt;ul&gt;&quot;)
+          for c in changes:
+            out(&quot;&lt;li&gt;%s&lt;/li&gt;&quot; % cgi.escape(c))
+          out(&quot;&lt;/ul&gt;&quot;)
+          updater.AddUpdate(merge_entry)
+        else:
+          out(&quot;&lt;p&gt;&lt;b&gt;Action: no changes&lt;/b&gt;, record already up-to-date.&lt;/p&gt;&quot;)
       else:
         out(&quot;&lt;p&gt;&lt;b&gt;Action: new Google Contact&lt;/b&gt;&quot;)
         updater.AddInsert(NewContactEntry(contact, group=group))</diff>
      <filename>addressbooker.py</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>15176620b8ed7e151579235c99850b41a852860b</id>
    </parent>
  </parents>
  <author>
    <name>Brad Fitzpatrick</name>
    <email>brad@danga.com</email>
  </author>
  <url>http://github.com/bradfitz/addressbooker/commit/68e2b7b84a6fd72ff31aca000e0d0014726c4856</url>
  <id>68e2b7b84a6fd72ff31aca000e0d0014726c4856</id>
  <committed-date>2008-11-30T17:23:04-08:00</committed-date>
  <authored-date>2008-11-30T17:23:04-08:00</authored-date>
  <message>proper updating</message>
  <tree>e54c6901779c3abf71385fa97b2486f0419ead84</tree>
  <committer>
    <name>Brad Fitzpatrick</name>
    <email>brad@danga.com</email>
  </committer>
</commit>
