Django Sending Email Alerts on only new alerts - python

I am trying to send email alerts for all new alerts that have just been created. I have tried
last_alert = Alert.objects.filter(kiosk=kiosk).last()
But that only gets the last alert and it triggers the same one all the time. It is possible to have 3 alerts be triggered at once. I am trying to implement a flag to know whether or not an alert has been sent. I'm probably using latest wrong here.
last_alert = Alert.objects.filter(kiosk=kiosk).latest('pk')
if last_alert.created_on:
alert_status = HTTP_208_ALREADY_REPORTED
send_alert_email = False
else:
alert_status = HTTP_201_CREATED
send_alert_email = True
last_alert.created_on = datetime.now(last_alert.kiosk.location.timezone)
Alert.create(kiosk=kiosk, created_on=datetime.now(last_alert.kiosk.location.timezone))
last_alert.save()
# Get Timezone aware date and time
current_dt = datetime.now().astimezone(kiosk.location.timezone)
current_time = current_dt.strftime('%I:%M %p')
current_date = current_dt.strftime('%m/%d/%Y')
email_props2 = {
'method': 'EMAIL',
'email': 'john#example.com',
'data': {
'facility': last_alert.kiosk.location.name,
'description': last_alert.description,
'pk': last_alert.pk,
'time': current_time,
'date': current_date,
'kioskName': kiosk.name,
'alert_type_display': last_alert.alert_type_display
}
}
if send_alert_email:
_send_email(
[email_props2['email']],
{'data': email_props2['data']},
ALERT_TEMPLATE_ID
)
Maybe I am approaching this problem wrong with the flag. Any help is very much appreciated.
thanks in advance

I have a solution. I added a processed field to the Alert model default it to False. Then filter all Alerts with field processed=False. Loop through all of the Alerts, if processed=False send an email, then set processed=True.
last_alert = Alert.objects.filter(kiosk=kiosk, processed=False)
# Get Timezone aware date and time
for alert in last_alert:
if alert.processed == False:
current_dt = datetime.now().astimezone(kiosk.location.timezone)
current_time = current_dt.strftime('%I:%M %p')
current_date = current_dt.strftime('%m/%d/%Y')
email_props2 = {
'method': 'EMAIL',
'email': 'john#example.com',
'data': {
'facility': alert.kiosk.location.name,
'description': alert.description,
'pk': alert.pk,
'time': current_time,
'date': current_date,
'kioskName': kiosk.name,
'alert_type_display': alert.alert_type_display
}
}
# Straight up send it, dude
_send_email(
[email_props2['email']],
{'data': email_props2['data']},
ALERT_TEMPLATE_ID
)
alert.processed = True
alert.save()

Related

Accessing nested data in a supposed dict

