class CampaignProperties(Resource): def __init__(self): super(Resource, self).__init__() self.campaign_table = DynamoTable('campaigns') self.donation_table = DynamoTable('donations') @swagger.operation(notes='Get the properties of a campaign by ID', nickname='Get Campaign Details', parameters=[{ "name": "campaign_id", "description": "UUID of the campaign", "required": True, "allowMultiple": False, "dataType": 'string', "paramType": "path" }]) def get(self, campaign_id): """Get Campaign Details""" # Get the campaign data = {"campaign_id": campaign_id} item = None try: item = self.campaign_table.get_item(data) except IOError as e: abort(400, description=e.message) if not item: abort(404, description="Campaign '{}' not found".format(campaign_id)) item["donation_email"] = "{}@donatemates.com".format( item["campaign_id"]) # Get donor amount stats amounts = self.donation_table.query_biggest("campaign_id", campaign_id, 5, index="DonationIndex") # Get donor time stats donors = self.donation_table.query_most_recent( "campaign_id", campaign_id, "donation_on", arrow.utcnow().isoformat(), limit=5) item["large_donors"] = [{ "donor_name": x["donor_name"], "donation_cents": float(x["donation_cents"]) } for x in amounts] item["recent_donors"] = [{ "donor_name": x["donor_name"], "donation_cents": float(x["donation_cents"]) } for x in donors] # Sum donors item[ "donation_total_cents"] = self.donation_table.integer_sum_attribute( "campaign_id", campaign_id, "donation_cents") # Get charity information charity = next(c for (i, c) in enumerate(SUPPORTED_CHARITIES) if c["id"] == item["charity_id"]) item["donation_url"] = charity["donation_url"] item["charity_name"] = charity["conversational_name"] del item["secret_id"] item = clean_dynamo_response(item) return item, 200 def put(self, campaign_id): abort(403, description="Missing Authorization Key") def post(self, campaign_id): abort(403, description="Missing Authorization Key")
def process_email_handler(event, context): logger = logging.getLogger("boto3") logger.setLevel(logging.WARN) print("Received event: " + json.dumps(event, indent=2)) # Check if S3 event or CloudWatch invocation. If just keeping things hot, exit. if "Records" in event: if "s3" in event["Records"][0]: key = event["Records"][0]["s3"]["object"]["key"] bucket = event["Records"][0]["s3"]["bucket"]["name"] else: print("Not an email. Move along...") return # Load message from S3 s3 = boto3.resource('s3') email_obj = s3.Object(bucket, key) email_mime = email_obj.get()['Body'].read().decode('utf-8') # Detect Charity for sup_charity in SUPPORTED_CHARITIES: class_ = getattr(charity, sup_charity["class"]) charity_class = class_(email_mime) # Detect charity if charity_class.is_receipt(): # Found the charity # Get campaign ID and campaign data campaign_id = charity_class.get_campaign_id() print("CAMPAIGN ID: {}".format(campaign_id)) campaign_table = DynamoTable('campaigns') campaign_key = {"campaign_id": campaign_id} campaign = campaign_table.get_item(campaign_key) if not campaign: print("WARNING: **** CAMPAIGN DOES NOT EXIST ****") dm_email = DonatematesEmail(charity_class.from_email) dm_email.send_campaign_does_not_exist() return True # Setup email sender dm_email = DonatematesEmail(charity_class.from_email, campaign["campaigner_email"]) # Get donation receipt data = charity_class.parse_email() data["receipt_id"] = data["receipt_id"].strip() # Validate this is a new donation donation_table = DynamoTable('donations') existing_receipts = donation_table.query_hash("receipt_id", data["receipt_id"], index="ReceiptIndex", limit=10) if existing_receipts: # This receipt already exists! print( "WARNING: **** Duplicate receipt detected - Campaign: {} - Receipt: {} - Bucket: {} - Key: {} ****" .format(campaign_id, data["receipt_id"], bucket, key)) # Notify user we didn't process it dm_email.send_duplicate_receipt(campaign_id, data["receipt_id"], key) return True # Add donation record data["campaign_id"] = campaign_id data["donation_on"] = arrow.utcnow().isoformat() data["email_bucket"] = bucket data["email_key"] = key print("DONATION DATA:") print(data) store_donation(data) # Get updated total donation donation_total_cents = donation_table.integer_sum_attribute( "campaign_id", campaign_id, "donation_cents") # Notify the Donor if campaign["campaign_status"] == "cancelled": # If cancelled, only notify donor and let them know the campaign isn't going on. dm_email.send_campaign_cancelled() else: # Send standard confirmation to donor dm_email.send_donation_confirmation(data["donation_cents"]) # Notify the campaigner if the campaign is active only if campaign["campaign_status"] == "active": # Update notification time (for future possible digest emails) campaign_table.update_attribute(campaign_key, "notified_on", arrow.utcnow().isoformat()) if donation_total_cents >= campaign["match_cents"]: # Update campaign status to "matched" campaign_table.update_attribute(campaign_key, "campaign_status", "matched") # Send campaign completion email! dm_email.send_campaign_matched(data["donor_name"], data["donation_cents"], donation_total_cents, campaign["match_cents"]) else: # Send normal update dm_email.send_campaign_update(data["donor_name"], data["donation_cents"], donation_total_cents, campaign["match_cents"]) # Exit return True # If you get here, you didn't successfully parse the email or it was unsupported # Save email to error bucket s3.Object('parse-fail-donatemates', '{}'.format( shortuuid.uuid())).copy_from(CopySource='{}/{}'.format(bucket, key)) # Reply to user print( "WARNING: **** Failed to detect a supported charity - Email Key: {} ****" .format(key))