import std/json, std/options, std/sequtils, std/sets, std/strutils, std/tables, std/times import timeutils import ./utils type AddressObj* = object id*: string city*: string state*: string street*: string zipCode*: string createdAt*: Option[DateTime] lastUpdatedAt*: Option[DateTime] FamilyObj* = object id*: string name*: string headsOfHousehold*: seq[string] headOfHouseholdIds*: seq[string] primaryAddressId*: seq[string] members*: seq[string] memberIds*: seq[string] createdAt*: Option[DateTime] lastUpdatedAt*: Option[DateTime] PersonObj* = object id*: string preferredName*: string firstName*: string middleNames*: string lastName*: string birthDate*: Option[DateTime] gender*: string primaryPhoneNumber*: string primaryEmailAddress*: string addresses*: seq[string] addressIds*: seq[string] marriedTo*: string marriedToId*: string anniversary*: Option[DateTime] parents*: seq[string] parentIds*: seq[string] children*: seq[string] childIds*: seq[string] relationshipToHff*: seq[string] createdAt*: Option[DateTime] lastUpdatedAt*: Option[DateTime] apiPermissions*: seq[string] RecordType* = enum rtPerson, rtFamily, rtAddress SyncRecordObj* = object id*: string knownIds*: HashSet[string] recType*: RecordType notionId*: string pcoId*: string Address* = ref AddressObj Family* = ref FamilyObj Person* = ref PersonObj SyncRecord* = ref SyncRecordObj MembershipDataSet* = ref object addresses*: TableRef[string, Address] families*: TableRef[string, Family] people*: TableRef[string, Person] func sameContents[T](a: seq[T], b: seq[T]): bool = let aCount = toCountTable(a) let bCount = toCountTable(b) for k in aCount.keys: if not bCount.hasKey(k) or aCount[k] != bCount[k]: return false return aCount.len == bCount.len func `==`*(a, b: Family): bool = return a.name == b.name and sameContents(a.headOfHouseholdIds, b.headOfHouseholdIds) and sameContents(a.primaryAddressId, b.primaryAddressId) and sameContents(a.memberIds, b.memberIds) func `==`*(a, b: Person): bool = return a.preferredName == b.preferredName and a.firstName == b.firstName and a.middleNames == b.middleNames and a.lastName == b.lastName and a.birthDate == b.birthDate and a.gender == b.gender and a.primaryPhoneNumber == b.primaryPhoneNumber and a.primaryEmailAddress == b.primaryEmailAddress and a.anniversary == b.anniversary and a.marriedToId == b.marriedToId and sameContents(a.addressIds, b.addressIds) and sameContents(a.parentIds, b.parentIds) and sameContents(a.childIds, b.childIds) and sameContents(a.relationshipToHff, b.relationshipToHff) func `$`*(rt: RecordType): string = case rt: of rtPerson: "Person" of rtFamily: "Family" of rtAddress: "Address" func initSyncRecord*(recType: RecordType, notionId, pcoId: string, knownIds = initHashSet[string]()): SyncRecord = result = SyncRecord( recType: recType, notionId: notionId, pcoId: pcoId, knownIds: knownIds) result.knownIds.incl(notionId) result.knownIds.incl(pcoId) func toPage*(a: Address): JsonNode = %*{ "properties": { "City": makeTextProp("rich_text", a.city), "State": makeSelectProp(a.state), "Street Address": makeTextProp("title", a.street), "Zip Code": makeTextProp("rich_text", a.zipCode) } } func toPage*(f: Family): JsonNode = %*{ "properties": { "Name": makeTextProp("title", f.name), "Head(s) of Household": makeRelationProp(f.headOfHouseholdIds), "Primary Address": makeRelationProp(f.primaryAddressId), "Members": makeRelationProp(f.memberIds) } } func toPage*(p: Person): JsonNode = %*{ "properties": { "Preferred Name": makeTextProp("title", p.preferredName), "First Name": makeTextProp("rich_text", p.firstName), "Middle Names": makeTextProp("rich_text", p.middleNames), "Last Name": makeTextProp("rich_text", p.lastName), "Birth Date": makeDateProp(p.birthDate), "Gender": makeSelectProp(p.gender), "Primary Phone Number": { "phone_number": p.primaryPhoneNumber }, "Email Address": { "email": p.primaryEmailAddress }, "Relationship to HFF": makeMultiSelectProp(p.relationshipToHff), "Address": makeRelationProp(p.addressIds), "Married To": makeRelationProp(@[p.marriedToId]), "Anniversary": makeDateProp(p.anniversary), "Parents": makeRelationProp(p.parentIds), "Children": makeRelationProp(p.childIds), "System: API Permissions": makeMultiSelectProp(p.apiPermissions), } } func toPage*(sr: SyncRecord): JsonNode = result = %*{ "properties": { "Notion Rec Id": makeTextProp("title", sr.notionId), "PCO Rec Id": makeTextProp("rich_text", sr.pcoId), "Alternate IDs": makeTextProp("rich_text", toSeq(sr.knownIds).join(";")) } } case sr.recType of rtAddress: result{"properties", "Rec Type"}= %"Address" of rtFamily: result{"properties", "Rec Type"}= %"Family" of rtPerson: result{"properties", "Rec Type"}= %"Person" proc addressFromPage*(page: JsonNode): Address = result = Address( id: page["id"].getStr, city: page.getText("City"), state: page.getSelect("State"), street: page.getTitle("Street Address"), zipCode: page.getText("Zip Code"), createdAt: some(parseIso8601(page["created_time"].getStr)), lastUpdatedAt: some(parseIso8601(page["last_edited_time"].getStr))) proc familyFromPage*(page: JsonNode): Family = result = Family( id: page["id"].getStr, name: page.getTitle("Name"), headsOfHousehold: page.getRolledupDisplayValues("Head(s) of Household (display)"), headOfHouseholdIds: page.getRelationIds("Head(s) of Household"), primaryAddressId: page.getRelationIds("Primary Address"), members: page.getRolledupDisplayValues("Members (display)"), memberIds: page.getRelationIds("Members"), createdAt: some(parseIso8601(page["created_time"].getStr)), lastUpdatedAt: some(parseIso8601(page["last_edited_time"].getStr))) proc personFromPage*(page: JsonNode): Person = let marriedToIds = page.getRelationIds("Married To") result = Person( id: page["id"].getStr, preferredName: page.getTitle("Preferred Name"), firstName: page.getText("First Name"), middleNames: page.getText("Middle Names"), lastName: page.getText("Last Name"), birthDate: page.getDateTime("Birth Date"), gender: page.getSelect("Gender"), primaryPhoneNumber: page.getPhone("Primary Phone Number"), primaryEmailAddress: page.getEmail("Email Address"), relationshipToHff: page.getMultiSelect("Relationship to HFF"), addresses: page.getRolledupDisplayValues("Full Address"), addressIds: page.getRelationIds("Address"), marriedTo: if marriedToIds.len == 0: "" else: page.getRolledupDisplayValues("Married To (display)")[0], marriedToId: if marriedToIds.len == 0: "" else: marriedToIds[0], anniversary: page.getDateTime("Anniversary"), parents: page.getRolledupDisplayValues("Parents (display)"), parentIds: page.getRelationIds("Parents"), children: page.getRolledupDisplayValues("Children (display)"), childIds: page.getRelationIds("Children"), createdAt: some(parseIso8601(page["created_time"].getStr)), lastUpdatedAt: some(parseIso8601(page["last_edited_time"].getStr)), apiPermissions: page.getMultiSelect("System: API Permissions")) func syncRecordFromPage*(page: JsonNode): SyncRecord = result = SyncRecord( id: page["id"].getStr, notionId: page.getTitle("Notion Rec Id"), pcoId: page.getText("PCO Rec Id"), knownIds: toHashSet(page.getText("Alternate IDs").split(";"))) case page.getSelect("Record Type") of "Address": result.recType = rtAddress of "Family": result.recType = rtFamily of "Person": result.recType = rtPerson