def make_control_nonaction(self, experiment_thing, submission, group="control"): if(self.submission_acceptable(submission) == False): self.log.info("{0}: Submission {1} is unacceptable. Declining to make nonaction".format( self.__class__.__name__, submission.id)) return None metadata = json.loads(experiment_thing.metadata_json) treatment_arm = int(metadata['randomization']['treatment']) condition = metadata['condition'] experiment_action = ExperimentAction( experiment_id = self.experiment.id, praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), action = "Intervention", action_object_type = ThingType.SUBMISSION.value, action_object_id = submission.id, metadata_json = json.dumps({"group":group, "condition":condition, "arm": "arm_"+str(treatment_arm)}) ) self.db_session.add(experiment_action) self.db_session.commit() self.log.info("{0}: Experiment {1} applied arm {2} to post {3} (condition = {4})".format( self.__class__.__name__, self.experiment_name, treatment_arm, submission.id, condition )) return experiment_action.id
def make_control_nonaction(self, experiment_thing, submission, group="control"): if (self.submission_acceptable(submission) == False): return None metadata = json.loads(experiment_thing.metadata_json) treatment_arm = int(metadata['randomization']['treatment']) condition = metadata['condition'] experiment_action = ExperimentAction( experiment_id=self.experiment.id, praw_key_id=PrawKey.get_praw_id(ENV, self.experiment_name), action="Intervention", action_object_type=ThingType.SUBMISSION.value, action_object_id=submission.id, metadata_json=json.dumps({ "group": group, "condition": condition, "arm": "arm_" + str(treatment_arm) })) self.db_session.add(experiment_action) self.db_session.commit() self.log.info( "{0}: Experiment {1} applied arm {2} to post {3} (condition = {4})" .format(self.__class__.__name__, self.experiment_name, treatment_arm, submission.id, condition)) return experiment_action.id
def connect(self, controller="Main"): r = None #Praw Connection Object handler = MultiprocessHandler() # Check the Database for a Stored Praw Key db_praw_id = PrawKey.get_praw_id(self.env, controller) pk = self.db_session.query(PrawKey).filter_by(id=db_praw_id).first() r = praw.Reddit(user_agent="Test version of CivilServant by u/natematias", handler=handler) access_information = {} if(pk is None): with open(os.path.join(self.base_dir, "config","access_information_{environment}.pickle".format(environment=self.env)), "rb") as fp: access_information = pickle.load(fp) else: access_information['access_token'] = pk.access_token access_information['refresh_token'] = pk.refresh_token access_information['scope'] = json.loads(pk.scope) r.set_access_credentials(**access_information) new_access_information = {} update_praw_key = False # SAVE OR UPDATE THE ACCESS TOKEN IF NECESSARY if(pk is None): pk = PrawKey(id=db_praw_id, access_token = r.access_token, refresh_token = r.refresh_token, scope = json.dumps(list(r._authentication)), authorized_username = r.user.json_dict['name'], authorized_user_id = r.user.json_dict['id']) self.db_session.add(pk) elif(access_information['access_token'] != r.access_token or access_information['refresh_token'] != r.refresh_token): pk.access_token = r.access_token pk.refresh_token = r.refresh_token pk.scope = json.dumps(list(r._authentication)) self.db_session.commit() return r
def submission_acceptable(self, submission): if (submission is None): ## TODO: Determine what to do if you can't find the post self.log.error("{0}: Can't find experiment {1} post {2}".format( self.__class__.__name__, self.subreddit, submission.id)) return False ## Avoid Acting if the Intervention has already been recorded if (self.db_session.query(ExperimentAction).filter( and_( ExperimentAction.experiment_id == self.experiment.id, ExperimentAction.action_object_type == ThingType.SUBMISSION.value, ExperimentAction.action_object_id == submission.id, ExperimentAction.action == "Intervention")).count() > 0): self.log.info( "{0}: Experiment {1} post {2} already has an Intervention recorded" .format(self.__class__.__name__, self.experiment_name, submission.id)) return False ## possible comment texts to avoid all_experiment_messages = [] for label, condition in self.experiment_settings['conditions'].items(): all_experiment_messages = all_experiment_messages + list( condition['arms'].values()) ## Avoid Acting if an identical sticky comment already exists for comment in submission.comments: if (hasattr(comment, "stickied") and comment.stickied and (comment.body in all_experiment_messages)): self.log.info( "{0}: Experiment {1} post {2} already has a sticky comment {2}" .format(self.__class__.__name__, self.experiment_name, submission.id, comment.id)) return False ## Avoid Acting if the submission is not recent enough curtime = time.time() # if((curtime - submission.created_utc) > 10800): if ((curtime - submission.created_utc) > self.max_eligibility_age): self.log.info( "{0}: Submission created_utc {1} is {2} seconds greater than current time {3}, exceeding the max eligibility age of {4}. Declining to Add to the Experiment" .format(self.__class__.__name__, submission.created_utc, curtime - submission.created_utc, curtime, self.max_eligibility_age)) experiment_action = ExperimentAction( experiment_id=self.experiment.id, praw_key_id=PrawKey.get_praw_id(ENV, self.experiment_name), action="NonIntervention:MaxAgeExceeded", action_object_type=ThingType.SUBMISSION.value, action_object_id=submission.id) return False return True
def make_sticky_post(self, submission): if(self.submission_acceptable(submission) == False): return None ## FIRST STEP: DETERMINE AMA STATUS ## TODO: refactor to check this only once is_ama = self.is_ama(submission) if(is_ama): comment_text = self.ama_comment_text else: comment_text = self.nonama_comment_text comment = submission.add_comment(comment_text) distinguish_results = comment.distinguish(sticky=True) self.log.info("Experiment {0} applied treatment to post {1} (AMA = {2}). Result: {3}".format( self.experiment_name, submission.id, is_ama, str(distinguish_results) )) experiment_action = ExperimentAction( experiment_id = self.experiment.id, praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), action_subject_type = ThingType.COMMENT.value, action_subject_id = comment.id, action = "Intervention", action_object_type = ThingType.SUBMISSION.value, action_object_id = submission.id, metadata_json = json.dumps({"group":"treatment", "action_object_created_utc":comment.created_utc}) ) comment_thing = ExperimentThing( experiment_id = self.experiment.id, object_created = datetime.datetime.fromtimestamp(comment.created_utc), object_type = ThingType.COMMENT.value, id = comment.id, metadata_json = json.dumps({"group":"treatment","submission_id":submission.id}) ) self.db_session.add(comment_thing) self.db_session.add(experiment_action) self.db_session.commit() return distinguish_results
def make_control_nonaction(self, submission): if(self.submission_acceptable(submission) == False): return None experiment_action = ExperimentAction( experiment_id = self.experiment.id, praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), action = "Intervention", action_object_type = ThingType.SUBMISSION.value, action_object_id = submission.id, metadata_json = json.dumps({"group":"control"}) ) self.db_session.add(experiment_action) self.db_session.commit() self.log.info("Experiment {0} applied control condition to post {1}".format( self.experiment_name, submission.id )) return experiment_action.id
def submission_acceptable(self, submission): if(submission is None): ## TODO: Determine what to do if you can't find the post self.log.error("Can't find experiment {0} post {1}".format(self.subreddit, submission.id)) return False ## Avoid Acting if the Intervention has already been recorded if(self.db_session.query(ExperimentAction).filter(and_( ExperimentAction.experiment_id == self.experiment.id, ExperimentAction.action_object_type == ThingType.SUBMISSION.value, ExperimentAction.action_object_id == submission.id, ExperimentAction.action == "Intervention")).count() > 0): self.log.info("Experiment {0} post {1} already has an Intervention recorded".format( self.experiment_name, submission.id)) return False ## Avoid Acting if an identical sticky comment already exists for comment in submission.comments: if(comment.stickied and (comment.body == self.ama_comment_text or comment.body == self.nonama_comment_text)): self.log.info("Experiment {0} post {1} already has a sticky comment {2}".format( self.experiment_name, submission.id, comment.id)) return False ## Avoid Acting if the submission is not recent enough curtime = time.time() # if((curtime - submission.created_utc) > 10800): if((curtime - submission.created_utc) > self.max_eligibility_age): self.log.info("Submission created_utc {0} is {1} seconds greater than current time {2}, exceeding the max eligibility age of {3}. Declining to Add to the Experiment".format( submission.created_utc, curtime - submission.created_utc, curtime, self.max_eligibility_age)) experiment_action = ExperimentAction( experiment_id = self.experiment.id, praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), action = "NonIntervention:MaxAgeExceeded", action_object_type = ThingType.SUBMISSION.value, action_object_id = submission.id ) return False return True
def connect(self, controller="Main"): r = None #Praw Connection Object handler = MultiprocessHandler() # Check the Database for a Stored Praw Key db_praw_id = PrawKey.get_praw_id(self.env, controller) pk = self.db_session.query(PrawKey).filter_by(id=db_praw_id).first() r = praw.Reddit( user_agent="Test version of CivilServant by u/natematias", handler=handler) access_information = {} if (pk is None): with open( os.path.join( self.base_dir, "config", "access_information_{environment}.pickle".format( environment=self.env)), "rb") as fp: access_information = pickle.load(fp) else: access_information['access_token'] = pk.access_token access_information['refresh_token'] = pk.refresh_token access_information['scope'] = json.loads(pk.scope) r.set_access_credentials(**access_information) new_access_information = {} update_praw_key = False # SAVE OR UPDATE THE ACCESS TOKEN IF NECESSARY if (pk is None): pk = PrawKey(id=db_praw_id, access_token=r.access_token, refresh_token=r.refresh_token, scope=json.dumps(list(r._authentication)), authorized_username=r.user.json_dict['name'], authorized_user_id=r.user.json_dict['id']) self.db_session.add(pk) elif (access_information['access_token'] != r.access_token or access_information['refresh_token'] != r.refresh_token): pk.access_token = r.access_token pk.refresh_token = r.refresh_token pk.scope = json.dumps(list(r._authentication)) self.db_session.commit() return r
def set_stylesheet(self, condition, arm): arms = self.experiment_settings['conditions'][condition]['arms'] intervention_line = arms[arm] found_code = False stylesheet_data = self.r.get_stylesheet(self.subreddit) if "stylesheet" in stylesheet_data.keys(): line_list = [] for line in stylesheet_data['stylesheet'].split("\n"): ## IF A LINE FROM THE STUDY IS FOUND, ## REPLACE IT WITH THE INTERVENTION if line in arms.values(): line = intervention_line found_code = True line_list.append(line) ## IF THE CODE IS NOT FOUND, ADD IT TO THE END if (found_code != True): line_list.append("/* CivilServantBot Experiment CSS */") line_list.append(intervention_line) line_list.append("") new_stylesheet = "\n".join(line_list) result = self.r.set_stylesheet(self.subreddit, new_stylesheet) if ('errors' in result.keys() and len(result['errors']) == 0): self.log.info( "{0}: Experiment {1}: Applied Arm {3} of Condition {4} in {2}". format(self.__class__.__name__, self.experiment.id, self.subreddit, arm, condition)) experiment_action = ExperimentAction( experiment_id=self.experiment.id, praw_key_id=PrawKey.get_praw_id(ENV, self.experiment_name), action="Intervention", action_object_type=ThingType.STYLESHEET.value, action_object_id=None, metadata_json=json.dumps({ "arm": arm, "condition": condition })) self.db_session.add(experiment_action) self.db_session.commit() else: self.log.error( "{0}: Experiment {1}: Failed to apply Arm {2} of Condition {3}. Reddit errors: {4}" .format(self.__class__.__name__, self.experiment.id, self.subreddit, arm, condition, ", ".join(result['errors']))) # experiment_action = ExperimentAction( # experiment_id = self.experiment.id, # praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), # action = "NonIntervention:PrawError.{0}.{1}".format(condition,arm), # action_object_type = ThingType.STYLESHEET.value, # action_object_id = None # ) # self.db_session.add(experiment_action) # self.db_session.commit() ## IF WE FAILED TO APPLY THE INTERVENTION, ROLL BACK THAT RANDOMIZATION self.experiment_settings['conditions'][condname][ 'next_randomization'] -= 1 self.experiment.settings_json = json.dumps( self.experiment_settings) self.db_session.commit() ## TO HELP WITH TESTING, RETURN THE FULL TEXT OF THE STYLESHEET return new_stylesheet
def make_sticky_post(self, experiment_thing, submission): if (self.submission_acceptable(submission) == False): return None metadata = json.loads(experiment_thing.metadata_json) treatment_arm = int(metadata['randomization']['treatment']) condition = metadata['condition'] comment_text = self.experiment_settings['conditions'][condition][ 'arms']["arm_" + str(treatment_arm)] ## THIS METHOD IS FOR USE WHEN INTENDING TO TEST AN EXPERIMENT WITHOUT ## MAKING ANY ACTUAL COMMENTS ON REDDIT #def _TEST_add_comment(): # import collections, random, string # return collections.namedtuple("Comment", ["id", "created_utc"])( # "_" + "".join(random.choice(string.ascii_lowercase) for i in range(6)), # datetime.datetime.utcnow().timestamp()) ## TO USE THIS DRY RUN TEST CODE, UNCOMMENT THE FOLLOWING TWO LINES ## AND COMMENT OUT THE TWO LINES AFTER IT #comment = _TEST_add_comment() #submission.add_comment(comment_text) ## distinguish_results = "DUMMY DISTINGUISH: Assume successful" comment = submission.add_comment(comment_text) distinguish_results = comment.distinguish(sticky=True) self.log.info( "{0}: Experiment {1} applied arm {2} to post {3} (condition = {4}). Result: {5}" .format(self.__class__.__name__, self.experiment_name, treatment_arm, submission.id, condition, str(distinguish_results))) experiment_action = ExperimentAction( experiment_id=self.experiment.id, praw_key_id=PrawKey.get_praw_id(ENV, self.experiment_name), action_subject_type=ThingType.COMMENT.value, action_subject_id=comment.id, action="Intervention", action_object_type=ThingType.SUBMISSION.value, action_object_id=submission.id, metadata_json=json.dumps({ "group": "treatment", "condition": condition, "arm": "arm_" + str(treatment_arm), "randomization": metadata['randomization'], "action_object_created_utc": comment.created_utc })) comment_thing = ExperimentThing( experiment_id=self.experiment.id, object_created=datetime.datetime.fromtimestamp( comment.created_utc), object_type=ThingType.COMMENT.value, id=comment.id, metadata_json=json.dumps({ "group": "treatment", "arm": "arm_" + str(treatment_arm), "condition": condition, "randomization": metadata['randomization'], "submission_id": submission.id })) self.db_session.add(comment_thing) self.db_session.add(experiment_action) self.db_session.commit() return distinguish_results
def set_stylesheet(self, condition, arm): arms = self.experiment_settings['conditions'][condition]['arms'] intervention_line = arms[arm] found_code = False stylesheet_data = self.r.get_stylesheet(self.subreddit) if "stylesheet" in stylesheet_data.keys(): line_list = [] for line in stylesheet_data['stylesheet'].split("\n"): ## IF A LINE FROM THE STUDY IS FOUND, ## REPLACE IT WITH THE INTERVENTION if line in arms.values(): line = intervention_line found_code = True line_list.append(line) ## IF THE CODE IS NOT FOUND, ADD IT TO THE END if(found_code!=True): line_list.append("/* CivilServantBot Experiment CSS */") line_list.append(intervention_line) line_list.append("") new_stylesheet = "\n".join(line_list) result = self.r.set_stylesheet(self.subreddit, new_stylesheet) if('errors' in result.keys() and len(result['errors'])==0): self.log.info("{0}: Experiment {1}: Applied Arm {3} of Condition {4} in {2}".format( self.__class__.__name__, self.experiment.id, self.subreddit, arm, condition)) experiment_action = ExperimentAction( experiment_id = self.experiment.id, praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), action = "Intervention", action_object_type = ThingType.STYLESHEET.value, action_object_id = None, metadata_json = json.dumps({"arm":arm, "condition":condition}) ) self.db_session.add(experiment_action) self.db_session.commit() else: self.log.error("{0}: Experiment {1}: Failed to apply Arm {2} of Condition {3}. Reddit errors: {4}".format( self.__class__.__name__, self.experiment.id, self.subreddit, arm,condition, ", ".join(result['errors']))) # experiment_action = ExperimentAction( # experiment_id = self.experiment.id, # praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), # action = "NonIntervention:PrawError.{0}.{1}".format(condition,arm), # action_object_type = ThingType.STYLESHEET.value, # action_object_id = None # ) # self.db_session.add(experiment_action) # self.db_session.commit() ## IF WE FAILED TO APPLY THE INTERVENTION, ROLL BACK THAT RANDOMIZATION self.experiment_settings['conditions'][condname]['next_randomization'] -= 1 self.experiment.settings_json = json.dumps(self.experiment_settings) self.db_session.commit() ## TO HELP WITH TESTING, RETURN THE FULL TEXT OF THE STYLESHEET return new_stylesheet
def make_sticky_post(self, experiment_thing, submission): if(self.submission_acceptable(submission) == False): self.log.info("{0}: Submission {1} is unacceptable. Declining to make sticky post".format( self.__class__.__name__, submission.id)) return None metadata = json.loads(experiment_thing.metadata_json) treatment_arm = int(metadata['randomization']['treatment']) condition = metadata['condition'] comment_text = self.experiment_settings['conditions'][condition]['arms']["arm_" + str(treatment_arm)] ## THIS METHOD IS FOR USE WHEN INTENDING TO TEST AN EXPERIMENT WITHOUT ## MAKING ANY ACTUAL COMMENTS ON REDDIT #def _TEST_add_comment(): # import collections, random, string # return collections.namedtuple("Comment", ["id", "created_utc"])( # "_" + "".join(random.choice(string.ascii_lowercase) for i in range(6)), # datetime.datetime.utcnow().timestamp()) ## TO USE THIS DRY RUN TEST CODE, UNCOMMENT THE FOLLOWING TWO LINES ## AND COMMENT OUT THE TWO LINES AFTER IT #comment = _TEST_add_comment() #submission.add_comment(comment_text) ## distinguish_results = "DUMMY DISTINGUISH: Assume successful" comment = submission.add_comment(comment_text) distinguish_results = comment.distinguish(sticky=True) self.log.info("{0}: Experiment {1} applied arm {2} to post {3} (condition = {4}). Result: {5}".format( self.__class__.__name__, self.experiment_name, treatment_arm, submission.id, condition, str(distinguish_results) )) experiment_action = ExperimentAction( experiment_id = self.experiment.id, praw_key_id = PrawKey.get_praw_id(ENV, self.experiment_name), action_subject_type = ThingType.COMMENT.value, action_subject_id = comment.id, action = "Intervention", action_object_type = ThingType.SUBMISSION.value, action_object_id = submission.id, metadata_json = json.dumps({"group":"treatment", "condition":condition, "arm":"arm_" + str(treatment_arm), "randomization": metadata['randomization'], "action_object_created_utc":comment.created_utc}) ) comment_thing = ExperimentThing( experiment_id = self.experiment.id, object_created = datetime.datetime.fromtimestamp(comment.created_utc), object_type = ThingType.COMMENT.value, id = comment.id, metadata_json = json.dumps({"group":"treatment", "arm":"arm_"+str(treatment_arm), "condition":condition, "randomization": metadata['randomization'], "submission_id":submission.id}) ) self.db_session.add(comment_thing) self.db_session.add(experiment_action) self.db_session.commit() return distinguish_results