Example #1
0
	def find_parent_based_on_subject_and_sender(self, communication, email):
		'''Find parent document based on subject and sender match'''
		parent = None

		if self.append_to and self.sender_field:
			if self.subject_field:
				# try and match by subject and sender
				# if sent by same sender with same subject,
				# append it to old coversation
				subject = frappe.as_unicode(strip(re.sub(r"(^\s*(fw|fwd|wg)[^:]*:|\s*(re|aw)[^:]*:\s*)*",
					"", email.subject, 0, flags=re.IGNORECASE)))

				parent = frappe.db.get_all(self.append_to, filters={
					self.sender_field: email.from_email,
					self.subject_field: ("like", "%{0}%".format(subject)),
					"creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT))
				}, fields="name")

				# match only subject field
				# when the from_email is of a user in the system
				# and subject is atleast 10 chars long
				if not parent and len(subject) > 10 and is_system_user(email.from_email):
					parent = frappe.db.get_all(self.append_to, filters={
						self.subject_field: ("like", "%{0}%".format(subject)),
						"creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT))
					}, fields="name")

			if parent:
				parent = frappe._dict(doctype=self.append_to, name=parent[0].name)
				return parent
Example #2
0
	def match_record_by_subject_and_sender(self, doctype):
		"""Find a record in the given doctype that matches with email subject and sender.

		Cases:
		1. Sometimes record name is part of subject. We can get document by parsing name from subject
		2. Find by matching sender and subject
		3. Find by matching subject alone (Special case)
			Ex: when a System User is using Outlook and replies to an email from their own client,
			it reaches the Email Account with the threading info lost and the (sender + subject match)
			doesn't work because the sender in the first communication was someone different to whom
			the system user is replying to via the common email account in Frappe. This fix bypasses
			the sender match when the sender is a system user and subject is atleast 10 chars long
			(for additional safety)

		NOTE: We consider not to match by subject if match record is very old.
		"""
		name = self.get_reference_name_from_subject()
		email_fields = self.get_email_fields(doctype)

		record = self.get_doc(doctype, name, ignore_error=True) if name else None

		if not record:
			subject = self.clean_subject(self.subject)
			filters = {
				email_fields.subject_field: ("like", f"%{subject}%"),
				"creation": (">", self.get_relative_dt(days=-60))
			}

			# Sender check is not needed incase mail is from system user.
			if not (len(subject) > 10 and is_system_user(self.from_email)):
				filters[email_fields.sender_field] = self.from_email

			name = frappe.db.get_value(self.email_account.append_to, filters = filters)
			record = self.get_doc(doctype, name, ignore_error=True) if name else None
		return record
Example #3
0
def update_first_response_time(parent, communication):
	if parent.meta.has_field("first_response_time") and not parent.get("first_response_time"):
		if is_system_user(communication.sender):
			first_responded_on = communication.creation
			if parent.meta.has_field("first_responded_on") and communication.sent_or_received == "Sent":
				parent.db_set("first_responded_on", first_responded_on)
			parent.db_set("first_response_time", round(time_diff_in_seconds(first_responded_on, parent.creation), 2))
Example #4
0
	def find_parent_based_on_subject_and_sender(self, communication, email):
		'''Find parent document based on subject and sender match'''
		parent = None

		if self.append_to and self.sender_field:
			if self.subject_field:
				# try and match by subject and sender
				# if sent by same sender with same subject,
				# append it to old coversation
				subject = strip(re.sub("(^\s*(Fw|FW|fwd)[^:]*:|\s*(Re|RE)[^:]*:\s*)*", "", email.subject))

				parent = frappe.db.get_all(self.append_to, filters={
					self.sender_field: email.from_email,
					self.subject_field: ("like", "%{0}%".format(subject)),
					"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
				}, fields="name")

				# match only subject field
				# when the from_email is of a user in the system
				# and subject is atleast 10 chars long
				if not parent and len(subject) > 10 and is_system_user(email.from_email):
					parent = frappe.db.get_all(self.append_to, filters={
						self.subject_field: ("like", "%{0}%".format(subject)),
						"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
					}, fields="name")

			if parent:
				parent = frappe.get_doc(self.append_to, parent[0].name)
				return parent