Alright, I'm stumped. I have googled everything I can think of from nested Dicts, Dicts inside Lists inside Dicts, to JSON referencing and have no idea how to get to this data.
I have this AWS Lambda handler that is reading Slack events and simply reversing someone's message and then spitting it out back to Slack. However, the bot can respond to itself (creating an infinite loop). I thought I had this solved, however, that was for the legacy stuff. I am Python stupid, so how do reference this data?
Data (slack_body_dict print from below):
{'token': 'NgapUeqidaGeTf4ONWkUQQiP', 'team_id': 'T7BD9RY57', 'api_app_id': 'A01LZHA7R9U', 'event': {'client_msg_id': '383aeac2-a436-4bad-8e19-7fa68facf916', 'type': 'message', 'text': 'rip', 'user': 'U7D1RQ9MM', 'ts': '1612727797.024200', 'team': 'T7BD9RY57', 'blocks': [{'type': 'rich_text', 'block_id': 'gA7K', 'elements': [{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': 'rip'}]}]}], 'channel': 'D01MK0JSNDP', 'event_ts': '1612727797.024200', 'channel_type': 'im'}, 'type': 'event_callback', 'event_id': 'Ev01MN8LJ117', 'event_time': 1612727797, 'authorizations': [{'enterprise_id': None, 'team_id': 'T7BD9RY57', 'user_id': 'U01MW6UK55W', 'is_bot': True, 'is_enterprise_install': False}], 'is_ext_shared_channel': False, 'event_context': '1-message-T7BD9RY57-D01MK0JSNDP'}
There is an 'is_bot' there under 'authorizations' I want to check. I assume this will let the bot stop responding to itself. However, for the life of me, I cannot reference it. It seems to be nested in there.
I have tried the following:
def lambda_handler(api_event, api_context):
print(f"Received event:\n{api_event}\nWith context:\n{api_context}")
# Grab relevant information form the api_event
slack_body_raw = api_event.get('body')
slack_body_dict = json.loads(slack_body_raw)
request_headers = api_event["headers"]
print(f"!!!!!!!!!!!!!!!!!!!!!!!body_dict:\n{slack_body_dict}")
print(f"#######################is_bot:\n{slack_body_dict('is_bot')}")
print(f"#######################is_bot:\n{slack_body_dict("is_bot")}")
print(f"#######################is_bot:\n{slack_body_dict(['is_bot']}")
print(f"#######################is_bot:\n{slack_body_dict(["is_bot"]}")
print(f"#######################is_bot:\n{slack_body_dict['authorizations']['is_bot']}")
As you can see I have absolutely no clue how to get to that variable to tell if it is true or false. Every 'is_bot' print reference results in an error. Can someone tell me how to reference that variable or give me something to google? Appreciate it. Code is below in case it is relevant.
import json
import os
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
def is_challenge(slack_event_body: dict) -> bool:
"""Is the event a challenge from slack? If yes return the correct response to slack
Args:
slack_event_body (dict): The slack event JSON
Returns:
returns True if it is a slack challenge event returns False otherwise
"""
if "challenge" in slack_event_body:
LOGGER.info(f"Challenge Data: {slack_event_body['challenge']}")
return True
return False
def lambda_handler(api_event, api_context):
# Grab relevant information form the api_event
slack_body_raw = api_event.get('body')
slack_body_dict = json.loads(slack_body_raw)
request_headers = api_event["headers"]
# This is to appease the slack challenge gods
if is_challenge(slack_body_dict):
challenge_response_body = {
"challenge": slack_body_dict["challenge"]
}
return helpers.form_response(200, challenge_response_body)
# This parses the slack body dict to get the event JSON
slack_event_dict = slack_body_dict["event"]
# Build the slack client.
slack_client = WebClient(token=os.environ['BOT_TOKEN'])
# We need to discriminate between events generated by
# the users, which we want to process and handle,
# and those generated by the bot.
if slack_body_dict['is_bot']: #THIS IS GIVING ME THE ERROR. I WANT TO CHECK IF BOT HERE.
logging.warning("Ignore bot event")
else:
# Get the text of the message the user sent to the bot,
# and reverse it.
text = slack_event_dict["text"]
reversed_text = text[::-1]
# Get the ID of the channel where the message was posted.
channel_id = slack_event_dict["channel"]
try:
response = slack_client.chat_postMessage(
channel=channel_id,
text=reversed_text
)
except SlackApiError as e:
# You will get a SlackApiError if "ok" is False
assert e.response["error"] # str like 'invalid_auth', 'channel_not_found'
The structure of the data is:
{
"authorizations": [
{
"is_bot": true
}
]
}
So you would need to first index "authorizations", then to get the first item 0, and lastly "is_bot".
data["authorizations"][0]["is_bot"]
Alternativly, you could iterate over all the authorizations and check if any (or all) of them are marked as a bot like so:
any(auth["is_bot"] for auth in slack_body_dict["authorizations"])

Python MagicMock.return_value returning MagicMock instead of return_value

I have a function that verifies if a given input string is a proper GCP zone:
def validate_zone(compute, project_id, zone):
try:
zone_response = compute.zones().get(project=project_id, zone=zone).execute()
print(zone_response)
print(zone_response.return_value)
if ['status'] in zone_response:
zone_details = {
'status': zone_response['status'],
'region': zone_response['region'],
'name': zone_response['name']
}
return zone_details
else:
return "Zone {} not found for project {}".format(zone, project_id)
except HttpError as error:
print("Error calling zone {}: \n {}".format(zone, error))
I am trying to write a test to verify that but I can't mock the output of the compute method correctly.
#patch('googleapiclient.discovery')
def test_validate_zone(self, mock_response):
compute = mock_response.build(serviceName='compute', version='v1')
compute.zones().get(project_id=self.project_id, zone=self.zone).execute().return_value = {
'status': 'status',
'region': 'region',
'name': 'name'
}
zone_response = inventory.validate_zone(compute, self.project_id, self.zone)
print(zone_response)
This results in the zone_response output being a MagicMock object with its return_value being correct as developed in the test.
zone_response = MagicMock name='discovery.build().zones().get().execute()' id='139870134525456'
zone_response.return_value = {'status': 'status', 'region': 'region', 'name': 'name'}
Any ideas on what I'm doing wrong? I've been trying to write tests for this for quite a while so maybe my approach is just off.
Turns out the issue was the () on the execute method in the test. So the correct test should be:
#patch('inventory.discovery.build', serviceName='compute', version='v1')
def test_validate_zone(self, compute):
print(compute)
compute.zones().get(project_id=self.project_id, zone=self.zone).execute.return_value = {
'status': 'status',
'region': 'region',
'name': 'name'
}
zone_response = inventory.validate_zone(compute, self.project_id, self.zone)
print(zone_response)
Source can be found at: https://realpython.com/python-mock-library/#managing-a-mocks-return-value

google calendar api not adding event and not throwing an error

I'm trying to follow google's example of adding a event and originally it threw errors because I formatted something wrong but now it's saying the event is being made and linking me to it but it never actually makes the event. The 3rd picture I attached is the result and it has a little popup that says error adding event, the weird thing is that none of my events at all show up on the calendar even though I'm logged into the same account in both cases and only have one calendar. The event never gets added to my calendar I've tried running it multiple times even using different data in the event and same result, it says it adds it but never does. I'm lost any help is greatly appreciated please feel free to ask for more info if you need!
edit:
here's my event creation function
def createEvent(summary, start_time, end_time, *args, description='', location='', timeZone='America/New_York'):
credentials = get_credentials()
service = discovery.build('calendar', 'v3', credentials=credentials)
event = {
'summary': summary,
'location': location,
'description': description,
'start': {
'dateTime': start_time,
'timeZone': timeZone,
},
'end': {
'dateTime': end_time,
'timeZone': timeZone,
},
'reminders': {
'useDefault': False,
'overrides': [
# {'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 10},
],
},
}
for arg in args:
event[arg[0]] = arg[1]
event = service.events().insert(calendarId='primary', body=event).execute()
print ('Event created: %s' % (event.get('htmlLink')))
edit 2:
this is how the function is called with an example of the information passed in
googEvent = ['CSC 385 hw', '20-1-31T22:59:59', '20-1-31T23:59:59', 'EC Mylavarapu']
createEvent(googEvent[0], googEvent[1], googEvent[2], description=googEvent[3])
After studying your code I found that you are so close to fixing it. You only need to force the date format into ISO 8601. To accomplish that, I used the following Python methods:
import datetime
…
googEvent = ['CSC 385 hw', datetime.datetime.strptime("31/01/2020 22:59:59",
"%d/%m/%Y %H:%M:%S").isoformat(), datetime.datetime.strptime(
"31/01/2020 23:59:59", "%d/%m/%Y %H:%M:%S").isoformat(), 'EC Mylavarapu']
createEvent(googEvent[0], googEvent[1], googEvent[2], description = googEvent[3])
This is only one way of doing it. Each date is first created from a human readable string using strptime() and later converted into ISO 8601 with isoformat(). Please, answer me back if you need further help.

Did i migrate from the default code to my testing one properly?

I want to create develop a chatbot using Lex's interface, as a first step i found a ScheduleAppointment default bot so i decided to do adjustments to it. The default prompts in this bot are date, time and appointmenttype. As a first step i went to using blueprint lex-make-appointment-python https://console.aws.amazon.com/lambda/home?region=us-east-1#/create/new?bp=lex-make-appointment-python and had to change lots of the default stuff there, for example it has an exact appointment time and days etc while the version i want to work on before developing it any further in lambda is one that would take ANY TIME and ANY DAY for example i can't get an error if i ask to schedule an appointment tomorrow but if the bot asks for date and i put something like hfujdhfu or banana i should be asked what is the date? again.
All that being said and done this is my version of that code after adjusting it :
import json
import dateutil.parser
import datetime
import time
import os
import math
import random
import logging
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
""" --- Helpers to build responses which match the structure of the necessary dialog actions --- """
def elicit_slot(session_attributes, intent_name, slots, slot_to_elicit, message, response_card):
return {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'ElicitSlot',
'intentName': intent_name,
'slots': slots,
'slotToElicit': slot_to_elicit,
'message': message,
'responseCard': response_card
}
}
def confirm_intent(session_attributes, intent_name, slots, message, response_card):
return {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'ConfirmIntent',
'intentName': intent_name,
'slots': slots,
'message': message,
'responseCard': response_card
}
}
def close(session_attributes, fulfillment_state, message):
response = {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'Close',
'fulfillmentState': fulfillment_state,
'message': message
}
}
return response
def delegate(session_attributes, slots):
return {
'sessionAttributes': session_attributes,
'dialogAction': {
'type': 'Delegate',
'slots': slots
}
}
def build_response_card(title, subtitle, options):
"""
Build a responseCard with a title, subtitle, and an optional set of options which should be displayed as buttons.
"""
buttons = None
if options is not None:
buttons = []
for i in range(min(5, len(options))):
buttons.append(options[i])
return {
'contentType': 'application/vnd.amazonaws.card.generic',
'version': 1,
'genericAttachments': [{
'title': title,
'subTitle': subtitle,
'buttons': buttons
}]
}
""" --- Helper Functions --- """
def parse_int(n):
try:
return int(n)
except ValueError:
return float('nan')
def try_ex(func):
"""
Call passed in function in try block. If KeyError is encountered return None.
This function is intended to be used to safely access dictionary.
Note that this function would have negative impact on performance.
"""
try:
return func()
except KeyError:
return None
def isvalid_date(date):
try:
dateutil.parser.parse(date)
return True
except ValueError:
return False
def build_time_output_string(appointment_time):
hour, minute = appointment_time.split(':') # no conversion to int in order to have original string form. for eg) 10:00 instead of 10:0
if int(hour) > 12:
return '{}:{} p.m.'.format((int(hour) - 12), minute)
elif int(hour) == 12:
return '12:{} p.m.'.format(minute)
elif int(hour) == 0:
return '12:{} a.m.'.format(minute)
return '{}:{} a.m.'.format(hour, minute)
def make_appointment(intent_request):
"""
Performs dialog management and fulfillment for booking a dentists appointment.
Beyond fulfillment, the implementation for this intent demonstrates the following:
1) Use of elicitSlot in slot validation and re-prompting
2) Use of confirmIntent to support the confirmation of inferred slot values, when confirmation is required
on the bot model and the inferred slot values fully specify the intent.
"""
appointment_type = intent_request['currentIntent']['slots']['AppointmentType']
date = intent_request['currentIntent']['slots']['Date']
appointment_time = intent_request['currentIntent']['slots']['Time']
source = intent_request['invocationSource']
output_session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {}
booking_map = json.loads(try_ex(lambda: output_session_attributes['bookingMap']) or '{}')
if source == 'DialogCodeHook':
# Perform basic validation on the supplied input slots.
slots = intent_request['currentIntent']['slots']
if not appointment_type:
return elicit_slot(
output_session_attributes,
intent_request['currentIntent']['name'],
intent_request['currentIntent']['slots'],
'AppointmentType',
{'contentType': 'PlainText', 'content': 'What type of appointment would you like to schedule?'},
build_response_card(
'Specify Appointment Type', 'What type of appointment would you like to schedule?',
build_options('AppointmentType', appointment_type, date, None)
)
)
if appointment_type and not date:
return elicit_slot(
output_session_attributes,
intent_request['currentIntent']['name'],
intent_request['currentIntent']['slots'],
'Date',
{'contentType': 'PlainText', 'content': 'When would you like to schedule your {}?'.format(appointment_type)},
build_response_card(
'Specify Date',
'When would you like to schedule your {}?'.format(appointment_type),
build_options('Date', appointment_type, date, None)
)
)
)
message_content = 'What time on {} works for you? '.format(date)
if appointment_time:
output_session_attributes['formattedTime'] = build_time_output_string(appointment_time)
)
available_time_string = build_available_time_string(appointment_type_availabilities)
return elicit_slot(
output_session_attributes,
intent_request['currentIntent']['name'],
slots,
'Time',
{'contentType': 'PlainText', 'content': '{}{}'.format(message_content, available_time_string)},
build_response_card(
'Specify Time',
'What time works best for you?',
build_options('Time', appointment_type, date, booking_map)
)
)
return delegate(output_session_attributes, slots)
duration = get_duration(appointment_type)
""" --- Intents --- """
def dispatch(intent_request):
"""
Called when the user specifies an intent for this bot.
"""
logger.debug('dispatch userId={}, intentName={}'.format(intent_request['userId'], intent_request['currentIntent']['name']))
intent_name = intent_request['currentIntent']['name']
# Dispatch to your bot's intent handlers
if intent_name == 'MakeAppointment':
return make_appointment(intent_request)
raise Exception('Intent with name ' + intent_name + ' not supported')
""" --- Main handler --- """
def lambda_handler(event, context):
"""
Route the incoming request based on intent.
The JSON body of the request is provided in the event slot.
"""
# By default, treat the user request as coming from the America/New_York time zone.
os.environ['TZ'] = 'America/New_York'
time.tzset()
logger.debug('event.bot.name={}'.format(event['bot']['name']))
return dispatch(event)
There is ) at line 171 and line 175 without any ( which must be causing syntax error. many code lines are unreachable because you are coding return before them but those won't cause syntax error.
You can watch the logs on Cloudwatch.
The blueprint which you are using is very complex and not beginner friendly. You really should be using this blueprint for starting. That is my suggestion.
Also, since you are using response_card so please be aware that the response cards won't be shown in the Lex Console window. It will work in Facebook and Slack though.

How to send multiple messages to mandrill

I have around 1000 unique mails with unique content. I want to bulk send them to mandril i.e. I dont want to make individual network calls for every message I am sending. The following document shows the api. But it does not define what I want to.
https://mandrillapp.com/api/docs/messages.python.html
What I want
mandrill_client = mandrill.Mandrill('YOUR_API_KEY')
messages ={ message1, message2 ....}
result = mandrill_client.messages.send(messages=messages, async=True, ip_pool='Main Pool', send_at='example send_at')
I could not find the above. But this is what I want.
Try this
message = {
#other details
'from_email': 'message.from_email#example.com',
'from_name': 'Example Name',
'to': [{'email': 'recipient.email#example.com',
'name': 'Recipient Name',
'type': 'to'},2nd reciepient,....],
}
result = mandrill_client.messages.send(messages=messages, async=True)

Categories

Resources