Cardholder Attendance Report

The windows client currently grabs all people and then checks against the events in the database to see when the person signed in for the day selected. The filter and processing are shown in the C# example as that is the language I am most familiar with. Please note this example should also filter for the earliest event in the day but instead show all the events found and groups them by person and event. The calls to the api are shown in all languages.

Example in C#

var currentInstance  = await client.GetCurrentInstanceAsync();
var app = (await client.GetAppsAsync(currentInstance)).Where(a=> a.ApiKey == "MercuryService").FirstOrDefault(); 
var eventAccessGrantedKey = (await client.GetEventTypesAsync(app)).Where(a=> a.CommonName == "Access Granted").FirstOrDefault()?.Key;
var eventAccessDeniedKey = (await client.GetEventTypesAsync(app)).Where(a=> a.CommonName == "Access Denied").FirstOrDefault()?.Key;
var peopleList = new List<PersonInfo>();
var peopleCount = 1000;
var page = 0;
//The call to get all the people in the instance
while(peopleCount==1000)
{
	var people = (await client.SearchAsync(currentInstance, "type:Person", page++, peopleCount, includeChildFolders:true, spanScope:false)).OfType<PersonInfo>();
	
	peopleCount = people.Count();
	if (peopleCount>0)
	{
		peopleList.AddRange(people);
	}
}

var eventList = new List<EventMessageData>();
var eventCount = 1000;
var eventPage = 0;
//The call to get all the events of intrest in the instance
while(eventCount==1000)
{
	var eventMessageData = await client.GetEventsAsync(currentInstance, 
		eventPage++, 
		eventCount, 
		includeSubFolders:false, 
		includeSharedInstances:false,
		spanScope:false, 
		requiresAck:false, 
		startingOn: DateTime.Today.AddHours(8), 
		endingOn: DateTime.Today.AddDays(1).AddTicks(-1), 
		priorityThreshold: 0, 
		forKeys: new[] {eventAccessGrantedKey, eventAccessDeniedKey}
	);
		
	eventCount = eventMessageData.Count();
	if (eventCount>0)
	{
		eventList.AddRange(eventMessageData);
	}
}

var PersonAndEvent = new List<Tuple<PersonInfo,EventMessageData>>();

foreach(var thisEvent in eventList)
{

	PersonAndEvent.Add(new Tuple<PersonInfo,EventMessageData>(peopleList.Where(p=>thisEvent.ObjectLinks.Any(l=> l.LinkedObjectKey == p.Key && l.Relation =="Person")).FirstOrDefault(),thisEvent));
}

Example in CURL

#The call to get people - May need to loop here
curl -X GET \
  'https://keepapi.feenicshosting.com/api/f/INSTANCE_KEY_HERE?q=type%3APerson&page=0&pageSize=1000&includeChildFolders=True&spanScope=False' \
  -H 'Accept: */*' \
  -H 'Authorization: Bearer TOKEN_GOES_HERE'  \
  -H 'Cache-Control: no-cache' \
  -H 'Connection: keep-alive' \
  -H 'Host: keepapi.feenicshosting.com' \
  -H 'accept-encoding: gzip, deflate' \
  -H 'cache-control: no-cache'

#The call to get the events we are interested in - May need to loop here
curl -X GET \
  'https://keepapi.feenicshosting.com/api/f/INSTANCE_KEY_HERE/events?page=0&pageSize=1000&includeSubFolders=False&includeSharedInstances=False&spanScope=False&requiresAck=False&startingOn=2019-06-27T12%3A00%3A00z&endingOn=2019-06-28T03%3A59%3A59z&priorityThreshold=0&forKeys=EVENT_ACCESS_GRANTED_KEY,EVENT_ACCESS_DENIED_KEY' \
  -H 'Accept: */*' \
  -H 'Authorization: Bearer TOKEN_GOES_HERE'  \
  -H 'Connection: keep-alive' \
  -H 'Host: keepapi.feenicshosting.com' \
  -H 'accept-encoding: gzip, deflate' \
  -H 'cache-control: no-cache'

Cardholder Attendance Report - Aggregate

This version still requires two calls Depending on the information you want but it does not retrieve all people that may or may not have signed in. It grabs the first Access Granted Event for a person for the day in question (Note: the windows report shows people that have had an access granted or denied here). We could be done here but if we want more information about the person we will need to query for that person. I suggest gathering all the people of interestís Ids and then make a call to retrieve them all at once. These calls could still run into timeout issues so best to limit and skip (page) the results. The filter and processing are shown in the C# example as that is the language I am most familiar with. The calls to the api are shown in all languages. In this example we find three people that are relevant to our events.

Example in C#