Example #5
0
	def find_parent_based_on_subject_and_sender(self, communication, email):
		'''Find parent document based on subject and sender match'''
		parent = None

		if self.append_to and self.sender_field:
			if self.subject_field:
				if '#' in email.subject:
					# try and match if ID is found
					# document ID is appended to subject
					# example "Re: Your email (#OPP-2020-2334343)"
					parent_id = email.subject.rsplit('#', 1)[-1].strip(' ()')
					if parent_id:
						parent = frappe.db.get_all(self.append_to, filters = dict(name = parent_id),
							fields = 'name')

				if not parent:
					# try and match by subject and sender
					# if sent by same sender with same subject,
					# append it to old coversation
					subject = frappe.as_unicode(strip(re.sub(r"(^\s*(fw|fwd|wg)[^:]*:|\s*(re|aw)[^:]*:\s*)*",
						"", email.subject, 0, flags=re.IGNORECASE)))

					parent = frappe.db.get_all(self.append_to, filters={
						self.sender_field: email.from_email,
						self.subject_field: ("like", "%{0}%".format(subject)),
						"creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT))
					}, fields = "name", limit = 1)

				if not parent and len(subject) > 10 and is_system_user(email.from_email):
					# match only subject field
					# when the from_email is of a user in the system
					# and subject is atleast 10 chars long
					parent = frappe.db.get_all(self.append_to, filters={
						self.subject_field: ("like", "%{0}%".format(subject)),
						"creation": (">", (get_datetime() - relativedelta(days=60)).strftime(DATE_FORMAT))
					}, fields = "name", limit = 1)



			if parent:
				parent = frappe._dict(doctype=self.append_to, name=parent[0].name)
				return parent
Example #6
0
	def set_thread(self, communication, email):
		"""Appends communication to parent based on thread ID. Will extract
		parent communication and will link the communication to the reference of that
		communication. Also set the status of parent transaction to Open or Replied.

		If no thread id is found and `append_to` is set for the email account,
		it will create a new parent transaction (e.g. Issue)"""
		in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>")
		parent = None

		if self.append_to:
			# set subject_field and sender_field
			meta_module = frappe.get_meta_module(self.append_to)
			meta = frappe.get_meta(self.append_to)
			subject_field = getattr(meta_module, "subject_field", "subject")
			if not meta.get_field(subject_field):
				subject_field = None
			sender_field = getattr(meta_module, "sender_field", "sender")
			if not meta.get_field(sender_field):
				sender_field = None

		if in_reply_to:
			if "@{0}".format(frappe.local.site) in in_reply_to:

				# reply to a communication sent from the system
				in_reply_to, domain = in_reply_to.split("@", 1)

				if frappe.db.exists("Communication", in_reply_to):
					parent = frappe.get_doc("Communication", in_reply_to)

					# set in_reply_to of current communication
					communication.in_reply_to = in_reply_to

					if parent.reference_name:
						parent = frappe.get_doc(parent.reference_doctype,
							parent.reference_name)

		if not parent and self.append_to and sender_field:
			if subject_field:
				# try and match by subject and sender
				# if sent by same sender with same subject,
				# append it to old coversation
				subject = strip(re.sub("^\s*(Re|RE)[^:]*:\s*", "", email.subject))

				parent = frappe.db.get_all(self.append_to, filters={
					sender_field: email.from_email,
					subject_field: ("like", "%{0}%".format(subject)),
					"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
				}, fields="name")

				# match only subject field
				# when the from_email is of a user in the system
				# and subject is atleast 10 chars long
				if not parent and len(subject) > 10 and is_system_user(email.from_email):
					parent = frappe.db.get_all(self.append_to, filters={
						subject_field: ("like", "%{0}%".format(subject)),
						"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
					}, fields="name")

			if parent:
				parent = frappe.get_doc(self.append_to, parent[0].name)

		if not parent and self.append_to and self.append_to!="Communication":
			# no parent found, but must be tagged
			# insert parent type doc
			parent = frappe.new_doc(self.append_to)

			if subject_field:
				parent.set(subject_field, email.subject)

			if sender_field:
				parent.set(sender_field, email.from_email)

			parent.flags.ignore_mandatory = True

			try:
				parent.insert(ignore_permissions=True)
			except frappe.DuplicateEntryError:
				# try and find matching parent
				parent_name = frappe.db.get_value(self.append_to, {sender_field: email.from_email})
				if parent_name:
					parent.name = parent_name
				else:
					parent = None

			# NOTE if parent isn't found and there's no subject match, it is likely that it is a new conversation thread and hence is_first = True
			communication.is_first = True

		if parent:
			communication.reference_doctype = parent.doctype
			communication.reference_name = parent.name
