feat: push contacts to google contacts

This commit is contained in:
Himanshu Warekar 2019-09-06 13:56:00 +05:30
parent 0c86588bf4
commit 6de3e9a1bf
4 changed files with 145 additions and 25 deletions

View file

@ -1,4 +1,5 @@
{
"_comments": "[]",
"allow_events_in_timeline": 1,
"allow_import": 1,
"allow_rename": 1,
@ -21,7 +22,14 @@
"gender",
"phone",
"image",
"sync_with_google_contacts",
"sb_00",
"google_contacts",
"google_contacts_id",
"cb_00",
"pulled_from_google_contacts",
"google_contacts_description",
"sb_01",
"email_ids",
"phone_nos",
"contact_details",
@ -29,10 +37,7 @@
"links",
"more_info",
"department",
"unsubscribed",
"column_break_17",
"pulled_from_google_contacts",
"google_contacts_description"
"unsubscribed"
],
"fields": [
{
@ -155,10 +160,6 @@
"fieldtype": "Data",
"label": "Designation"
},
{
"fieldname": "column_break_17",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "unsubscribed",
@ -171,15 +172,17 @@
"label": "Middle Name"
},
{
"depends_on": "eval:doc.source==\"Google Contacts\"",
"depends_on": "pulled_from_google_contacts",
"fieldname": "google_contacts_description",
"fieldtype": "Small Text",
"label": "Google Contacts Description"
},
{
"collapsible": 1,
"depends_on": "eval:doc.sync_with_google_contacts || doc.pulled_from_google_contacts",
"fieldname": "sb_00",
"fieldtype": "Section Break",
"label": "Contact Details"
"label": "Google Contacts"
},
{
"fieldname": "email_ids",
@ -205,12 +208,39 @@
"fieldtype": "Check",
"label": "Pulled from Google Contacts",
"read_only": 1
},
{
"default": "0",
"fieldname": "sync_with_google_contacts",
"fieldtype": "Check",
"label": "Sync with Google Contacts"
},
{
"fieldname": "google_contacts",
"fieldtype": "Link",
"label": "Google Contacts",
"options": "Google Contacts"
},
{
"fieldname": "cb_00",
"fieldtype": "Column Break"
},
{
"fieldname": "sb_01",
"fieldtype": "Section Break",
"label": "Contact Details"
},
{
"fieldname": "google_contacts_id",
"fieldtype": "Data",
"label": "Google Contacts Id",
"read_only": 1
}
],
"icon": "fa fa-user",
"idx": 1,
"image_field": "image",
"modified": "2019-09-06 06:43:02.159233",
"modified": "2019-09-06 20:23:15.088231",
"modified_by": "Administrator",
"module": "Contacts",
"name": "Contact",

View file

@ -42,6 +42,9 @@ class Contact(Document):
if self.email_id and not self.image:
self.image = has_gravatar(self.email_id)
if self.sync_with_google_contacts and not self.google_contacts:
frappe.throw(_("Select Google Contacts to which contact should be synced."))
deduplicate_dynamic_links(self)
def set_user(self):

View file

@ -136,6 +136,18 @@ doc_events = {
"on_change": [
"frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points"
],
},
"Email Group Member": {
"validate": "frappe.email.doctype.email_group.email_group.restrict_email_group"
},
"Event": {
"after_insert": "frappe.integrations.doctype.google_calendar.google_calendar.insert_event_in_google_calendar",
"on_update": "frappe.integrations.doctype.google_calendar.google_calendar.update_event_in_google_calendar",
"on_trash": "frappe.integrations.doctype.google_calendar.google_calendar.delete_event_from_google_calendar",
},
"Contact": {
"after_insert": "frappe.integrations.doctype.google_contacts.google_contacts.insert_contacts_to_google_contacts",
"on_update": "frappe.integrations.doctype.google_contacts.google_contacts.update_contacts_to_google_contacts",
}
}

View file

