Let's say I have two JSON objects (call them 'websites' and 'links'). I need to end up with both objects in a single CSV (ideally in separate columns).
I'm working with something like this:
File.open("file.json", "w") do |f|
combined = [websites, links]
f.write(JSON.pretty_generate(combined))
end
And then I'm using the Ruby gem json2csv to convert this file to a CSV. But when I do, I get the following error:
error: undefined method 'keys' for #<Array:0x007fea8a8e33f8>
I can't figure out what's wrong. When I look in file.json, it appears to be structured like this: [{websites}, {links}]. From my limited knowledge of JSON, I think that's right, but I could easily be wrong.
Also, I know this won't get me separate columns in the CSV. If anyone has an answer for that part, major bonus points.
EDIT: JSON examples included below; error message changed after minor fix on my end.
websites:
{
"uri": "https://v1/websites",
"id": 28235674,
"background": null,
"createdDate": 1399585684000,
"lastActivityDate": 1430682494000,
"lastCommunicationDate": 1430682494000,
"lastNonCommunicationChronicleDate": 1430330886000,
"lastModifiedDate": 1449263116000,
"lastViewedDate": 1421429034000,
"preferredContactType": null,
"rss": "",
"emailAddresses": [
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "not found",
"type": "Work"
}
],
"phoneNumbers": [
],
"streetAddresses": [
],
"socialNetworks": [
{
"profileUrl": "http://twitter.com",
"name": "Twitter"
},
{
"profileUrl": "http://www.facebook.com",
"name": "Facebook"
},
{
"profileUrl": "http://plus.google.com",
"name": "GooglePlus"
},
{
"profileUrl": "http://www.linkedin.com",
"name": "LinkedIn"
},
{
"profileUrl": "http://twitter.com",
"name": "Twitter"
}
],
"contactUrls": [
],
"tags": [
"tag1",
"tag2"
],
"mostRecentActivity": "https://v1/history",
"mostRecentChronicle": "https://v1/history",
"mostRecentCommunication": "https://v1/history",
"mostRecentNonCommunicationChronicle": "https://v1/history",
"projectStates": "https://v1/websites",
"history": "https://v1/history",
"customFieldValues": [
],
"name": "",
"primaryDomain": "",
"domains": [
""
],
"associatedPeople": "https://v1/people",
"payments": "https://payments",
"links": "https://v1/links",
"type": "https://v1/websites"
}
links:
{
"uri": "https://v1/links/custom_fields",
"id": 15529329,
"value": "Name",
"backgroundColor": null,
"customField": "https://v1/links/custom_fields"
}
combined output:
[
{
"uri": "https://v1/websites",
"id": 28235674,
"background": null,
"createdDate": 1399585684000,
"lastActivityDate": 1430682494000,
"lastCommunicationDate": 1430682494000,
"lastNonCommunicationChronicleDate": 1430330886000,
"lastModifiedDate": 1449263116000,
"lastViewedDate": 1421429034000,
"preferredContactType": null,
"rss": "",
"emailAddresses": [
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "",
"type": "Work"
},
{
"email": "not found",
"type": "Work"
}
],
"phoneNumbers": [
],
"streetAddresses": [
],
"socialNetworks": [
{
"profileUrl": "http://twitter.com/",
"name": "Twitter"
},
{
"profileUrl": "http://www.facebook.com",
"name": "Facebook"
},
{
"profileUrl": "http://plus.google.com",
"name": "GooglePlus"
},
{
"profileUrl": "http://www.linkedin.com/",
"name": "LinkedIn"
},
{
"profileUrl": "http://twitter.com/",
"name": "Twitter"
}
],
"contactUrls": [
],
"tags": [
"tag1",
"tag2"
],
"mostRecentActivity": "https://v1/history/",
"mostRecentChronicle": "https://v1/history/",
"mostRecentCommunication": "https://v1/history/",
"mostRecentNonCommunicationChronicle": "https://v1/history/",
"projectStates": "https://v1/websites/",
"history": "https://v1/history",
"customFieldValues": [
],
"name": "",
"primaryDomain": "",
"domains": [
""
],
"associatedPeople": "https://v1/people",
"links": "https://v1/links",
"type": "https://v1/websites"
},
{
"uri": "https://links/custom_fields",
"id": 15529329,
"value": "Name",
"backgroundColor": null,
"customField": "https://links/custom_fields"
}
]
JSON.pretty_generate() is expecting a hash and you are passing an array of 2 hashes. Start with: combined.map { |c| f.write(JSON.pretty_generate(c)) } and then mapping them into your CSV should be as easy as following the CSV documentation.
Related
Due to a (somehow) special requirement to store data alongside a FHIR resource, I am looking for the cleanest way to extend it to maintain such extra data, which is basically dynamic key-value lists.
On the one hand I have something like the following, with 0..* sets of key-value, that I have tried to store nesting three extensions (adjusting the context by level) being the last a single limitation to the accepted types (string/integer), although it does not allow me to repeat ids, anyway I am getting around it using prefixes XD.
"n-lists-of-key-value": [
{
"list1-key1": "list1-value1",
"list1-key2": "list1-value1",
"list1-key3": "list1-value1"
},
{
"list2-key1": "list2-value1",
"list2-key2": "list2-value2"
}
]
becomes:
···,
"extension": [ {
"id": "n-lists-of-key-value",
"url": "http://x.org/fhir/Extension/NListOfKeyValueExtension",
"extension": [ {
"id": "list_1",
"url": "http://x.org/fhir/Extension/ListKeyValueExtension,
"extension": [ {
"id": "list1-key1",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list1-value1"
}, {
"id": "list1-key2",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list1-value1"
}, {
"id": "list1-key3",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list1-value1"
} ]
}, {
"id": "list_2",
"url": "http://x.org/fhir/Extension/ListKeyValueExtension,
"extension": [ {
"id": "list2-key1",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list2-value1"
}, {
"id": "list2-key2",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list2-value2"
} ]
} ]
} ],
But on the other hand I have this, which is what I find most difficult to model, because trying to reuse the extension definitions I run into (conceptual?) difficulties with slicing and the use of the discriminators, failing to get past the error "Element matches more than one slice".
"my-3-fixed-named-sets": {
"fixed-named-set1": {
"list1-key1": "list1-value1",
"list1-key2": "list1-value2",
"list1-key3": "list1-value3"
},
"fixed-named-set2": {
"list2-key1": "list2-value1",
"list2-key2": "list2-value2"
},
"fixed-named-set3": {
"list2-key1": "list2-value1",
"list2-key2": "list2-value2"
}
}
becomes:
···,
"extension": [ {
"id": "my-3-fixed-named-sets",
"url": "http://x.org/fhir/Extension/NListOfKeyValueExtension",
"extension": [ {
"id": "fixed-named-set1",
"url": "http://x.org/fhir/Extension/ListKeyValueExtension,
"extension": [ {
"id": "list1-key1",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list1-value1"
}, {
"id": "list1-key2",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list1-value2"
}, {
"id": "list1-key3",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list1-value3"
} ]
}, {
"id": "fixed-named-set2",
"url": "http://x.org/fhir/Extension/ListKeyValueExtension,
"extension": [ {
"id": "list2-key1",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list2-value1"
}, {
"id": "list2-key2",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list2-value2"
} ],
}, {
"id": "fixed-named-set3",
"url": "http://x.org/fhir/Extension/ListKeyValueExtension,
"extension": [ {
"id": "list2-key1",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list2-value1"
}, {
"id": "list2-key3",
"url": "http://x.org/fhir/Extension/KeyValueExtension,
"valueString": "list2-value3"
} ]
} ]
} ],
I am aware that by indiscriminately including new extensions with different names/urls, I can end up ingesting all the data, but I suspect that there is a much cleaner way and that I am failing in , probably, the way to include an extra condition into the discriminator (https://hl7.org/fhir/profiling.html#slicing), so that I could point to the proper slide? or de fact of extending extensions or extending value[x] slices?
"discriminator": [
{
"type": "value",
"path": "url"
},
{ "???": "??Extension.value.slideName??
···
Then my specific question would be... can I slice the value[x] of the extension of a specific profile, and which in turn make them extensions? this lasy is to get having n key value lists
Any comment is very welcome :)
I have a set of health care information in FHIR json format. Before storing such sensitive information i would like to anonymize the sensitive information such as name , phone number, address, physician contacts, etc.
I googled about anonimizing the fhir json and found there exist some security labels which classifies the information as Confidential , restricted , etc - https://www.hl7.org/fhir/valueset-security-labels.html
https://www.hl7.org/fhir/extension-auditevent-anonymized.html - This talks about the extension and the structure of anonymized data.
I am not sure how to replace these variables with security codes. Is there any api which does that or i have to find all the tags which are sensitive and loop the json and replace tags with security code.
Can someone give me an example of this can be achieved.
i have attached a sample json which i generated via synthea
{
"fullUrl": "urn:uuid:7a534e34-40a2-48ab-bc92-066d23251a8b",
"resource": {
"resourceType": "Patient",
"id": "7a534e34-40a2-48ab-bc92-066d23251a8b",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Generated by Synthea.Version identifier: v2.5.0-378-gee8c6470\n . Person seed: 6477291342685874262 Population seed: 1586936726889</div>"
},
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName",
"valueString": "Hilma831 Luettgen772"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/patient-birthPlace",
"valueAddress": {
"city": "Boston",
"state": "Massachusetts",
"country": "US"
}
},
{
"url": "http://synthetichealth.github.io/synthea/disability-adjusted-life-years",
"valueDecimal": 3.3178400761167306
},
{
"url": "http://synthetichealth.github.io/synthea/quality-adjusted-life-years",
"valueDecimal": 71.68215992388328
}
],
"identifier": [
{
"system": "https://github.com/synthetichealth/synthea",
"value": "7a534e34-40a2-48ab-bc92-066d23251a8b"
},
{
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "MR",
"display": "Medical Record Number"
}
],
"text": "Medical Record Number"
},
"system": "http://hospital.smarthealthit.org",
"value": "7a534e34-40a2-48ab-bc92-066d23251a8b"
},
{
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "SS",
"display": "Social Security Number"
}
],
"text": "Social Security Number"
},
"system": "http://hl7.org/fhir/sid/us-ssn",
"value": "999-80-4232"
},
{
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "DL",
"display": "Driver's License"
}
],
"text": "Driver's License"
},
"system": "urn:oid:2.16.840.1.113883.4.3.25",
"value": "S99911013"
},
{
"type": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"code": "PPN",
"display": "Passport Number"
}
],
"text": "Passport Number"
},
"system": "http://standardhealthrecord.org/fhir/StructureDefinition/passportNumber",
"value": "X66843459X"
}
],
"name": [
{
"use": "official",
"family": "Kreiger457",
"given": [
"Anthony633"
],
"prefix": [
"Mr."
]
}
],
"telecom": [
{
"system": "phone",
"value": "555-660-2614",
"use": "home"
}
],
"gender": "male",
"birthDate": "1939-12-25",
"deceasedDateTime": "2015-02-02T22:36:55+05:30",
"address": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/geolocation",
"extension": [
{
"url": "latitude",
"valueDecimal": 42.11942297974089
},
{
"url": "longitude",
"valueDecimal": -71.21834679934824
}
]
}
],
"line": [
"496 Tromp Mews Unit 96"
],
"city": "Walpole",
"state": "Massachusetts",
"postalCode": "02081",
"country": "US"
}
],
"maritalStatus": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus",
"code": "M",
"display": "M"
}
],
"text": "M"
},
"multipleBirthBoolean": false,
"communication": [
{
"language": {
"coding": [
{
"system": "urn:ietf:bcp:47",
"code": "en-US",
"display": "English"
}
],
"text": "English"
}
}
]
},
"request": {
"method": "POST",
"url": "Patient"
}
}
There is no 'standard' way to anonymize because anonymization is a risk-reduction approach that requires knowledge of both the threats you're trying to defend against and how the data needs to be used. When combined with other data, any element could potentially be 'sensitive'. It could also be essential to the intended analysis.
The extension you're looking at is specific to audit event. The way you'd actually flag a Patient resource as having been anonymized is using the ANONYED security tag in resource.meta. I don't believe there's a standard extension for marking what elements should be anonymized. If there were such an element, the element would also need to indicated how the data should be anonymized and the alorithm would typically need to take into account multiple elements - or even multiple resources. E.g. Should dates be changed to a random value, or just have the day shifted? Should all dates in the set of related resources be shifted by the same amount or should they be adjusted independently. If you're changing the practitioners pointed to by different resources, should they be left as is, each one changed randomly, the same practitioner changed to the same random practitioner?
How do I search for a person with BOTH given names I provide?
I have the following 2 patients who are "close". Everything (in the Human Name area) is the same except one of the GivenNames are the same.
Note "Apple" vs "Banana".
{
"resourceType": "Bundle",
"id": "269caf66-0ccc-43e7-b9a5-f16f84db0149",
"meta": {
"lastUpdated": "2019-11-20T19:30:26.858917+00:00"
},
"type": "searchset",
"link": [
{
"relation": "self",
"url": "https://localhost:44348/Patient?given=Jingerheimer"
}
],
"entry": [
{
"fullUrl": "https://localhost:44348/Patient/504f6bd3-e9b4-4846-8948-97bf09c70722",
"resource": {
"resourceType": "Patient",
"id": "504f6bd3-e9b4-4846-8948-97bf09c70722",
"meta": {
"versionId": "1",
"lastUpdated": "2019-11-20T19:26:11.005+00:00"
},
"identifier": [
{
"system": "ssn",
"value": "111-11-1111"
},
{
"system": "uuid",
"value": "da55d068e0784b359fa97498a11543c5"
}
],
"name": [
{
"family": "Smith",
"given": [
"John",
"Apple",
"Jingerheimer"
]
}
]
},
"search": {
"mode": "match"
}
},
{
"fullUrl": "https://localhost:44348/Patient/10054ce9-6141-4eca-bc5b-0978f8c8afcb",
"resource": {
"resourceType": "Patient",
"id": "10054ce9-6141-4eca-bc5b-0978f8c8afcb",
"meta": {
"versionId": "1",
"lastUpdated": "2019-11-20T19:26:48.962+00:00"
},
"identifier": [
{
"system": "ssn",
"value": "222-22-2222"
},
{
"system": "uuid",
"value": "52d09f9436d44591816fd229dd139523"
}
],
"name": [
{
"family": "Smith",
"given": [
"John",
"Banana",
"Jingerheimer"
]
}
]
},
"search": {
"mode": "match"
}
}
]
}
One has GivenNames that include "Apple". The other includes GivenNames that include "Banana".
This search works fine:
https://localhost:44348/Patient/?given=Jingerheimer
What I have tried is:
https://localhost:44348/Patient/?given=Jingerheimer&given=Apple
but that gives me no results.
Note, omitting "given=Jingerheimer" is not an option....that filters a bunch of others.
I'm trying to get
"Has BOTH of the given names I provide"
Your syntax is correct, so I think the server does not handle the search correctly. Can you check the self link for your second search to see if it reflects the search you performed? Does the result Bundle have an OperationOutcome detailing something went wrong? If all that seems okay, you'll need to check your server's code.
When the other person declined the meeting and proposed a new time. In Outlook, you can see the proposed time.
Now I am trying to use Microsoft Graph API to get that proposed time.
For example, the original meeting date is 2018-03-08, and the other person declined and proposed a new date 2018-03-12.
I tried
GET /beta/me/messages/{messageId}=?$expand=microsoft.graph.eventMessage/event
However, I cannot find the proposed time from the result returned. How can I get it? Thanks
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#users('576552d5-3bc0-42a6-a53d-bfceb405db23')/messages/$entity",
"#odata.type": "#microsoft.graph.eventMessage",
"#odata.etag": "W/\"DAAAABYAAACpTc/InBsuTYwTUBb+VIb4AADoRAyI\"",
"id": "AAMkADBlZTUwNTkxLWVmODgtNDVhNC1iZjhlLTdjNjA1ODZlMDI5MgBGAAAAAACUbnk-iwQZRbXMgkfKtmYhBwCpTc-InBsuTYwTUBb_VIb4AAAAAAEMAACpTc-InBsuTYwTUBb_VIb4AADnwc8mAAA=",
"createdDateTime": "2018-03-06T22:29:10Z",
"lastModifiedDateTime": "2018-03-06T22:29:11Z",
"changeKey": "DAAAABYAAACpTc/InBsuTYwTUBb+VIb4AADoRAyI",
"categories": [],
"receivedDateTime": "2018-03-06T22:29:11Z",
"sentDateTime": "2018-03-06T22:29:05Z",
"hasAttachments": false,
"internetMessageId": "<MWHPR15MB18399806CC97C61817C9A2B18BD90#MWHPR15MB1839.namprd15.prod.outlook.com>",
"subject": "New Time Proposed: Test",
"bodyPreview": "",
"importance": "normal",
"parentFolderId": "AAMkADBlZTUwNTkxLWVmODgtNDVhNC1iZjhlLTdjNjA1ODZlMDI5MgAuAAAAAACUbnk-iwQZRbXMgkfKtmYhAQCpTc-InBsuTYwTUBb_VIb4AAAAAAEMAAA=",
"conversationId": "AAQkADBlZTUwNTkxLWVmODgtNDVhNC1iZjhlLTdjNjA1ODZlMDI5MgAQAOnuCMgoRLdGs-1scw6i7EU=",
"conversationIndex": "AdO1mnWL6e4IyChEt0az/WxzDqLsRQAABO5D",
"isDeliveryReceiptRequested": null,
"isReadReceiptRequested": false,
"isRead": false,
"isDraft": false,
"webLink": "https://outlook.office365.com/owa/?ItemID=AAMkADBlZTUwNTkxLWVmODgtNDVhNC1iZjhlLTdjNjA1ODZlMDI5MgBGAAAAAACUbnk%2FiwQZRbXMgkfKtmYhBwCpTc%2FInBsuTYwTUBb%2BVIb4AAAAAAEMAACpTc%2FInBsuTYwTUBb%2BVIb4AADnwc8mAAA%3D&exvsurl=1&viewmodel=ReadMessageItem",
"inferenceClassification": "focused",
"unsubscribeData": [],
"unsubscribeEnabled": false,
"meetingMessageType": "meetingDeclined",
"type": "singleInstance",
"isOutOfDate": false,
"isAllDay": false,
"isDelegated": false,
"body": {
"contentType": "html",
"content": "Hi"
},
"sender": {
"emailAddress": {
"name": "Rose",
"address": "rose#example.com"
}
},
"from": {
"emailAddress": {
"name": "Rose",
"address": "rose#example.com"
}
},
"toRecipients": [
{
"emailAddress": {
"name": "Jack",
"address": "jack#example.com"
}
}
],
"ccRecipients": [],
"bccRecipients": [],
"replyTo": [],
"mentionsPreview": null,
"flag": {
"flagStatus": "notFlagged"
},
"startDateTime": {
"dateTime": "2018-03-08T04:00:00.0000000",
"timeZone": "UTC"
},
"endDateTime": {
"dateTime": "2018-03-08T05:00:00.0000000",
"timeZone": "UTC"
},
"location": {
"displayName": "Test",
"locationType": "default",
"uniqueIdType": "unknown"
},
"recurrence": null,
"event#odata.context": "https://graph.microsoft.com/beta/$metadata#users('576552d5-3bc0-42a6-a53d-bfceb405db23')/messages('AAMkADBlZTUwNTkxLWVmODgtNDVhNC1iZjhlLTdjNjA1ODZlMDI5MgBGAAAAAACUbnk-iwQZRbXMgkfKtmYhBwCpTc-InBsuTYwTUBb_VIb4AAAAAAEMAACpTc-InBsuTYwTUBb_VIb4AADnwc8mAAA%3D')/microsoft.graph.eventMessage/event/$entity",
"event": {
"#odata.etag": "W/\"qU3PyJwbLk2ME1AW/lSG+AAA6EQMeA==\"",
"id": "AAMkADBlZTUwNTkxLWVmODgtNDVhNC1iZjhlLTdjNjA1ODZlMDI5MgBGAAAAAACUbnk-iwQZRbXMgkfKtmYhBwCpTc-InBsuTYwTUBb_VIb4AAAAAAENAACpTc-InBsuTYwTUBb_VIb4AADnwkmiAAA=",
"createdDateTime": "2018-03-06T22:28:32.3852279Z",
"lastModifiedDateTime": "2018-03-06T22:29:11.4604154Z",
"changeKey": "qU3PyJwbLk2ME1AW/lSG+AAA6EQMeA==",
"categories": [],
"originalStartTimeZone": "Pacific Standard Time",
"originalEndTimeZone": "Pacific Standard Time",
"iCalUId": "040000008200E00074C5B7101A82E0080000000000000000000000000000000000000000310000007643616C2D5569640100000033324633333433392D433744452D344338362D393046452D44424639314131363444323900",
"reminderMinutesBeforeStart": 15,
"isReminderOn": true,
"hasAttachments": false,
"subject": "Test",
"bodyPreview": "Test",
"importance": "normal",
"sensitivity": "normal",
"isAllDay": false,
"isCancelled": false,
"isOrganizer": true,
"responseRequested": true,
"seriesMasterId": null,
"showAs": "busy",
"type": "singleInstance",
"webLink": "https://outlook.office365.com/owa/?itemid=AAMkADBlZTUwNTkxLWVmODgtNDVhNC1iZjhlLTdjNjA1ODZlMDI5MgBGAAAAAACUbnk%2FiwQZRbXMgkfKtmYhBwCpTc%2FInBsuTYwTUBb%2BVIb4AAAAAAENAACpTc%2FInBsuTYwTUBb%2BVIb4AADnwkmiAAA%3D&exvsurl=1&path=/calendar/item",
"onlineMeetingUrl": null,
"responseStatus": {
"response": "organizer",
"time": "0001-01-01T00:00:00Z"
},
"body": {
"contentType": "html",
"content": "Hi"
},
"start": {
"dateTime": "2018-03-08T04:00:00.0000000",
"timeZone": "UTC"
},
"end": {
"dateTime": "2018-03-08T05:00:00.0000000",
"timeZone": "UTC"
},
"location": {
"displayName": "Test",
"locationType": "default",
"uniqueId": "Test",
"uniqueIdType": "private"
},
"locations": [
{
"displayName": "Test",
"locationType": "default",
"uniqueId": "Test",
"uniqueIdType": "private"
}
],
"recurrence": null,
"attendees": [
{
"type": "required",
"status": {
"response": "declined",
"time": "2018-03-06T22:29:05Z"
},
"emailAddress": {
"name": "Rose",
"address": "rose#example.com"
}
}
],
"organizer": {
"emailAddress": {
"name": "Jack",
"address": "jack#example.com"
}
}
}
}
The API doesn't expose this information directly. The related entities just don't have the fields defined to show this information. However, the data is obviously there, you just need to know how to get to it. The answer lies in extended properties.
Basically since Graph doesn't expose these values, we need to dig into the MAPI properties that store this data. After a little digging through the Exchange server protocol docs I found PidLidAppointmentProposedStartWhole and PidLidAppointmentProposedEndWhole. We just need to translate those MAPI property definitions into the Graph extended property syntax.
From the doc, those are both in the PSETID_Appointment namespace, which uses the GUID {00062002-0000-0000-C000-000000000046}. PidLidAppointmentProposedStartWhole uses id 0x8250, and PidLidAppointmentProposedEndWhole uses id 0x8251. So those should translate to:
PidLidAppointmentProposedStartWhole: 'SystemTime {00062002-0000-0000-C000-000000000046} Id 0x8250'
PidLidAppointmentProposedEndWhole: 'SystemTime {00062002-0000-0000-C000-000000000046} Id 0x8251'
If we use those in an $expand clause as per Get singleValueLegacyExtendedProperty, we get something like:
GET /me/mailfolders/inbox/messages?$expand=singleValueExtendedProperties($filter=id eq 'SystemTime {00062002-0000-0000-C000-000000000046} Id 0x8250' or id eq 'SystemTime {00062002-0000-0000-C000-000000000046} Id 0x8251')
And the response looks something like this (other properties omitted):
{
"subject": "New Time Proposed: Let's meet",
"singleValueExtendedProperties": [
{
"id": "SystemTime {00062002-0000-0000-c000-000000000046} Id 0x8250",
"value": "2018-03-20T20:00:00Z"
},
{
"id": "SystemTime {00062002-0000-0000-c000-000000000046} Id 0x8251",
"value": "2018-03-20T21:00:00Z"
}
]
}
How do I fetch all user Ids under our company domain using Google API?
I want to get list of all users under our domain.
Then I want to get a list of all emails for each user.
I think you are referring to Retrieve all users in a domain:
To retrieve all users in the same domain, use the following GET request and include the authorization described in Authorize requests. For readability, this example uses line returns:
GET https://www.googleapis.com/admin/directory/v1/users
?domain=primary domain name&pageToken=token for next results page
&maxResults=max number of results per page
&orderBy=email, givenName, or familyName
&sortOrder=ascending or descending
&query=email, givenName, or familyName:the query's value*
By default, the system returns a list of 100 users in the alphabetical order of the user's email address:
GET https://www.googleapis.com/admin/directory/v1/users?domain=example.com&maxResults=2
A successful response returns an HTTP 200 status code. Along with the status code, the response returns 2 user accounts in the example.com domain (maxResults=2):
{
"kind": "directory#users",
"users": [
{
"kind": "directory#user",
"id": "the unique user id",
"primaryEmail": "liz#example.com",
"name": {
"givenName": "Liz",
"familyName": "Smith",
"fullName": "Liz Smith"
},
"isAdmin": true,
"isDelegatedAdmin": false,
"lastLoginTime": "2013-02-05T10:30:03.325Z",
"creationTime": "2010-04-05T17:30:04.325Z",
"agreedToTerms": true,
"hashFunction: "SHA-1",
"suspended": false,
"changePasswordAtNextLogin": false,
"ipWhitelisted": false,
"ims": [
{
"type": "work",
"protocol": "gtalk",
"im": "lizim#talk.example.com",
"primary": true
}
],
"emails": [
{
"address": "liz#example.com",
"type": "work",
"customType": "",
"primary": true
}
],
"addresses": [
{
"type": "work",
"customType": "",
"streetAddress": "1600 Amphitheatre Parkway",
"locality": "Mountain View",
"region": "CA",
"postalCode": "94043"
}
],
"externalIds": [
{
"value": "employee number",
"type": "custom",
"customType": "office"
}
],
"relations": [
{
"value": "susan",
"type": "friend",
"customType": ""
}
],
"organizations": [
{
"name": "Google Inc.",
"title": "SWE",
"primary": true,
"customType": "",
"description": "Software engineer"
}
],
"phones": [
{
"value": "+1 nnn nnn nnnn",
"type": "work"
}
],
"aliases": [
"lizsmith#example.com",
"lsmith#example.com"
],
"nonEditableAliases: [
"liz#test.com"
],
"customerId": "C03az79cb",
"orgUnitPath": "corp/engineering",
"isMailboxSetup": true,
"includeInGlobalAddressList": true
},
{
"kind": "directory#user",
"id": "user unique ID",
"primaryEmail": "admin2#example.com",
"name": {
"givenName": "admin",
"familyName": "two",
"fullName": "admin two"
},
"isAdmin": true,
"isDelegatedAdmin": true,
"lastLoginTime": "2013-02-05T10:30:03.325Z",
"creationTime": "2010-04-05T17:30:04.325Z",
"agreedToTerms": true,
"hashFunction: "SHA-1",
"suspended": true,
"suspensionReason": "ADMIN",
"changePasswordAtNextLogin": false,
"ipWhitelisted": false,
"emails": [
{
"address": "admin2#example.com",
"type": "work",
"customType": "",
"primary": true
}
],
"externalIds": [
{
"value": "contractor license number",
"type": "custom",
"customType": "work"
}
],
"relations": [
{
"value": "liz",
"type": "friend",
"customType": ""
}
],
"aliases": [
"second_admin#example.com"
],
"nonEditableAliases: [
"admin#test.com"
],
"customerId": "C03az79cb",
"orgUnitPath": "corp/engineering",
"isMailboxSetup": true,
"includeInGlobalAddressList": true
}
],
"nextPageToken": "next page token"
}
You can also check out Retrieve all account users
To retrieve all users in an account which can consist of multiple domains, use the following GET request and include the authorization described in Authorize requests.