Example #7
0
    def set_thread(self, communication, email):
        """Appends communication to parent based on thread ID. Will extract
		parent communication and will link the communication to the reference of that
		communication. Also set the status of parent transaction to Open or Replied.

		If no thread id is found and `append_to` is set for the email account,
		it will create a new parent transaction (e.g. Issue)"""
        in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>")
        parent = None

        if self.append_to:
            # set subject_field and sender_field
            meta_module = frappe.get_meta_module(self.append_to)
            meta = frappe.get_meta(self.append_to)
            subject_field = getattr(meta_module, "subject_field", "subject")
            if not meta.get_field(subject_field):
                subject_field = None
            sender_field = getattr(meta_module, "sender_field", "sender")
            if not meta.get_field(sender_field):
                sender_field = None

        if in_reply_to:
            if "@{0}".format(frappe.local.site) in in_reply_to:

                # reply to a communication sent from the system
                in_reply_to, domain = in_reply_to.split("@", 1)

                if frappe.db.exists("Communication", in_reply_to):
                    parent = frappe.get_doc("Communication", in_reply_to)

                    # set in_reply_to of current communication
                    communication.in_reply_to = in_reply_to

                    if parent.reference_name:
                        parent = frappe.get_doc(parent.reference_doctype,
                                                parent.reference_name)

        if not parent and self.append_to and sender_field:
            if subject_field:
                # try and match by subject and sender
                # if sent by same sender with same subject,
                # append it to old coversation
                subject = strip(
                    re.sub("^\s*(Re|RE)[^:]*:\s*", "", email.subject))

                parent = frappe.db.get_all(
                    self.append_to,
                    filters={
                        sender_field:
                        email.from_email,
                        subject_field: ("like", "%{0}%".format(subject)),
                        "creation":
                        (">", (get_datetime() -
                               relativedelta(days=10)).strftime(DATE_FORMAT))
                    },
                    fields="name")

                # match only subject field
                # when the from_email is of a user in the system
                # and subject is atleast 10 chars long
                if not parent and len(subject) > 10 and is_system_user(
                        email.from_email):
                    parent = frappe.db.get_all(
                        self.append_to,
                        filters={
                            subject_field: ("like", "%{0}%".format(subject)),
                            "creation":
                            (">",
                             (get_datetime() -
                              relativedelta(days=10)).strftime(DATE_FORMAT))
                        },
                        fields="name")

            if parent:
                parent = frappe.get_doc(self.append_to, parent[0].name)

        if not parent and self.append_to and self.append_to != "Communication":
            # no parent found, but must be tagged
            # insert parent type doc
            parent = frappe.new_doc(self.append_to)

            if subject_field:
                parent.set(subject_field, email.subject)

            if sender_field:
                parent.set(sender_field, email.from_email)

            parent.flags.ignore_mandatory = True

            try:
                parent.insert(ignore_permissions=True)
            except frappe.DuplicateEntryError:
                # try and find matching parent
                parent_name = frappe.db.get_value(
                    self.append_to, {sender_field: email.from_email})
                if parent_name:
                    parent.name = parent_name
                else:
                    parent = None

            # NOTE if parent isn't found and there's no subject match, it is likely that it is a new conversation thread and hence is_first = True
            communication.is_first = True

        if parent:
            communication.reference_doctype = parent.doctype
            communication.reference_name = parent.name
