The 3rd party system I am using (vendor product) still uses Python 2.7 and doesn't support Python 3+ so bear with me, I'm fully aware Python 3 is out and this is a limitation of the system I have to use rather than a choice.
I am trying to do an integration between this third party product and MS teams - basically, the third party system provides data, I read this into my Python script and output a message to Teams using a webhook. It mostly works, but I'm struggling to load in some of the variables from the systems data.
For example, in my code, I use the following:
messageID='"{}"'.format(item["messageId"])
recipient='"{}"'.format(item["recipient"]["email"])
subject='"{}"'.format(item["subject"])
sender='"{}"'.format(item["sender"]["email"])
which has output like this:
messageId="34239482030783472#test.net"
recipient="testuser#domain.com"
subject="Email subject here"
sender="sender#domain2.com"
This is all fine, the trouble comes when I need to format my string to post to the Teams webhook.
It currently looks like:
teams_card='{"#type": "MessageCard","#context": "http://schema.org/extensions","themeColor": "0076D7","summary": “PTR”,”sections": [{"activityTitle": "PTR Incident Created","activitySubtitle": “End “User Exposed to Phishing Threat,”facts": [{"name": “Message” ID,”value": %s}, {"name": "Subject”,”value": %s},{“name": "End User","value": %s},{“name": “sender”,”value": %s}],”markdown": true}],"potentialAction": [{"#type": "OpenUri","name": "View Related Emails","targets": [{"os": "default","uri": "https://maskedurlhere.com”}]}]}’ % (messageId,subject,recipient,sender)
which throws an error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: not enough arguments for format string
I tried to use .format option also, but this fails with a different error:
teams_card='{"#type": "MessageCard","#context": "http://schema.org/extensions","themeColor": "0076D7","summary": “PTR”,”sections": [{"activityTitle": "PTR Incident Created","activitySubtitle": “End “User Exposed to Phishing Threat,”facts": [{"name": “Message” ID,”value": %s}, {"name": "Subject”,”value": %s},{“name": "End User","value": %s},{“name": “sender”,”value": %s}],”markdown": true}],"potentialAction": [{"#type": "OpenUri","name": "View Related Emails","targets": [{"os": "default","uri": "https://maskedurlhere.com”}]}]}’.format(messageId,subject,recipient,sender)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: '"#type"'
The teams card variable is fine and posts to Teams successfully when it's just text, but trying to load in these variables doesn't seem to work at all.
Any ideas?
To pass dynamic values in Json you need to use format like ${value}
Please follow below example json format
Template JSON
{
"type": "AdaptiveCard",
"body": [
{
"type": "Container",
"style": "emphasis",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"size": "Large",
"weight": "Bolder",
"text": "**EXPENSE APPROVAL**",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "Image",
"url": "${status_url}",
"altText": "${status}",
"height": "30px"
}
],
"width": "auto"
}
]
}
],
"bleed": true
},
{
"type": "Container",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"size": "ExtraLarge",
"text": "${purpose}",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.OpenUrl",
"title": "EXPORT AS PDF",
"url": "https://adaptivecards.io"
}
]
}
],
"width": "auto"
}
]
},
{
"type": "TextBlock",
"spacing": "Small",
"size": "Small",
"weight": "Bolder",
"color": "Accent",
"text": "[${code}](https://adaptivecards.io)",
"wrap": true
},
{
"type": "FactSet",
"spacing": "Large",
"facts": [
{
"title": "Submitted By",
"value": "**${created_by_name}** ${creater_email}"
},
{
"title": "Duration",
"value": "${formatTicks(min(select(expenses, x, int(x.created_by))), 'yyyy-MM-dd')} - ${formatTicks(max(select(expenses, x, int(x.created_by))), 'yyyy-MM-dd')}"
},
{
"title": "Submitted On",
"value": "${formatDateTime(submitted_date, 'yyyy-MM-dd')}"
},
{
"title": "Reimbursable Amount",
"value": "$${formatNumber(sum(select(expenses, x, if(x.is_reimbursable, x.total, 0))), 2)}"
},
{
"title": "Awaiting approval from",
"value": "**${approver}** ${approver_email}"
},
{
"title": "Submitted to",
"value": "**${other_submitter}** ${other_submitter_email}"
}
]
}
]
},
{
"type": "Container",
"spacing": "Large",
"style": "emphasis",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "DATE",
"wrap": true
}
],
"width": "auto"
},
{
"type": "Column",
"spacing": "Large",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "CATEGORY",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "AMOUNT",
"wrap": true
}
],
"width": "auto"
}
]
}
],
"bleed": true
},
{
"$data": "${expenses}",
"type": "Container",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "${formatDateTime(created_time, 'MM-dd')}",
"wrap": true
}
],
"width": "auto"
},
{
"type": "Column",
"spacing": "Medium",
"items": [
{
"type": "TextBlock",
"text": "${description}",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "$${formatNumber(total, 2)}",
"wrap": true
}
],
"width": "auto"
},
{
"type": "Column",
"spacing": "Small",
"selectAction": {
"type": "Action.ToggleVisibility",
"targetElements": [
"cardContent${$index}",
"chevronDown${$index}",
"chevronUp${$index}"
]
},
"verticalContentAlignment": "Center",
"items": [
{
"type": "Image",
"id": "chevronDown${$index}",
"url": "https://adaptivecards.io/content/down.png",
"width": "20px",
"altText": "${description} $${total} collapsed"
},
{
"type": "Image",
"id": "chevronUp${$index}",
"url": "https://adaptivecards.io/content/up.png",
"width": "20px",
"altText": "${description} $${total} expanded",
"isVisible": false
}
],
"width": "auto"
}
]
},
{
"type": "Container",
"id": "cardContent${$index}",
"isVisible": false,
"items": [
{
"type": "Container",
"items": [
{
"$data": "${custom_fields}",
"type": "TextBlock",
"text": "* ${value}",
"isSubtle": true,
"wrap": true
},
{
"type": "Container",
"items": [
{
"type": "Input.Text",
"id": "comment${$index}",
"placeholder": "Add your comment here."
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "Send",
"data": {
"id": "_qkQW8dJlUeLVi7ZMEzYVw",
"action": "comment",
"lineItem": 1
}
}
]
}
],
"width": "auto"
}
]
}
]
}
]
}
]
},
{
"type": "ColumnSet",
"spacing": "Large",
"separator": true,
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"horizontalAlignment": "Right",
"text": "Total Expense Amount \t",
"wrap": true
},
{
"type": "TextBlock",
"horizontalAlignment": "Right",
"text": "Non-reimbursable Amount",
"wrap": true
},
{
"type": "TextBlock",
"horizontalAlignment": "Right",
"text": "Advance Amount",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "$${formatNumber(sum(select(expenses, x, x.total)), 2)}",
"wrap": true
},
{
"type": "TextBlock",
"text": "(-) $${formatNumber(sum(select(expenses, x, if(x.is_reimbursable, 0, x.total))), 2)} \t",
"wrap": true
},
{
"type": "TextBlock",
"text": "(-) 0.00 \t",
"wrap": true
}
],
"width": "auto"
},
{
"type": "Column",
"width": "auto"
}
]
},
{
"type": "Container",
"style": "emphasis",
"items": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"horizontalAlignment": "Right",
"text": "Amount to be Reimbursed",
"wrap": true
}
],
"width": "stretch"
},
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "$${formatNumber(sum(select(expenses, x, if(x.is_reimbursable, x.total, 0))), 2)}",
"wrap": true
}
],
"width": "auto"
},
{
"type": "Column",
"width": "auto"
}
]
}
],
"bleed": true
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"selectAction": {
"type": "Action.ToggleVisibility",
"targetElements": [
"cardContent4",
"showHistory",
"hideHistory"
]
},
"verticalContentAlignment": "Center",
"items": [
{
"type": "TextBlock",
"id": "showHistory",
"horizontalAlignment": "Right",
"color": "Accent",
"text": "Show history",
"wrap": true
},
{
"type": "TextBlock",
"id": "hideHistory",
"horizontalAlignment": "Right",
"color": "Accent",
"text": "Hide history",
"wrap": true,
"isVisible": false
}
],
"width": 1
}
]
},
{
"type": "Container",
"id": "cardContent4",
"isVisible": false,
"items": [
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "* Expense submitted by **${created_by_name}** on {{DATE(${formatDateTime(created_date, 'yyyy-MM-ddTHH:mm:ssZ')}, SHORT)}}",
"isSubtle": true,
"wrap": true
},
{
"type": "TextBlock",
"text": "* Expense ${expenses[0].status} by **${expenses[0].approver}** on {{DATE(${formatDateTime(approval_date, 'yyyy-MM-ddTHH:mm:ssZ')}, SHORT)}}",
"isSubtle": true,
"wrap": true
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "Approve",
"style": "positive",
"data": {
"id": "_qkQW8dJlUeLVi7ZMEzYVw",
"action": "approve"
}
},
{
"type": "Action.ShowCard",
"title": "Reject",
"style": "destructive",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"id": "RejectCommentID",
"placeholder": "Please specify an appropriate reason for rejection.",
"isMultiline": true
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Send",
"data": {
"id": "_qkQW8dJlUeLVi7ZMEzYVw",
"action": "reject"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
]
}
]
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2",
"fallbackText": "This card requires Adaptive Cards v1.2 support to be rendered properly."
}
Data Json
{
"code": "ER-13052",
"message": "success",
"created_by_name" : "Matt Hidinger",
"created_date" : "2019-07-15T18:33:12+0800",
"submitted_date": "2019-04-14T18:33:12+0800",
"creater_email" : "matt#contoso.com",
"status" : "Pending",
"status_url" : "https://adaptivecards.io/content/pending.png",
"approver": "Thomas",
"purpose" : "Trip to UAE",
"approval_date" : "2019-07-15T22:33:12+0800",
"approver" : "Thomas",
"approver_email" : "thomas#contoso.com",
"other_submitter" : "David",
"other_submitter_email" : "david#contoso.com",
"expenses": [
{
"expense_id": "16367000000083065",
"approver" : "Thomas",
"date": "2017-02-21",
"description": "Air Travel Expense",
"created_by": "636965431200000000",
"created_by_name": "PATRICIA",
"employee_number": "E001",
"currency_id": "16367000000000097",
"currency_code": "USD",
"paid_through_account_id": "16367000000036003",
"paid_through_account_name": "Employee Reimbursements",
"bcy_total": 13900.79,
"bcy_subtotal": 13900.79,
"total": 300,
"total_without_tax": 300,
"is_billable": true,
"is_reimbursable": true,
"reference_number": "DD145",
"due_days": "Due in 15 days",
"merchant_id": "16367000000074027",
"merchant_name": "ABS Solutions",
"status": "approved",
"created_time": "2019-06-19T18:33:12+0800",
"last_modified_time": "2017-02-21T18:42:46+0530",
"receipt_name": "receipt1.jpg",
"report_id": "16367000000083075",
"mileage_type": "non_mileage",
"report_name": "Purchase",
"is_receipt_only": false,
"distance": 0,
"per_diem_rate": 0,
"per_diem_days": 0,
"per_diem_id": "",
"per_diem_name": "",
"expense_type": "non_mileage",
"location": "Washington",
"receipt_type": "jpg",
"policy_violated": false,
"comments_count": 0,
"report_status": "submitted",
"price_precision": 2,
"mileage_rate": 0,
"mileage_unit": "km",
"receipt_status": "processed",
"is_uncategorized": false,
"is_expired": false,
"gl_code": "LG001",
"exchange_rate": 66.943366,
"start_reading": "",
"end_reading": "",
"payment_mode": "Check",
"customer_id": "27927000000075081",
"customer_name": "ACME Corp.",
"custom_fields": [
{
"customfield_id": "16367000000277001",
"label": "Other Name",
"value": "Leg 1 on Tue, Jun 19th, 2019 at 6:00 AM."
},
{
"customfield_id": "16367000000277001",
"label": "Other Name",
"value": "Leg 2 on Tue, Jun 19th, 2019 at 7:15 PM."
}
],
"project_id": "27927000001243001",
"project_name": "Coffee Research",
"transaction_description": "",
"tax_id": "16367000000086001",
"tax_name": "Sales Tax",
"tax_percentage": 2,
"amount": 207.65,
"is_inclusive_tax": false,
"vehicle_type": "Bike",
"vehicle_id": "17456000000078029",
"fuel_type": "lpg",
"engine_capacity_range": "between_1401cc_and_1600cc",
"is_personal": false,
"policy_id": "16367000000092011",
"policy_name": "LIC",
"documents": [
{
"file_name": "receipt1.jpg",
"file_size_formatted": "71.8 KB",
"attachment_order": 1,
"document_id": "16367000000083071"
}
],
"reimbursement_reference": "",
"reimbursement_date": "",
"reimbursement_paid_through_account_id": "",
"reimbursement_paid_through_account_name": "",
"reimbursement_currency_id": "",
"reimbursement_currency_code": ""
},
{
"expense_id": "16367000000083065",
"date": "2019-06-19",
"description": "Auto Mobile Expense",
"created_by": "636965431200000000",
"created_by_name": "PATRICIA",
"employee_number": "E001",
"currency_id": "16367000000000097",
"currency_code": "USD",
"paid_through_account_id": "16367000000036003",
"paid_through_account_name": "Employee Reimbursements",
"bcy_total": 13900.79,
"bcy_subtotal": 13900.79,
"total": 100,
"total_without_tax": 100,
"is_billable": true,
"is_reimbursable": true,
"reference_number": "DD145",
"due_days": "Due in 15 days",
"merchant_id": "16367000000074027",
"merchant_name": "ABS Solutions",
"status": "submitted",
"created_time": "2019-06-19T18:33:12+0800",
"last_modified_time": "2017-02-21T18:42:46+0530",
"receipt_name": "receipt1.jpg",
"report_id": "16367000000083075",
"mileage_type": "non_mileage",
"report_name": "Purchase",
"is_receipt_only": false,
"distance": 0,
"per_diem_rate": 0,
"per_diem_days": 0,
"per_diem_id": "",
"per_diem_name": "",
"expense_type": "non_mileage",
"location": "Washington",
"receipt_type": "jpg",
"policy_violated": false,
"comments_count": 0,
"report_status": "submitted",
"price_precision": 2,
"mileage_rate": 0,
"mileage_unit": "km",
"receipt_status": "processed",
"is_uncategorized": false,
"is_expired": false,
"gl_code": "LG001",
"exchange_rate": 66.943366,
"start_reading": "",
"end_reading": "",
"payment_mode": "Check",
"customer_id": "27927000000075081",
"customer_name": "ACME Corp.",
"custom_fields": [
{
"customfield_id": "16367000000277001",
"label": "Other Name",
"value": " Contoso Car Rentrals, Tues 6/19 at 7:00 AM"
}
],
"project_id": "27927000001243001",
"project_name": "Coffee Research",
"transaction_description": "",
"tax_id": "16367000000086001",
"tax_name": "Sales Tax",
"tax_percentage": 2,
"amount": 207.65,
"is_inclusive_tax": false,
"vehicle_type": "Bike",
"vehicle_id": "17456000000078029",
"fuel_type": "lpg",
"engine_capacity_range": "between_1401cc_and_1600cc",
"is_personal": false,
"policy_id": "16367000000092011",
"policy_name": "LIC",
"documents": [
{
"file_name": "receipt1.jpg",
"file_size_formatted": "71.8 KB",
"attachment_order": 1,
"document_id": "16367000000083071"
}
],
"reimbursement_reference": "",
"reimbursement_date": "",
"reimbursement_paid_through_account_id": "",
"reimbursement_paid_through_account_name": "",
"reimbursement_currency_id": "",
"reimbursement_currency_code": ""
},
{
"expense_id": "16367000000083065",
"date": "2019-06-21",
"description": "Excess Baggage Cost",
"created_by": "636967159200000000",
"created_by_name": "PATRICIA",
"employee_number": "E001",
"currency_id": "16367000000000097",
"currency_code": "USD",
"paid_through_account_id": "16367000000036003",
"paid_through_account_name": "Employee Reimbursements",
"bcy_total": 13900.79,
"bcy_subtotal": 13900.79,
"total": 50.38,
"total_without_tax": 4.3,
"is_billable": true,
"is_reimbursable": false,
"reference_number": "DD145",
"due_days": "Due in 15 days",
"merchant_id": "16367000000074027",
"merchant_name": "ABS Solutions",
"status": "submitted",
"created_time": "2019-06-21T18:33:12+0800",
"last_modified_time": "2017-02-21T18:42:46+0530",
"receipt_name": "receipt1.jpg",
"report_id": "16367000000083075",
"mileage_type": "non_mileage",
"report_name": "Purchase",
"is_receipt_only": false,
"distance": 0,
"per_diem_rate": 0,
"per_diem_days": 0,
"per_diem_id": "",
"per_diem_name": "",
"expense_type": "non_mileage",
"location": "Washington",
"receipt_type": "jpg",
"policy_violated": false,
"comments_count": 0,
"report_status": "submitted",
"price_precision": 2,
"mileage_rate": 0,
"mileage_unit": "km",
"receipt_status": "processed",
"is_uncategorized": false,
"is_expired": false,
"gl_code": "LG001",
"exchange_rate": 66.943366,
"start_reading": "",
"end_reading": "",
"payment_mode": "Check",
"customer_id": "27927000000075081",
"customer_name": "ACME Corp.",
"custom_fields": [
],
"project_id": "27927000001243001",
"project_name": "Coffee Research",
"transaction_description": "",
"tax_id": "16367000000086001",
"tax_name": "Sales Tax",
"tax_percentage": 2,
"amount": 207.65,
"is_inclusive_tax": false,
"vehicle_type": "Bike",
"vehicle_id": "17456000000078029",
"fuel_type": "lpg",
"engine_capacity_range": "between_1401cc_and_1600cc",
"is_personal": false,
"policy_id": "16367000000092011",
"policy_name": "LIC",
"documents": [
{
"file_name": "receipt1.jpg",
"file_size_formatted": "71.8 KB",
"attachment_order": 1,
"document_id": "16367000000083071"
}
],
"reimbursement_reference": "",
"reimbursement_date": "",
"reimbursement_paid_through_account_id": "",
"reimbursement_paid_through_account_name": "",
"reimbursement_currency_id": "",
"reimbursement_currency_code": ""
}
]
}
Please go through this for more info.
Hi I have two dictionaries 1.Primary, 2. Secondary
Need to check first field of both dictionary
If filed is same compare the title with primary and secondary
if Primary have missing dictionary which is in dictionary add that to primary
Primary dictionary
{"Latest":[
{
"name": "Employee",
"field": "employee",
"values": [
{
"title": "A",
"paragraph": "null",
"role": "Deveoper"
},
{
"title": "C",
"paragraph": "null",
"role": "Tester"
}
]
},
{
"name": "Project",
"field": "project",
"values": [
{
"title": "NEW_York",
"paragraph": "null",
"role": "Long Term"
}
]
},
{
"name": "Designation",
"field": "designation",
"values": [
{
"title": "Developer",
"paragraph": "null",
"role": "null"
}
]
}
]}
Secondary dictionary
[
{
"name": "Employee",
"field": "employee",
"values": [
{
"title": "A",
"paragraph": "null",
"role": "null"
},
{
"title": "B",
"paragraph": "null",
"role": "null"
}
]
},
{
"name": "Project",
"field": "project",
"values": [
{
"title": "NEW_York",
"paragraph": "test",
"role": "null"
}
]
},
{
"name": "Designation",
"field": "designation",
"values": [
{
"title": "Tester",
"paragraph": "null",
"role": "null"
}
]
}
]
Expected out
{"Latest":[
{
"name": "Employee",
"field": "employee",
"values": [
{
"title": "A",
"paragraph": "null",
"role": "Deveoper"
},
{
"title": "C",
"paragraph": "null",
"role": "Tester"
},
{
"title": "B",
"paragraph": "null",
"role": "null"
}
]
},
{
"name": "Project",
"field": "project",
"values": [
{
"title": "NEW_York",
"paragraph": "null",
"role": "Long Term"
}
]
},
{
"name": "Designation",
"field": "designation",
"values": [
{
"title": "Developer",
"paragraph": "null",
"role": "null"
},
{
"title": "Tester",
"paragraph": "null"
"role": "null"
}
]
}
]}
COde
for i in primary['Latest']:
for j in secondary:
if i['field'] == j['field']:
for a in i['values']:
for b in j['values']:
if a['title'] == b['title']:
i['values'].append(b)
My code executes indefinitely
The issue is that you are appending in i['values'] while iterating on it, creating unexpected behaviour. You can fix it by iterating on a "frozen" list if i['values'] like this:
from copy import deepcopy
for i in primary['Latest']:
for j in secondary:
if i['field'] == j['field']:
values = deepcopy(i['values'])
for a in values:
for b in j['values']:
if a['title'] == b['title']:
i['values'].append(b)
This resolves the "indefinitely" part, but doesn't quite work, here is the output:
print(json.dumps(primary, indent=2))
{
"Latest": [
{
"name": "Employee",
"field": "employee",
"values": [
{
"title": "A",
"paragraph": null,
"role": "Deveoper"
},
{
"title": "C",
"paragraph": null,
"role": "Tester"
},
{
"title": "A",
"paragraph": null,
"role": "null"
}
]
},
{
"name": "Project",
"field": "project",
"values": [
{
"title": "NEW_York",
"paragraph": "null",
"role": "Long Term"
},
{
"title": "NEW_York",
"paragraph": "test",
"role": "null"
}
]
},
{
"name": "Designation",
"field": "designation",
"values": [
{
"title": "Developer",
"paragraph": null,
"role": "null"
}
]
}
]
}
So I fixed it a little:
for primary_elem in primary['Latest']:
primary_titles = [value['title'] for value in primary_elem['values']]
for secondary_elem in secondary:
if secondary_elem['field'] == primary_elem['field']:
for secondary_value in secondary_elem['values']:
if secondary_value['title'] not in primary_titles:
primary_elem['values'].append(secondary_value)
Which gives
>>> print(json.dumps(primary, indent=2))
{
"Latest": [
{
"name": "Employee",
"field": "employee",
"values": [
{
"title": "A",
"paragraph": null,
"role": "Deveoper"
},
{
"title": "C",
"paragraph": null,
"role": "Tester"
},
{
"title": "B",
"paragraph": null,
"role": "null"
}
]
},
{
"name": "Project",
"field": "project",
"values": [
{
"title": "NEW_York",
"paragraph": "null",
"role": "Long Term"
}
]
},
{
"name": "Designation",
"field": "designation",
"values": [
{
"title": "Developer",
"paragraph": null,
"role": "null"
},
{
"title": "Tester",
"paragraph": null,
"role": "null"
}
]
}
]
}
I have been spending the past days trying to retrieve all of the "dateTime" values from "start" and "end" from this nested dictionary in a nested list(s) with every kind of looping, yet I haven't been successful. Does anyone know how I shall approach it?
AllCals = [
{
"accessRole": "reader",
"defaultReminders": [],
"etag": "\"\"",
"items": [],
"kind": "calendar#events",
"nextSyncToken": "=",
"summary": "Holidays in Canada",
"timeZone": "America/Toronto",
"updated": "2020-11-09T20:44:17.000Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"etag": "\"\"",
"items": [],
"kind": "calendar#events",
"nextSyncToken": "=",
"summary": "Business Sim",
"timeZone": "America/Toronto",
"updated": "2020-10-26T19:23:14.031Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"etag": "\"\"",
"items": [],
"kind": "calendar#events",
"nextSyncToken": "=",
"summary": "COMN",
"timeZone": "America/Toronto",
"updated": "2020-11-05T20:15:35.506Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"etag": "\"\"",
"items": [
{
"created": "2020-11-09T16:45:47.000Z",
"creator": {
"email": "#gmail.com"
},
"end": {
"dateTime": "2020-11-10T18:30:00-05:00"
},
"etag": "\"\"",
"htmlLink": "https://www.google.com/calendar",
"iCalUID": "#google.com",
"id": "",
"kind": "calendar#event",
"organizer": {
"displayName": "Predictive",
"email": "#group.calendar.google.com",
"self": true
},
"reminders": {
"useDefault": true
},
"sequence": 1,
"start": {
"dateTime": "2020-11-10T17:30:00-05:00"
},
"status": "confirmed",
"summary": "Group call",
"updated": "2020-11-10T00:13:33.387Z"
},
{
"created": "2020-11-11T02:02:03.000Z",
"creator": {
"email": "#gmail.com"
},
"end": {
"dateTime": "2020-11-10T22:30:00-05:00"
},
"etag": "\"\"",
"htmlLink": "https://www.google.com/calendar/event",
"iCalUID": "#google.com",
"id": "",
"kind": "calendar#event",
"organizer": {
"displayName": "Predictive",
"email": "#group.calendar.google.com",
"self": true
},
"reminders": {
"useDefault": true
},
"sequence": 0,
"start": {
"dateTime": "2020-11-10T21:30:00-05:00"
},
"status": "confirmed",
"summary": "predictive part",
"updated": "2020-11-11T02:02:03.410Z"
}
],
"kind": "calendar#",
"nextSyncToken": "-=",
"summary": "Predictive",
"timeZone": "America/Toronto",
"updated": "2020-11-11T02:02:03.410Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"description": "",
"etag": "\"\"",
"items": [
{
"created": "2020-09-23T02:48:00.000Z",
"creator": {
"email": "#gmail.com"
},
"end": {
"date": "2020-11-10"
},
"etag": "\"\"",
"htmlLink": "",
"iCalUID": "#google.com",
"id": "",
"kind": "calendar#event",
"organizer": {
"displayName": "Real Estate",
"email": "#group.calendar.google.com",
"self": true
},
"reminders": {
"useDefault": false
},
"sequence": 3,
"start": {
"date": "2020-11-09"
},
"status": "confirmed",
"summary": "Assignment 3",
"updated": "2020-09-26T17:36:50.714Z"
}
],
"kind": "#events",
"nextSyncToken": "=",
"summary": "Real Estate",
"timeZone": "America/Toronto",
"updated": "2020-11-06T06:24:35.930Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"etag": "\"\"",
"items": [
{
"created": "2020-09-30T19:24:53.000Z",
"creator": {
"email": "#gmail.com"
},
"end": {
"dateTime": "2020-11-10T02:00:00-05:00",
"timeZone": "America/Toronto"
},
"etag": "\"\"",
"htmlLink": "",
"iCalUID": "#google.com",
"id": "",
"kind": "calendar#event",
"organizer": {
"displayName": "Reading",
"email": "#group.calendar.google.com",
"self": true
},
"originalStartTime": {
"dateTime": "2020-11-10T01:00:00-05:00",
"timeZone": "America/Toronto"
},
"recurringEventId": "",
"reminders": {
"useDefault": true
},
"sequence": 0,
"start": {
"dateTime": "2020-11-10T01:00:00-05:00",
"timeZone": "America/Toronto"
},
"status": "confirmed",
"summary": "Reading",
"updated": "2020-09-30T19:24:53.912Z"
}
],
"kind": "calendar#events",
"nextSyncToken": "=",
"summary": "Reading",
"timeZone": "America/Toronto",
"updated": "2020-09-30T19:24:53.912Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"etag": "\"\"",
"items": [
{
"created": "2020-11-11T00:09:57.000Z",
"creator": {
"email": "#gmail.com"
},
"end": {
"dateTime": "2020-11-10T20:30:00-05:00"
},
"etag": "\"\"",
"htmlLink": "",
"iCalUID": "#google.com",
"id": "",
"kind": "calendar#event",
"organizer": {
"displayName": "ToDo",
"email": "#group.calendar.google.com",
"self": true
},
"reminders": {
"useDefault": true
},
"sequence": 0,
"start": {
"dateTime": "2020-11-10T19:30:00-05:00"
},
"status": "confirmed",
"summary": "",
"updated": "2020-11-11T00:09:57.152Z"
}
],
"kind": "calendar#events",
"nextSyncToken": "-=",
"summary": "ToDo",
"timeZone": "America/Toronto",
"updated": "2020-11-11T00:09:57.152Z"
},
{
"accessRole": "owner",
"defaultReminders": [
{
"method": "popup",
"minutes": 30
}
],
"etag": "\"\"",
"items": [
{
"created": "2020-09-29T03:29:51.000Z",
"creator": {
"email": "#gmail.com",
"self": true
},
"end": {
"dateTime": "2020-11-10T14:30:00-05:00",
"timeZone": "America/Toronto"
},
"etag": "\"\"",
"htmlLink": "",
"iCalUID": ".com",
"id": "",
"kind": "calendar#event",
"organizer": {
"email": "#gmail.com",
"self": true
},
"originalStartTime": {
"dateTime": "2020-11-10T11:30:00-05:00",
"timeZone": "America/Toronto"
},
"recurringEventId": "",
"reminders": {
"useDefault": true
},
"sequence": 0,
"start": {
"dateTime": "2020-11-10T11:30:00-05:00",
"timeZone": "America/Toronto"
},
"status": "confirmed",
"summary": "ENTR 4700",
"updated": "2020-09-29T03:29:51.740Z"
},
{
"created": "2020-11-10T05:39:09.000Z",
"creator": {
"email": "#gmail.com",
"self": true
},
"end": {
"dateTime": "2020-11-10T11:30:00-05:00"
},
"etag": "",
"htmlLink": "",
"iCalUID": "#google.com",
"id": "",
"kind": "calendar#event",
"organizer": {
"email": "#gmail.com",
"self": true
},
"reminders": {
"useDefault": true
},
"sequence": 1,
"start": {
"dateTime": "2020-11-10T10:30:00-05:00"
},
"status": "confirmed",
"summary": "Pick a term sheet ",
"updated": "2020-11-10T05:39:12.662Z"
}
],
"kind": "calendar#events",
"nextSyncToken": "=",
"summary": "ENTR 4700",
"timeZone": "America/Toronto",
"updated": "2020-11-10T06:12:07.093Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"etag": "\"\"",
"items": [],
"kind": "calendar#events",
"nextSyncToken": "=",
"summary": "Programming",
"timeZone": "America/Toronto",
"updated": "2020-11-08T01:45:49.847Z"
},
{
"accessRole": "owner",
"defaultReminders": [],
"etag": "\"\"",
"items": [],
"kind": "calendar#events",
"nextSyncToken": "",
"summary": "Alexa",
"timeZone": "America/Toronto",
"updated": "2020-11-08T16:37:12.291Z"
}
]
print(type(AllCals))
<class 'list'>
Unfortunately, google's calendar API doesn't provide all the events of the user even when "CalendarId" is set to 'primary' which has been an issue amongst developers. Therefore, I have no choice but to extract each calendar of the user, merge, and then parse.
The ultimate goal: Retrieve the user's duration of events from the calendar.
events = events_result.get('items', [])
Ids = [item['id'] for item in events]
AllCals = [service.events().list(calendarId=id, timeMin=yesterdayy, timeMax = utc_dt,singleEvents=True,orderBy='updated').execute() for id in Ids ]
The example data you showed seems to be JSON, but I'll assume you have a list of dicts, each with a key "items" holding an array of calendar items, each of which has keys "start" and "end", each containing a dict with either a key "date" or "dateTime" containing an ISO date string.
I still don't know what you are going to do with it, but if I understand you correctly, you want to retrieve the durations of all calendar's items.
from datetime import datetime
# AllCals = ...
for calendar in AllCals:
for item in calendar["items"]:
# this could probably be done more elegantly. you basically want item["start"]["date"] or item["start"]["dateTime"], whichever is present
item_start = item["start"]
item_start_string = item_start["date"] if "date" in item_start else item_start["dateTime"]
start_datetime = datetime.fromisoformat(item_start_string)
item_end = item["end"]
item_end_string = item_end["date"] if "date" in item_end else item_end["dateTime"]
end_datetime = datetime.fromisoformat(item_end_string)
duration = end_datetime - start_datetime
print(f"{item['summary']}: {duration}")
With your example data, this prints:
Group call: 1:00:00
predictive part: 1:00:00
Assignment 3: 1 day, 0:00:00
Reading: 1:00:00
: 1:00:00
ENTR 4700: 3:00:00
Pick a term sheet : 1:00:00