def notify_new_recommendation(recommendation_set: object, app_ns: str): ''' Sends an SNS notification indicating that a new recommendation has been generated Parameters ---------- recommendation_set: object the SecurityRecommendationSet object repr app_ns: str The application namespace supplied to the command line used to identify the appropriate CloudFormation exports ''' recommnedation_month = parser.parse(recommendation_set.model['valid_to']) formatted_ticker_message = "" for security in recommendation_set.model['securities_set']: formatted_ticker_message += "Ticker Symbol: %s\n" % security[ 'ticker_symbol'] sns_topic_arn = aws_service_wrapper.cf_read_export_value( constants.sns_app_notifications_topic_arn(app_ns)) subject = "New Stock Recommendation Available" message = "A New Stock Recommendation is available for the month of %s\n" % recommnedation_month.strftime( "%B, %Y") message += "\n\n" message += formatted_ticker_message log.info("Publishing Recommendation set to SNS topic: %s" % sns_topic_arn) aws_service_wrapper.sns_publish_notification(sns_topic_arn, subject, message)
def publish_current_returns(updated_portfolio: object, updated: bool, app_ns: str): ''' publishes current returns as a SNS notifcation, given an updated portfolio ''' sns_topic_arn = aws_service_wrapper.cf_read_export_value( constants.sns_app_notifications_topic_arn(app_ns)) subject = "Portfolio Update - " if updated == True: subject = "Stock Advisor Update - New Portfolio was created" else: subject = "Stock Advisor Update - Portfolio Returns" creation_date = parser.parse(updated_portfolio.model['creation_date']) price_date = parser.parse(updated_portfolio.model['price_date']) message = "Portfolio was created on: %s\n" % datetime.strftime( creation_date.astimezone(get_localzone()), '%Y/%m/%d %I:%M %p (%Z)') message += "Price date is: %s\n\n" % datetime.strftime( price_date, '%Y/%m/%d') for security in updated_portfolio.model['current_portfolio']['securities']: message += "Symbol: %s\n" % security['ticker_symbol'] message += "Purchase Price: %.2f\n" % security[ 'purchase_price'] message += "Current Price: %.2f (%+d%%)\n" % ( security['current_price'], round(security['current_returns'] * 100)) message += "\n" log.info("Publishing portfolio update to SNS topic: %s" % sns_topic_arn) aws_service_wrapper.sns_publish_notification( sns_topic_arn, subject, message)
def from_s3_bucket(cls, ticker_object_name: str, app_ns: str): ''' Creates a TickerFile object instane based on an S3 bucket. The bucket is detrmined by looking at the system's ClouFormation exports. Parameters ---------- ticker_object_name : str S3 object name app_ns : str Application namespace used to identify the appropriate CloudFormation exports ''' s3_object_path = "%s/%s" % (constants.S3_TICKER_FILE_FOLDER_PREFIX, ticker_object_name) destination_path = "%s/%s" % (constants.APP_DATA_DIR, ticker_object_name) log.debug( "Reading S3 Data Bucket location from CloudFormation Exports") s3_data_bucket_name = aws_service_wrapper.cf_read_export_value( constants.s3_data_bucket_export_name(app_ns)) util.create_dir(constants.APP_DATA_DIR) log.debug("Downloading s3://%s --> %s" % (s3_object_path, destination_path)) try: aws_service_wrapper.s3_download_object(s3_data_bucket_name, s3_object_path, destination_path) except AWSError as awe: if "(404)" in str(awe) and "Not Found" in str(awe): log.debug( "File not found in S3. Looking for local alternatives") # Attempt to upload a local copy of the file if it exists local_ticker_path = '%s/%s' % (constants.TICKER_DATA_DIR, ticker_object_name) if os.path.isfile(local_ticker_path): log.debug("Attempting to upload %s --> s3://%s/%s" % (local_ticker_path, s3_data_bucket_name, s3_object_path)) aws_service_wrapper.s3_upload_object( local_ticker_path, s3_data_bucket_name, s3_object_path) return cls.from_local_file(constants.TICKER_DATA_DIR, ticker_object_name) else: log.debug("No local alternatives found") raise awe else: raise awe return cls.from_local_file(constants.APP_DATA_DIR, ticker_object_name)
def try_from_s3(cls, config_filename: str, app_ns: str): ''' Downloads a configuration file from S3 given the supplied file (object) name and application namespace used to read cloudformation exports. If the file does not exist use the local one and upload it to S3 ''' try: s3_data_bucket_name = aws_service_wrapper.cf_read_export_value( constants.s3_data_bucket_export_name(app_ns)) s3_object_name = "%s/%s" % (constants.S3_CONFIG_OLDER_PREFIX, config_filename) dest_filename = "%s.s3download" % config_filename dest_path = "%s%s" % (constants.CONFIG_FILE_PATH, dest_filename) log.info("Downloading Configuration File: s3://%s/%s --> %s" % (s3_data_bucket_name, s3_object_name, dest_path)) aws_service_wrapper.s3_download_object(s3_data_bucket_name, s3_object_name, dest_path) return cls.from_local_config(dest_filename) except AWSError as awe: if awe.resource_not_found(): log.debug( "Configuration not found in S3. Looking for local alternatives" ) # Attempt to upload a local copy of the configuration if it # exists local_configuration_path = "%s%s" % ( constants.CONFIG_FILE_PATH, config_filename) if os.path.isfile(local_configuration_path): log.debug("Attempting to upload %s --> s3://%s/%s" % (local_configuration_path, s3_data_bucket_name, s3_object_name)) aws_service_wrapper.s3_upload_object( local_configuration_path, s3_data_bucket_name, s3_object_name) return cls.from_local_config(config_filename) else: log.debug("No local alternatives found") raise awe else: raise awe
def from_s3(cls, app_ns: str, s3_object_name: str): ''' loads the model from S3 using preconfigured object names ''' util.create_dir(constants.APP_DATA_DIR) s3_data_bucket_name = aws_service_wrapper.cf_read_export_value( constants.s3_data_bucket_export_name(app_ns)) s3_object_path = "%s/%s" % (cls.model_s3_folder_prefix, s3_object_name) dest_path = "%s/%s" % (constants.APP_DATA_DIR, s3_object_name) log.info( "Downloading %s: s3://%s/%s --> %s" % (cls.model_name, s3_data_bucket_name, s3_object_path, dest_path)) aws_service_wrapper.s3_download_object(s3_data_bucket_name, s3_object_path, dest_path) return cls.from_local_file(dest_path)
def try_from_s3(cls, app_ns: str, ticker_file_name: str): ''' Override for BaseModel.from_s3 loads the model from S3. If one is not found look for a local alternative and upload it to the same bucket. This is done to eliminate the need to pre-populate S3 with any data when the application is first installed. ''' try: return cls.from_s3(app_ns, ticker_file_name) except AWSError as awe: if awe.resource_not_found(): log.debug( "File not found in S3. Looking for local alternatives") s3_object_path = "%s/%s" % (cls.model_s3_folder_prefix, ticker_file_name) log.debug( "Reading S3 Data Bucket location from CloudFormation Exports" ) s3_data_bucket_name = aws_service_wrapper.cf_read_export_value( constants.s3_data_bucket_export_name(app_ns)) # Attempt to upload a local copy of the file if it exists local_ticker_path = '%s/%s' % (constants.TICKER_DATA_DIR, ticker_file_name) if os.path.isfile(local_ticker_path): log.debug("Attempting to upload %s --> s3://%s/%s" % (local_ticker_path, s3_data_bucket_name, s3_object_path)) aws_service_wrapper.s3_upload_object( local_ticker_path, s3_data_bucket_name, s3_object_path) return cls.from_local_file(local_ticker_path) else: log.debug("No local alternatives found") raise awe else: raise awe
def save_to_s3(self, app_ns: str): ''' Uploads the model to S3 Parameters ---------- app_ns : str The application namespace supplied to the command line used to identify the appropriate CloudFormation exports ''' self.validate_model() s3_data_bucket_name = aws_service_wrapper.cf_read_export_value( constants.s3_data_bucket_export_name(app_ns)) object_name = "%s/%s" % (self.model_s3_folder_prefix, self.model_s3_object_name) log.info("Uploading %s to S3: s3://%s/%s" % (self.model_name, s3_data_bucket_name, object_name)) aws_service_wrapper.s3_upload_ascii_string( util.format_dict(self.model), s3_data_bucket_name, object_name)
def notify_new_recommendation(notification_list: list, app_ns: str): ''' Sends an SNS notification indicating that a new recommendation has been generated Parameters ---------- notification_list: list List of new SecurityRecommendationSet objects that require notifications app_ns: str The application namespace supplied to the command line used to identify the appropriate CloudFormation exports ''' if notification_list == None or len(notification_list) == 0: return message = "" sns_topic_arn = aws_service_wrapper.cf_read_export_value( constants.sns_app_notifications_topic_arn(app_ns)) subject = "New Stock Recommendation Available" for recommendation_set in notification_list: formatted_ticker_message = "" for security in recommendation_set.model['securities_set']: formatted_ticker_message += "Ticker Symbol: %s\n" % security[ 'ticker_symbol'] message += "A New Stock Recommendation is available for the following trading strategy %s\n" % recommendation_set.model[ 'strategy_name'] message += "\n" message += formatted_ticker_message message += "\n\n" log.info("Publishing Recommendation set to SNS topic: %s" % sns_topic_arn) aws_service_wrapper.sns_publish_notification(sns_topic_arn, subject, message)