Example #8
0
	def set_thread(self, communication, email):
		"""Appends communication to parent based on thread ID. Will extract
		parent communication and will link the communication to the reference of that
		communication. Also set the status of parent transaction to Open or Replied.

		If no thread id is found and `append_to` is set for the email account,
		it will create a new parent transaction (e.g. Issue)"""
		in_reply_to = (email.mail.get("In-Reply-To") or "")
		parent = None
		if self.append_to:
			# set subject_field and sender_field
			meta_module = frappe.get_meta_module(self.append_to)
			meta = frappe.get_meta(self.append_to)
			subject_field = getattr(meta_module, "subject_field", "subject")
			if not meta.get_field(subject_field):
				subject_field = None
			sender_field = getattr(meta_module, "sender_field", "sender")
			if not meta.get_field(sender_field):
				sender_field = None
		matched = False
		if in_reply_to:
			# reply to a communication sent from the system
			reply_found = frappe.db.get_value("Communication", {"message_id": in_reply_to}, ["name", "reference_doctype", "reference_name"], order_by="creation", as_dict=1)
			if reply_found:
				
				# set in_reply_to of current communication
				communication.in_reply_to = reply_found.name
				if frappe.db.exists(reply_found.reference_doctype, reply_found.reference_name):
					communication.reference_doctype = reply_found.reference_doctype
					communication.reference_name = reply_found.reference_name
				matched = True
			if email.message_id:
				first = frappe.db.get_value("Communication", {"message_id": email.message_id},["name", "reference_doctype", "reference_name"], order_by="creation", as_dict=1)
			
				if first:
					#set timeline hide to parent doc so are linked
					communication.timeline_hide = first.name
					if frappe.db.exists(first.reference_doctype, first.reference_name):
						communication.reference_doctype = first.reference_doctype
						communication.reference_name = first.reference_name
					matched = True
		if not matched:
			if not parent and self.append_to and sender_field:
				if subject_field:
					# try and match by subject and sender
					# if sent by same sender with same subject,
					# append it to old coversation
					subject = strip(re.sub("^\s*(Re|RE)[^:]*:\s*", "", email.subject))
	
					parent = frappe.db.get_all(self.append_to, filters={
						sender_field: email.from_email,
						subject_field: ("like", "%{0}%".format(subject)),
						"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
					}, fields="name")
	
					# match only subject field
					# when the from_email is of a user in the system
					# and subject is atleast 10 chars long
					if not parent and len(subject) > 10 and is_system_user(email.from_email):
						parent = frappe.db.get_all(self.append_to, filters={
							subject_field: ("like", "%{0}%".format(subject)),
							"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
						}, fields="name")
	
				if parent:
					parent = frappe.get_doc(self.append_to, parent[0].name)
	
			if not parent:
				# try match doctype based on subject
				if ':' in email.subject:
					try:
						subject = strip(re.sub("(^\s*(Fw|FW|fwd)[^:]*:|\s*(Re|RE)[^:]*:\s*)*","", email.subject))
						if ':' in subject:
							reference_doctype,reference_name = subject.split(': ',1)
							parent = frappe.get_doc(reference_doctype,reference_name)
					except:
						try:
							ref = re.search("((?<=New Leave Application: ).*(?= - Employee:))",email.subject).group(0)
							parent = frappe.get_doc("Leave Application",ref)
						except:
							pass
	
			if not parent and self.append_to and self.append_to!="Communication":
				# no parent found, but must be tagged
				# insert parent type doc
				parent = frappe.new_doc(self.append_to)
	
				if subject_field:
					parent.set(subject_field, email.subject)
	
				if sender_field:
					parent.set(sender_field, email.from_email)
	
				parent.flags.ignore_mandatory = True
	
				try:
					parent.insert(ignore_permissions=True)
				except frappe.DuplicateEntryError:
					# try and find matching parent
					parent_name = frappe.db.get_value(self.append_to, {sender_field: email.from_email})
					if parent_name:
						parent.name = parent_name
					else:
						parent = None
	
				# NOTE if parent isn't found and there's no subject match, it is likely that it is a new conversation thread and hence is_first = True
				communication.is_first = True
	
			if parent:
				communication.reference_doctype = parent.doctype
				communication.reference_name = parent.name

		# check if message is notification and disable notifications for this message
		isnotification = email.mail.get("isnotification")
		if isnotification:
			if "notification" in isnotification:
				communication.unread_notification_sent = 1