def reduce(key, stringified_values): values = [ast.literal_eval(v) for v in stringified_values] for item in values: if item['type'] == 'feedback': subscription_services.subscribe_to_thread(key, item['id']) elif item['type'] == 'exploration': subscription_services.subscribe_to_activity(key, item['id'])
def create_suggestion(exploration_id, author_id, exploration_version, state_name, description, suggestion_content): """Creates a new SuggestionModel and the corresponding FeedbackThreadModel domain object. Args: exploration_id: str. The exploration id the suggestion belongs to. author_id: str. ID of the user who submitted the suggestion. exploration_version: int. The exploration version for which the suggestion was made. state_name: str or None. The state name for the thread. If None, this indicates that the thread pertains to the exploration as a whole. description: str. Learner-provided description of suggestion changes. suggestion_content: dict. Only contains two keys, "type" and "value". For historical reasons, the value of "type" is always "text" while the value of "value" is the actual content of the suggestion. """ thread_id = _create_models_for_thread_and_first_message( exploration_id, state_name, author_id, description, DEFAULT_SUGGESTION_THREAD_INITIAL_MESSAGE, True) feedback_models.SuggestionModel.create( exploration_id, thread_id, author_id, exploration_version, state_name, description, suggestion_content) full_thread_id = ( feedback_models.FeedbackThreadModel.generate_full_thread_id( exploration_id, thread_id)) subscription_services.subscribe_to_thread(author_id, full_thread_id) _enqueue_suggestion_email_task(exploration_id, thread_id)
def create_suggestion(exploration_id, author_id, exploration_version, state_name, description, suggestion_content): """Creates a new SuggestionModel and the corresponding FeedbackThreadModel domain object. Args: exploration_id: str. The exploration id the suggestion belongs to. author_id: str. ID of the user who submitted the suggestion. exploration_version: int. The exploration version for which the suggestion was made. state_name: str or None. The state name for the thread. If None, this indicates that the thread pertains to the exploration as a whole. description: str. Learner-provided description of suggestion changes. suggestion_content: dict. Only contains two keys, "type" and "value". For historical reasons, the value of "type" is always "text" while the value of "value" is the actual content of the suggestion. """ thread_id = _create_models_for_thread_and_first_message( exploration_id, state_name, author_id, description, DEFAULT_SUGGESTION_THREAD_INITIAL_MESSAGE, True) feedback_models.SuggestionModel.create(exploration_id, thread_id, author_id, exploration_version, state_name, description, suggestion_content) full_thread_id = ( feedback_models.FeedbackThreadModel.generate_full_thread_id( exploration_id, thread_id)) subscription_services.subscribe_to_thread(author_id, full_thread_id) _enqueue_suggestion_email_task(exploration_id, thread_id)
def test_thread_and_exploration_subscriptions_are_tracked_individually(self): self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), []) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID) subscription_services.subscribe_to_exploration(USER_ID, EXP_ID) self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID]) self.assertEqual(self._get_exploration_ids_subscribed_to(USER_ID), [EXP_ID])
def reduce(key, stringified_values): values = [ast.literal_eval(v) for v in stringified_values] for item in values: if item["type"] == "feedback": subscription_services.subscribe_to_thread(key, item["id"]) elif item["type"] == "exploration": subscription_services.subscribe_to_activity(key, item["id"])
def create_message( exploration_id, thread_id, author_id, updated_status, updated_subject, text): """Creates a new message for the thread and subscribes the author to the thread. Returns False if the message with the ID already exists. """ from core.domain import event_services # Get the thread at the outset, in order to check that the thread_id passed # in is valid. full_thread_id = ( feedback_models.FeedbackThreadModel.generate_full_thread_id( exploration_id, thread_id)) thread = feedback_models.FeedbackThreadModel.get(full_thread_id) message_id = feedback_models.FeedbackMessageModel.get_message_count( exploration_id, thread_id) msg = feedback_models.FeedbackMessageModel.create( exploration_id, thread_id, message_id) msg.thread_id = full_thread_id msg.message_id = message_id msg.author_id = author_id if updated_status: if message_id == 0: # New thread. event_services.FeedbackThreadCreatedEventHandler.record( thread.exploration_id) else: # Thread status changed. event_services.FeedbackThreadStatusChangedEventHandler.record( thread.exploration_id, thread.status, updated_status) msg.updated_status = updated_status if updated_subject: msg.updated_subject = updated_subject msg.text = text msg.put() # We do a put() even if the status and subject are not updated, so that the # last_updated time of the thread reflects the last time a message was # added to it. if message_id != 0 and (updated_status or updated_subject): if updated_status and updated_status != thread.status: thread.status = updated_status if updated_subject and updated_subject != thread.subject: thread.subject = updated_subject thread.put() if (user_services.is_user_registered(author_id) and len(text) > 0 and feconf.CAN_SEND_EMAILS_TO_USERS and feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS): # send feedback message email if user is registered. add_message_to_email_buffer( author_id, exploration_id, thread_id, message_id) if author_id: subscription_services.subscribe_to_thread(author_id, full_thread_id) return True
def create_message(exploration_id, thread_id, author_id, updated_status, updated_subject, text): """Creates a new message for the thread and subscribes the author to the thread. Returns False if the message with the ID already exists. """ from core.domain import event_services # Get the thread at the outset, in order to check that the thread_id passed # in is valid. full_thread_id = ( feedback_models.FeedbackThreadModel.generate_full_thread_id( exploration_id, thread_id)) thread = feedback_models.FeedbackThreadModel.get(full_thread_id) message_id = feedback_models.FeedbackMessageModel.get_message_count( exploration_id, thread_id) msg = feedback_models.FeedbackMessageModel.create(exploration_id, thread_id, message_id) msg.thread_id = full_thread_id msg.message_id = message_id msg.author_id = author_id if updated_status: if message_id == 0: # New thread. event_services.FeedbackThreadCreatedEventHandler.record( thread.exploration_id) else: # Thread status changed. event_services.FeedbackThreadStatusChangedEventHandler.record( thread.exploration_id, thread.status, updated_status) msg.updated_status = updated_status if updated_subject: msg.updated_subject = updated_subject msg.text = text msg.put() # We do a put() even if the status and subject are not updated, so that the # last_updated time of the thread reflects the last time a message was # added to it. if message_id != 0 and (updated_status or updated_subject): if updated_status and updated_status != thread.status: thread.status = updated_status if updated_subject and updated_subject != thread.subject: thread.subject = updated_subject thread.put() if (user_services.is_user_registered(author_id) and len(text) > 0 and feconf.CAN_SEND_EMAILS_TO_USERS and feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS): # send feedback message email if user is registered. add_message_to_email_buffer(author_id, exploration_id, thread_id, message_id) if author_id: subscription_services.subscribe_to_thread(author_id, full_thread_id) return True
def test_thread_and_exp_subscriptions_are_tracked_individually(self): self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), []) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID) subscription_services.subscribe_to_exploration(USER_ID, EXP_ID) self.assertEqual( self._get_thread_ids_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID]) self.assertEqual( self._get_exploration_ids_subscribed_to(USER_ID), [EXP_ID])
def reduce(key, stringified_values): """Implements the reduce function for this job.""" values = [ast.literal_eval(v) for v in stringified_values] for item in values: if item['type'] == 'feedback': subscription_services.subscribe_to_thread(key, item['id']) elif item['type'] == 'exploration': subscription_services.subscribe_to_exploration(key, item['id']) elif item['type'] == 'collection': subscription_services.subscribe_to_collection(key, item['id'])
def test_thread_and_activity_subscriptions_are_tracked_individually(self): USER_ID = 'user_id' FEEDBACK_THREAD_ID = 'fthread_id' ACTIVITY_ID = 'activity_id' self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), []) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID) subscription_services.subscribe_to_activity(USER_ID, ACTIVITY_ID) self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID]) self.assertEqual(self._get_activity_ids_subscribed_to(USER_ID), [ACTIVITY_ID])
def test_thread_and_activity_subscriptions_are_tracked_individually(self): USER_ID = 'user_id' FEEDBACK_THREAD_ID = 'fthread_id' ACTIVITY_ID = 'activity_id' self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), []) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID) subscription_services.subscribe_to_activity(USER_ID, ACTIVITY_ID) self.assertEqual( self._get_thread_ids_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID]) self.assertEqual( self._get_activity_ids_subscribed_to(USER_ID), [ACTIVITY_ID])
def create_suggestion(exploration_id, author_id, exploration_version, state_name, description, suggestion_content): """Creates a new SuggestionModel object and the corresponding FeedbackThreadModel object.""" thread_id = _create_models_for_thread_and_first_message( exploration_id, state_name, author_id, description, DEFAULT_SUGGESTION_THREAD_INITIAL_MESSAGE, True ) feedback_models.SuggestionModel.create( exploration_id, thread_id, author_id, exploration_version, state_name, description, suggestion_content ) full_thread_id = feedback_models.FeedbackThreadModel.generate_full_thread_id(exploration_id, thread_id) subscription_services.subscribe_to_thread(author_id, full_thread_id)
def test_get_all_threads_subscribed_to(self): self.assertEqual( subscription_services.get_all_threads_subscribed_to( USER_ID), []) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID) self.assertEqual( subscription_services.get_all_threads_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID]) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID_2) self.assertEqual( subscription_services.get_all_threads_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID, FEEDBACK_THREAD_ID_2])
def setUp(self): super(UserSubscriptionsModelValidatorTests, self).setUp() self.signup(self.OWNER_EMAIL, self.OWNER_USERNAME) self.signup(USER_EMAIL, USER_NAME) self.owner_id = self.get_user_id_from_email(self.OWNER_EMAIL) self.user_id = self.get_user_id_from_email(USER_EMAIL) self.owner = user_services.UserActionsInfo(self.owner_id) explorations = [exp_domain.Exploration.create_default_exploration( '%s' % i, title='title %d' % i, category='category%d' % i ) for i in xrange(3)] for exp in explorations: exp_services.save_new_exploration(self.owner_id, exp) rights_manager.publish_exploration(self.owner, exp.id) collections = [collection_domain.Collection.create_default_collection( '%s' % i, title='title %d' % i, category='category%d' % i ) for i in xrange(3, 6)] for collection in collections: collection_services.save_new_collection(self.owner_id, collection) rights_manager.publish_collection(self.owner, collection.id) thread_id = feedback_services.create_thread( 'exploration', 'exp_id', None, 'a subject', 'some text') subscription_services.subscribe_to_thread( self.user_id, thread_id) subscription_services.subscribe_to_creator(self.user_id, self.owner_id) for exp in explorations: subscription_services.subscribe_to_exploration( self.user_id, exp.id) for collection in collections: subscription_services.subscribe_to_collection( self.user_id, collection.id) self.process_and_flush_pending_tasks() prod_validation_jobs_one_off.MODEL_TO_VALIDATOR_MAPPING = { user_models.UserSubscriptionsModel: prod_validation_jobs_one_off.UserSubscriptionsModelValidator, }
def create_suggestion(exploration_id, author_id, exploration_version, state_name, description, suggestion_content): """Creates a new SuggestionModel object and the corresponding FeedbackThreadModel object.""" thread_id = _create_models_for_thread_and_first_message( exploration_id, state_name, author_id, description, DEFAULT_SUGGESTION_THREAD_INITIAL_MESSAGE, True) feedback_models.SuggestionModel.create( exploration_id, thread_id, author_id, exploration_version, state_name, description, suggestion_content) full_thread_id = ( feedback_models.FeedbackThreadModel.generate_full_thread_id( exploration_id, thread_id)) subscription_services.subscribe_to_thread(author_id, full_thread_id)
def create_message( thread_id, author_id, updated_status, updated_subject, text): """Creates a new message for the thread and subscribes the author to the thread. Returns False if the message with the ID already exists. """ from core.domain import event_services # Get the thread at the outset, in order to check that the thread_id passed # in is valid. thread = feedback_models.FeedbackThreadModel.get(thread_id) message_id = feedback_models.FeedbackMessageModel.get_message_count( thread_id) msg = feedback_models.FeedbackMessageModel.create(thread_id, message_id) msg.thread_id = thread_id msg.message_id = message_id msg.author_id = author_id if updated_status: if message_id == 0: # New thread. event_services.FeedbackThreadCreatedEventHandler.record( thread.exploration_id) else: # Thread status changed. event_services.FeedbackThreadStatusChangedEventHandler.record( thread.exploration_id, thread.status, updated_status) msg.updated_status = updated_status if updated_subject: msg.updated_subject = updated_subject msg.text = text msg.put() # We do a put() even if the status and subject are not updated, so that the # last_updated time of the thread reflects the last time a message was # added to it. if message_id != 0 and (updated_status or updated_subject): if updated_status and updated_status != thread.status: thread.status = updated_status if updated_subject and updated_subject != thread.subject: thread.subject = updated_subject thread.put() if author_id: subscription_services.subscribe_to_thread(author_id, thread_id) return True
def create_message(thread_id, author_id, updated_status, updated_subject, text): """Creates a new message for the thread and subscribes the author to the thread. Returns False if the message with the ID already exists. """ from core.domain import event_services # Get the thread at the outset, in order to check that the thread_id passed # in is valid. thread = feedback_models.FeedbackThreadModel.get(thread_id) message_id = feedback_models.FeedbackMessageModel.get_message_count( thread_id) msg = feedback_models.FeedbackMessageModel.create(thread_id, message_id) msg.thread_id = thread_id msg.message_id = message_id msg.author_id = author_id if updated_status: if message_id == 0: # New thread. event_services.FeedbackThreadCreatedEventHandler.record( thread.exploration_id) else: # Thread status changed. event_services.FeedbackThreadStatusChangedEventHandler.record( thread.exploration_id, thread.status, updated_status) msg.updated_status = updated_status if updated_subject: msg.updated_subject = updated_subject msg.text = text msg.put() # We do a put() even if the status and subject are not updated, so that the # last_updated time of the thread reflects the last time a message was # added to it. if message_id != 0 and (updated_status or updated_subject): if updated_status and updated_status != thread.status: thread.status = updated_status if updated_subject and updated_subject != thread.subject: thread.subject = updated_subject thread.put() if author_id: subscription_services.subscribe_to_thread(author_id, thread_id) return True
def test_get_external_id_relationship_failure(self): nonexist_thread_id = 'nonexist_thread_id' subscription_services.subscribe_to_thread( self.user_id, nonexist_thread_id) job = prod_validation_jobs_one_off.ProdValidationAuditOneOffJob job_id = job.create_new() job.enqueue(job_id) self.process_and_flush_pending_tasks() actual_output = job.get_output(job_id) expected_output = [ ( u'[u\'failed validation check for general_feedback_thread_ids ' 'field check of UserSubscriptionsModel\', ' '[u"Model id 110211048197157141232: based on ' 'field general_feedback_thread_ids having value ' 'nonexist_thread_id, expect model GeneralFeedbackThreadModel ' 'with id nonexist_thread_id but it doesn\'t exist"]]'), u'[u\'fully-validated UserSubscriptionsModel\', 1]'] self.assertEqual(sorted(actual_output), sorted(expected_output))
def test_subscribe_to_feedback_thread(self): self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), []) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID) self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID]) # Repeated subscriptions to the same thread have no effect. subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID) self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID]) subscription_services.subscribe_to_thread(USER_ID, FEEDBACK_THREAD_ID_2) self.assertEqual(self._get_thread_ids_subscribed_to(USER_ID), [FEEDBACK_THREAD_ID, FEEDBACK_THREAD_ID_2])
def create_message(exploration_id, thread_id, author_id, updated_status, updated_subject, text, received_via_email=False): """Creates a new message for the thread and subscribes the author to the thread. Args: exploration_id: str. The exploration id the thread belongs to. thread_id: str. The thread id the message belongs to. author_id: str. The author id who creates this message. updated_status: str, one of STATUS_CHOICES. New thread status. Must be supplied if this is the first message of a thread. For the rest of the thread, should exist only when the status changes. updated_subject: str. New thread subject. Must be supplied if this is the first message of a thread. For the rest of the thread, should exist only when the subject changes. text: str. The text of the feedback message. This may be ''. received_via_email: bool. Whether new message is received via email or web. """ from core.domain import event_services # Get the thread at the outset, in order to check that the thread_id passed # in is valid. full_thread_id = ( feedback_models.FeedbackThreadModel.generate_full_thread_id( exploration_id, thread_id)) thread = feedback_models.FeedbackThreadModel.get(full_thread_id) message_id = feedback_models.FeedbackMessageModel.get_message_count( exploration_id, thread_id) msg = feedback_models.FeedbackMessageModel.create(exploration_id, thread_id, message_id) msg.thread_id = full_thread_id msg.message_id = message_id msg.author_id = author_id if updated_status: if message_id == 0: # New thread. event_services.FeedbackThreadCreatedEventHandler.record( thread.exploration_id) else: # Thread status changed. event_services.FeedbackThreadStatusChangedEventHandler.record( thread.exploration_id, thread.status, updated_status) msg.updated_status = updated_status if updated_subject: msg.updated_subject = updated_subject msg.text = text msg.received_via_email = received_via_email msg.put() # Update the message count in the thread. if thread.message_count is not None: thread.message_count += 1 else: thread.message_count = ( feedback_models.FeedbackMessageModel.get_message_count( exploration_id, thread_id)) # We do a put() even if the status and subject are not updated, so that the # last_updated time of the thread reflects the last time a message was # added to it. old_status = thread.status if message_id != 0 and (updated_status or updated_subject): if updated_status and updated_status != thread.status: thread.status = updated_status if updated_subject and updated_subject != thread.subject: thread.subject = updated_subject new_status = thread.status thread.put() if (user_services.is_user_registered(author_id) and feconf.CAN_SEND_EMAILS and feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS): # send feedback message email if user is registered. _add_message_to_email_buffer(author_id, exploration_id, thread_id, message_id, len(text), old_status, new_status) if author_id: subscription_services.subscribe_to_thread(author_id, full_thread_id) add_message_id_to_read_by_list(exploration_id, thread_id, author_id, message_id)
def create_message( exploration_id, thread_id, author_id, updated_status, updated_subject, text, received_via_email=False): """Creates a new message for the thread and subscribes the author to the thread. Args: exploration_id: str. The exploration id the thread belongs to. thread_id: str. The thread id the message belongs to. author_id: str. The author id who creates this message. updated_status: str, one of STATUS_CHOICES. New thread status. Must be supplied if this is the first message of a thread. For the rest of the thread, should exist only when the status changes. updated_subject: str. New thread subject. Must be supplied if this is the first message of a thread. For the rest of the thread, should exist only when the subject changes. text: str. The text of the feedback message. This may be ''. received_via_email: bool. Whether new message is received via email or web. """ from core.domain import event_services # Get the thread at the outset, in order to check that the thread_id passed # in is valid. full_thread_id = ( feedback_models.FeedbackThreadModel.generate_full_thread_id( exploration_id, thread_id)) thread = feedback_models.FeedbackThreadModel.get(full_thread_id) message_id = feedback_models.FeedbackMessageModel.get_message_count( exploration_id, thread_id) msg = feedback_models.FeedbackMessageModel.create( exploration_id, thread_id, message_id) msg.thread_id = full_thread_id msg.message_id = message_id msg.author_id = author_id if updated_status: if message_id == 0: # New thread. event_services.FeedbackThreadCreatedEventHandler.record( thread.exploration_id) else: # Thread status changed. event_services.FeedbackThreadStatusChangedEventHandler.record( thread.exploration_id, thread.status, updated_status) msg.updated_status = updated_status if updated_subject: msg.updated_subject = updated_subject msg.text = text msg.received_via_email = received_via_email msg.put() # We do a put() even if the status and subject are not updated, so that the # last_updated time of the thread reflects the last time a message was # added to it. old_status = thread.status if message_id != 0 and (updated_status or updated_subject): if updated_status and updated_status != thread.status: thread.status = updated_status if updated_subject and updated_subject != thread.subject: thread.subject = updated_subject new_status = thread.status thread.put() if (user_services.is_user_registered(author_id) and feconf.CAN_SEND_EMAILS and feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS): # send feedback message email if user is registered. _add_message_to_email_buffer( author_id, exploration_id, thread_id, message_id, len(text), old_status, new_status) if author_id: subscription_services.subscribe_to_thread(author_id, full_thread_id)
def create_message(thread_id, author_id, updated_status, updated_subject, text, received_via_email=False): """Creates a new message for the thread and subscribes the author to the thread. Args: thread_id: str. The thread id the message belongs to. author_id: str. The author id who creates this message. updated_status: str. One of STATUS_CHOICES. New thread status. Must be supplied if this is the first message of a thread. For the rest of the thread, should exist only when the status changes. updated_subject: str. New thread subject. Must be supplied if this is the first message of a thread. For the rest of the thread, should exist only when the subject changes. text: str. The text of the feedback message. This may be ''. received_via_email: bool. Whether new message is received via email or web. """ from core.domain import event_services # Get the thread at the outset, in order to check that the thread_id passed # in is valid. thread = feedback_models.GeneralFeedbackThreadModel.get(thread_id) message_id = feedback_models.GeneralFeedbackMessageModel.get_message_count( thread_id) message = feedback_models.GeneralFeedbackMessageModel.create( thread_id, message_id) message.thread_id = thread_id message.message_id = message_id message.author_id = author_id message.text = text message.received_via_email = received_via_email if updated_status: message.updated_status = updated_status if message_id == 0: # New thread. if thread.entity_type == feconf.ENTITY_TYPE_EXPLORATION: event_services.FeedbackThreadCreatedEventHandler.record( thread.entity_id) else: # Thread status changed. if thread.entity_type == feconf.ENTITY_TYPE_EXPLORATION: event_services.FeedbackThreadStatusChangedEventHandler.record( thread.entity_id, thread.status, updated_status) if updated_subject: message.updated_subject = updated_subject message.put() # Update the message data cache of the thread. if text: thread.last_nonempty_message_text = text thread.last_nonempty_message_author_id = author_id if thread.message_count is not None: thread.message_count += 1 else: thread.message_count = (feedback_models.GeneralFeedbackMessageModel. get_message_count(thread_id)) # We do a put() even if the status and subject are not updated, so that the # last_updated time of the thread reflects the last time a message was added # to it. old_status = thread.status if message_id != 0 and (updated_status or updated_subject): if updated_status and updated_status != thread.status: thread.status = updated_status if updated_subject and updated_subject != thread.subject: thread.subject = updated_subject new_status = thread.status thread.put() # We do a put on the suggestion linked (if it exists) to the thread, so that # the last_updated time changes to show that there is activity in the # thread. if thread.has_suggestion: suggestion_id = thread_id suggestion = ( suggestion_models.GeneralSuggestionModel.get_by_id(suggestion_id)) # As the thread is created before the suggestion, for the first message # we need not update the suggestion. if suggestion: suggestion.put() if (feconf.CAN_SEND_EMAILS and (feconf.CAN_SEND_FEEDBACK_MESSAGE_EMAILS and user_services.is_user_registered(author_id))): _add_message_to_email_buffer(author_id, thread_id, message_id, len(text), old_status, new_status) if author_id: subscription_services.subscribe_to_thread(author_id, thread_id) add_message_id_to_read_by_list(thread_id, author_id, message_id)