var currentInstance  = await client.GetCurrentInstanceAsync();
var app = (await client.GetAppsAsync(currentInstance)).Where(a=> a.ApiKey == "MercuryService").FirstOrDefault(); 
var eventAccessGrantedKey = (await client.GetEventTypesAsync(app)).Where(a=> a.CommonName == "Access Granted").FirstOrDefault()?.Key;
var operations = 
	new BsonDocument[] 
	{ 
	 	new BsonDocument("$match", 
	    new BsonDocument
	        {
	            { "ObjectLinks.LinkedObjectId", 
	    new ObjectId(eventAccessGrantedKey) }, 
	            { "InFolderId", 
	    new ObjectId(currentInstance.Key) }, 
	            { "OccurredOn.Date", 
	    new BsonDocument
	            {
	                { "$gte", 
	    new DateTime(2019, 6, 26, 0, 0, 0) }, 
	                { "$lt", 
	    new DateTime(2019, 6, 27, 0, 0, 0) }
	            } }
	        }),
	    new BsonDocument("$sort", 
	    new BsonDocument("OccurredOn", 1)),
	    new BsonDocument("$skip", 0),
	    new BsonDocument("$limit", 1000),
	    new BsonDocument("$unwind", 
	    new BsonDocument("path", "$ObjectLinks")),
	    new BsonDocument("$match", 
	    new BsonDocument("ObjectLinks.Relation", "Person")),
	    new BsonDocument("$group", 
	    new BsonDocument
	        {
	            { "_id", "$ObjectLinks.LinkedObjectId" }, 
	            { "OccuredOn", 
	    new BsonDocument("$min", "$OccurredOn") }, 
	            { "ObjectLinks", 
	    new BsonDocument("$min", "$ObjectLinks") }, 
	            { "EventData", 
	    new BsonDocument("$min", "$EventData") }
	        })
	};
//The call to get all the events of interest in the instance
var AttendanceReport = await client.AggregateAsync(currentInstance, "Events", operations);
var PeoplesIdsInReport = new List<string>();
foreach(var document in AttendanceReport)
{
	BsonValue personId;
	if (document.TryGetValue("_id", out personId) && !string.IsNullOrWhiteSpace(personId.ToString()))
	{
		PeoplesIdsInReport.Add(personId.ToString());
	}	
}
//The call to get the needed people the instance
var PeopleInAttendenceReport = await client.GetByIdsAsync(PeoplesIdsInReport.ToArray());

Example in CURL

curl -X POST \
  'https://keepapi.feenicshosting.com/api/f/INSTANCE_KEY_HERE/aggregate/Events?queryTimeout=30&jsonStrict=False%20' \
  -H 'Accept: */*' \
  -H 'Authorization: Bearer TOKEN_GOES_HERE'  \
  -H 'Connection: keep-alive' \
  -H 'Content-Type: application/json' \
  -H 'Host: keepapi.feenicshosting.com' \
  -H 'accept-encoding: gzip, deflate' \
  -H 'cache-control: no-cache' \
  -H 'content-length: 679' \
  -d '["
        { 
            \"$match\" : 
            { 
                \"ObjectLinks.LinkedObjectId\" : ObjectId(\"EVENT_ACCESS_GRANTED_KEY\"),
                \"InFolderId\" : ObjectId(\"INSTANCE_KEY_HERE\"), 
                \"OccurredOn.Date\" : 
                { 
                    \"$gte\" : ISODate(\"2019-06-26T04:00:00Z\"),
                    \"$lt\" : ISODate(\"2019-06-27T04:00:00Z\") 
                } 
            } 
        }","
        { \"$sort\" : { \"OccurredOn\" : 1 } }","
        { \"$skip\" : 0 }","
        { \"$limit\" : 1000 }","
        { \"$unwind\" : { \"path\" : \"$ObjectLinks\" } }","
        { \"$match\" : { \"ObjectLinks.Relation\" : \"Person\" } }","
        { 
            \"$group\" : 
            { 
                \"_id\" : \"$ObjectLinks.LinkedObjectId\",
                \"OccuredOn\" : { \"$min\" : \"$OccurredOn\" },
                \"ObjectLinks\" : { \"$min\" : \"$ObjectLinks\" }, 
                \"EventData\" : { \"$min\" : \"$EventData\" } 
            } 
        }"
    ]'

# Get the People Required
curl -X POST \
  https://keepapi.feenicshosting.com/api/baseinfo \
  -H 'Accept: */*' \
  -H 'Authorization: Bearer TOKEN_GOES_HERE'  \
  -H 'Connection: keep-alive' \
  -H 'Content-Type: application/json' \
  -H 'Host: keepapi.feenicshosting.com' \
  -H 'accept-encoding: gzip, deflate' \
  -H 'cache-control: no-cache' \
  -H 'content-length: 82' \
  -d '["PERSON_1_KEY"," PERSON_2_KEY "," PERSON_3_KEY "]'

Example Of Aggregate Used in JSON

[
    { 
        "$match" : 
        { 
            "ObjectLinks.LinkedObjectId" : ObjectId("EVENT_ACCESS_GRANTED_KEY"),
            "InFolderId" : ObjectId("INSTANCE_KEY_HERE"), 
            "OccurredOn.Date" : 
            { 
                "$gte" : ISODate("2019-06-26T04:00:00Z"),
                "$lt" : ISODate("2019-06-27T04:00:00Z") 
            } 
        } 
    },
    { "$sort" : { "OccurredOn" : 1 } },
    { "$skip" : 0 }","
    { "$limit" : 1000 }","
    { "$unwind" : { "path" : "$ObjectLinks" } },
    { "$match" : { "ObjectLinks.Relation" : "Person" } },
    { 
        "$group" : 
        { 
            "_id" : "$ObjectLinks.LinkedObjectId",
            "OccuredOn" : { "$min" : "$OccurredOn" },
            "ObjectLinks" : { "$min" : "$ObjectLinks" }, 
            "EventData" : { "$min" : "$EventData" } 
        } 
    }
]