@ -10,12 +10,11 @@ import google.oauth2.credentials
from frappe.model.document import Document
from frappe import _
from googleapiclient.errors import HttpError
from frappe.utils import get_request_site_address
from frappe.integrations.doctype.google_settings.google_settings import get_auth_url
SCOPES = "https://www.googleapis.com/auth/contacts"
REQUEST = "https://people.googleapis.com/v1/people/me/connections"
PARAMS = {"personFields": "names,emailAddresses,organizations,phoneNumbers"}
class GoogleContacts(Document):
@ -135,24 +134,28 @@ def sync(g_contact=None):
for g in google_contacts:
return sync_contacts_from_google_contacts(g.name)
def sync_contacts_from_google_contacts(g_contact, page_length=10):
def sync_contacts_from_google_contacts(g_contact):
"""
Syncs Contacts from Google Contacts.
https://developers.google.com/people/api/rest/v1/contactGroups/list
"""
google_contacts, account = get_google_contacts_object(g_contact)
if not account.pull_from_google_contacts:
return
results = []
contacts_updated = 0
while True:
try:
sync_token = account.get_password(fieldname="next_sync_token", raise_exception=False) or None
contacts = google_contacts.people().connections().list(resourceName='people/me', maxResults=page_length,
syncToken=sync_token).execute()
contacts = google_contacts.people().connections().list(resourceName='people/me',syncToken=sync_token,
personFields="names,emailAddresses,organizations,phoneNumbers").execute()
except HttpError as err:
frappe.throw(_("Google Contacts - Could not sync contacts from Google Contacts, error code {0}.").format(err.resp.status))
frappe.throw(_("Google Contacts - Could not sync contacts from Google Contacts {0}, error code {1}.").format(account.name, err.resp.status))
for contact in contacts.get("items"):
for contact in contacts.get("connections"):
results.append(contact)
if not contacts.get("nextPageToken"):
@ -161,29 +164,29 @@ def sync_contacts_from_google_contacts(g_contact, page_length=10):
frappe.db.commit()
break
frappe.db.set_value("Google Contacts", doc.name, "last_sync_on", frappe.utils.now_datetime())
frappe.db.set_value("Google Contacts", account.name, "last_sync_on", frappe.utils.now_datetime())
for idx, connection in enumerate(results):
frappe.publish_realtime('import_google_contacts', dict(progress=idx+1, total=r.get("totalPeople")), user=frappe.session.user)
frappe.publish_realtime('import_google_contacts', dict(progress=idx+1, total=len(results)), user=frappe.session.user)
for name in connection.get("names"):
if name.get("metadata").get("primary"):
contacts_updated += 1
contact = frappe.get_doc({
"doctype": "Contact",
"salutation": name.get("honorificPrefix") or "",
"first_name": name.get("givenName") or "",
"middle_name": name.get("middleName") or "",
"last_name": name.get("familyName") or "",
"designation": get_indexed_value(connection.get("organizations"), 0, "title"),
"source": "Google Contacts",
"pulled_from_google_contacts": 1,
"google_contacts_description": get_indexed_value(connection.get("organizations"), 0, "name")
})
for email in connection.get("emailAddresses", []):
contact.add_email(email_id=email.get("value"), is_primary=1 if email.get("primary") else 0)
contact.add_email(email_id=email.get("value"), is_primary=1 if email.get("metadata").get("primary") else 0)
for phone in connection.get("phoneNumbers", []):
contact.add_phone(phone=phone.get("value"), is_primary=1 if phone.get("primary") else 0)
contact.add_phone(phone=phone.get("value"), is_primary=1 if phone.get("metadata").get("primary") else 0)
contact.insert(ignore_permissions=True)
@ -191,7 +194,79 @@ def sync_contacts_from_google_contacts(g_contact, page_length=10):
else _("No new Google Contacts synced.")
def insert_contacts_to_google_contacts(doc, method=None):
pass
"""
Syncs Contacts from Google Contacts.
https://developers.google.com/people/api/rest/v1/people/createContact
"""
if not frappe.db.exists("Google Contacts", {"name": doc.google_contacts}) or doc.pulled_from_google_contacts \
or not doc.sync_with_google_contacts:
return
google_contacts, account = get_google_contacts_object(doc.google_contacts)
if not account.push_to_google_contacts:
return
names = {
"givenName": doc.first_name,
"middleName": doc.middle_name,
"familyName": doc.last_name
}
phoneNumbers = [{"value": phone_no.phone} for phone_no in doc.phone_nos]
emailAddresses = [{"value": email_id.email_id} for email_id in doc.email_ids]
try:
contact = google_contacts.people().createContact(parent='people/me', body={"names": [names],"phoneNumbers": phoneNumbers,
"emailAddresses": emailAddresses}).execute()
frappe.db.set_value("Contact", doc.name, "google_contacts_id", contact.get("resourceName"))
except HttpError as err:
frappe.msgprint(_("Google Calendar - Could not insert contact in Google Contacts {0}, error code {1}.").format(account.name, err.resp.status))
def update_contacts_to_google_contacts(doc, method=None):
"""
Syncs Contacts from Google Contacts.
https://developers.google.com/people/api/rest/v1/people/updateContact
"""
# Workaround to avoid triggering updation when Event is being inserted since
# creation and modified are same when inserting doc
if not frappe.db.exists("Google Contacts", {"name": doc.google_contacts}) or doc.modified == doc.creation \
or not doc.sync_with_google_contacts:
return
if doc.sync_with_google_contacts and not doc.google_contacts_id:
# If sync_with_google_contacts is checked later, then insert the contact rather than updating it.
insert_contacts_to_google_contacts(doc)
return
google_contacts, account = get_google_contacts_object(doc.google_contacts)
if not account.push_to_google_contacts:
return
names = {
"givenName": doc.first_name,
"middleName": doc.middle_name,
"familyName": doc.last_name
}
phoneNumbers = [{"value": phone_no.phone} for phone_no in doc.phone_nos]
emailAddresses = [{"value": email_id.email_id} for email_id in doc.email_ids]
try:
contact = google_contacts.people().get(resourceName=doc.google_contacts_id, \
personFields="names,emailAddresses,organizations,phoneNumbers").execute()
contact["names"] = [names]
contact["phoneNumbers"] = phoneNumbers
contact["emailAddresses"] = emailAddresses
google_contacts.people().updateContact(resourceName=doc.google_contacts_id,body={"names":[names],
"phoneNumbers":phoneNumbers,"emailAddresses":emailAddresses},
updatePersonFields="names,emailAddresses,organizations,phoneNumbers").execute()
frappe.msgprint(_("Contact Synced with Google Contacts."))
except HttpError as err:
frappe.msgprint(_("Google Contacts - Could not update contact in Google Contacts {0}, error code {1}.").format(account.name, err.resp.status))
def get_indexed_value(d, index, key):
if not d: