Send a friend email for all assignments with plain-text contact information
Most people don't know where to check their ministering assignment, and don't know when it has changed.
We want to periodically send a church email (and eventually texts) showing assignments in a simple way, and with a call to action.
https://lcr.churchofjesuschrist.org/ministering?lang=eng&type=EQ&tab=assigned
Here's the step-by-step overview:
- "Scrape" the ministering assignment data from the JSON "script" tag in the HTML
- Transform that data from the format used for page layout to a more generally useful format
- Fetch the missing person information (phone numbers, email, address) from the
card
API - Template the complete data set as you wish to display it in print or email
- Send the batch of emails, one API call per each
- Visit Ministering Assignments at https://lcr.churchofjesuschrist.org/ministering?lang=eng&type=EQ&tab=assigned.
- Cmd ⌘ + Alt ⌥ + i to open the JavaScript inspector console
- Copy and paste this script
var script = document.createElement('script'); script.src="https://churchofjesuschristdev.github.io/ministering-assignments/demo.js"; document.body.append(script);
- Wait for it to load.
- JSON: You can view or Download the assignments as JSON:
console.log(CJCD.toJSON()); CJCD.download('ministering.json', null, 2);
- CSV: You can view or Download as CSV as well:
console.log(CJCD.toCSV()); CJCD.download('assignments.csv', null, 2);
Security Notice: Generally speaking it's a bad idea to run scripts from randos on the Internet. However, you can view the full contents of that script in this repository, and being that it's encrypted with HTTPS, and hosted via Github, you have every reasonable assurance that the script is authentically from this repository. :)
This part is relatively simple.
Authentication is cookie-based, so no authentication details are provided.
async function sendEmail(message) {
return fetch("https://lcr.churchofjesuschrist.org/services/leader-messaging/send-message?lang=eng", {
"headers": {
"accept": "application/json, text/plain, */*",
"content-type": "application/json;charset=UTF-8"
},
"credentials": "include",
"method": "POST",
"body": JSON.stringify(message, null, 2)
});
}
var message = {
lang: 'eng',
recipients: [ 1000000001 ],
allowReplyAll: false,
subject: "[Test] You're a Wizard Harry!",
messageBody: 'Harry! You've been accepted to Hogwarts School Stake.",
type: 'EQ'
};
sendEmail(message);
Unfortunately, it doesn't seem possible to get the JSON you need from the API.
Instead, you have to get it from a JSON object stored in a quasi "script tag".
var data = JSON.parse($('script[type="application/json"]').innerText);
The format of the data in the HTML isn't ideal, but it's workable. Here's an abreviated view:
{
"props": {
"pageProps": {
"initialState": {
"ministeringData": {
//
// misnomer: these are districts
//
"elders": [
{
"districtName": "District 1",
"districtUuid": "50000000-0000-4000-8000-000000000001",
"supervisorName": "Black, Sirius",
"supervisorLegacyCmisId": 1000000005,
"supervisorPersonUuid": "10000000-0000-4000-8000-000000000005",
"companionships": [
"id": "60000000-0000-4000-8000-000000000001",
"ministers": [
{
"personUuid": "10000000-0000-4000-8000-000000000003",
"legacyCmisId": 1000000003,
"name": "Weasley, Ronald",
"nameOrder": 2,
"email": "[email protected]",
"interviews": [
{
"id": "A0000000-0000-4000-8000-000000000001",
"date": "2020-03-01T00:00:00.000",
"timestamp": "2020-03-01T06:00:00.000+0000"
}
],
"youthBasedOnAge": false
}
],
"assignments": [
{
//
// misnomer: this refers to head of household, as the family identifier
//
"personUuid": "10000000-0000-4000-8000-000000000001",
"legacyCmisId": 1000000001,
"name": "Potter, Harry James & Ginevra Molly",
"nameOrder": 1,
"youthBasedOnAge": false
}
],
"recentlyChangedDate": "2021-06-01T06:00:00.000+0000",
"recentlyChangedDateInMilliseconds": 1622527200000
]
}
]
}
}
}
}
}
Take a look at the Full Data Shape for more detail.
And this is how you can access it
// Misnomer, these are Ministering Districts
data.props.pageProps.initialState.ministeringData.elders
// Sadly, no ID
data.props.pageProps.initialState.ministeringData.elders[0].supervisorName
// Individual ID and email, but no phone number
data.props.pageProps.initialState.ministeringData.elders[0].companionships[0].ministers[0].legacyCmisId
// Individual ID, but no contact info
data.props.pageProps.initialState.ministeringData.elders[0].companionships[0].assignments[0].legacyCmisId
If we want to omit the most garbage and get the most useful data only:
console.log(
JSON.stringify(
{
props: {
pageProps: {
initialState: {
ministeringData: {
elders: data.props.pageProps.initialState.ministeringData.elders
}
}
}
}
},
null,
2
)
);
You can then save that to a file and use it as a cache.
// load companionship data and card cache, if any
CJCD.init(data, cards || {});
// transform the data into an individual-oriented format
CJCD.organize();
// get the missing information from card API
await CJCD.getCards();
var assignments = CJCD.toJSON();
assignments
will look like this:
[
{
"member": {
"nickname": "",
"given_name": "",
"family_name": "",
"phone": "",
"email": "",
"address": "",
"district": ""
},
"companions": [],
"assignments": [],
"ministers": []
}
]
If you want to get the card values to save them for local caching:
console.log(JSON.stringify(CJCD._cards, null, 2));
This will put all person info in cards
and all companionship info in people
.
var CJCD;
CJCD = (function () {
var ids = {};
var people = {};
function getPerson(id) {
if (!people[id]) {
ids[id] = true;
people[id] = {
companions: {},
assignments: {},
ministers: {},
};
}
return people[id];
}
function organize() {
CJCD._data.props.pageProps.initialState.ministeringData.elders.forEach(
function (district) {
district.companionships.forEach(function (ship, i) {
//console.log("companionship", i);
// this is a little imperfect for people in multiple companionships, but should work generally
ship.ministers.forEach(function (m, j) {
//console.log("minister", j);
ids[m.legacyCmisId] = true;
var p = getPerson(m.legacyCmisId);
if (ship.ministers) {
ship.ministers.forEach(function (n) {
if (m === n) {
return;
}
p.companions[n.legacyCmisId] = true;
});
}
if (ship.assignments) {
ship.assignments.forEach(function (a) {
var q = getPerson(a.legacyCmisId);
q.ministers[m.legacyCmisId] = true;
p.assignments[a.legacyCmisId] = true;
ids[a.legacyCmisId] = true;
});
}
});
});
}
);
}
async function getCards() {
for (id in ids) {
await getCard(id);
}
}
function getCachedCard(id) {
return CJCD._cards[id];
}
async function getCard(id) {
if (CJCD._cards[id]) {
return CJCD._cards[id];
}
//console.log(Object.keys(cards).length, id);
var cardUrl =
`https://lcr.churchofjesuschrist.org/services/member-card` +
`?id=${id}&includePriesthood=true&lang=eng&type=INDIVIDUAL`;
return fetch(cardUrl, {
credentials: "same-origin",
}).then(function (resp) {
return resp.json().then(function (data) {
CJCD._cards[id] = data;
return CJCD._cards[id];
});
});
}
function toJSON() {
var assignments = [];
return Object.keys(CJCD.ids).map(function (id) {
var p = CJCD.getPerson(id);
var c = CJCD.getCachedCard(id);
var isMinister = false;
var assignment = {
member: p, // TODO format member
assignments: [],
ministers: [],
companions: [],
};
if (Object.keys(p.assignments).length) {
isMinister = true;
assignment.assignments = Object.keys(p.assignments).map(
function (m) {
var family = CJCD.getCachedCard(m);
// TODO format in a reasonable way
return family;
}
);
}
if (Object.keys(p.companions).length > 0) {
isMinister = true;
assignment.companions = Object.keys(p.companions).map(function (
id
) {
var companion = CJCD.getCachedCard(id);
// TODO format in a reasonable way
return companion;
});
}
if (Object.keys(p.ministers).length > 0) {
assignment.ministers = Object.keys(p.ministers).map(function (
id
) {
var minister = CJCD.getCachedCard(id);
// TODO format in a reasonable way
return minister;
});
}
return assignment;
});
}
return {
_data: null,
_cards: null,
_people: people,
ids: ids,
init: async function (_data, _cards) {
CJCD._data = _data;
CJCD._cards = _cards || CJCD._cards || {};
CJCD.organize();
await CJCD.getCards();
},
organize: organize,
getPerson: getPerson,
getCard: getCard,
getCards: getCards,
getCachedCard: getCachedCard,
toJSON: toJSON,
};
})();
if ("undefined" != typeof module) {
module.exports = CJCD;
}
This will create an email for everyone who has a ministering assignment and/or is assigned to a set of ministers.
var CJCD;
if ("undefined" === typeof CJCD) {
CJCD = require("./email.js");
}
var assigneeSubject = "Will you do the Elder's Quorum a Favor?";
var assigneeMessage = `\n<p>We wanted to let know who your ministers are. Feel free to reach out to them if you ever need to borrow a cup of sugar, or get a ride to the airport. :)\n`;
var ministerSubject = "Updated Assignments: Will you do the Elder's Quorum a Favor?";
var ministerMessage =
`\n<p>We've updated ministering assignments wanted to make sure you have yours. We also have two favors to ask: ` +
`\n<p>1. We'd like to ask you to pray for the families you minister to by name today (and often). ` +
`\n<p>2. If you haven't heard from your ministers will you do a little reverse ministering and reach out to them? `;
function formatTel(t) {
t = (t || "").replace(/\D/g, "").trim();
if (!t) {
return "";
}
if (t.length > 10) {
if ("1" !== t[0] || t.length > 11) {
console.warn("phone too long:", t);
}
t = t.slice(-10);
//return "";
}
if (t.length < 10) {
console.warn("phone too short:", t);
t = t.padStart(10, "_");
//return "";
}
return `(${t.slice(0, 3)}) ${t.slice(3, 6)}-${t.slice(6, 10)}`;
}
function formatEmail(name, email) {
if (!email || !/@/.test(email)) {
return "";
}
//return `<pre>"${name}" <${email}>,</pre>`;
return `${email}`;
}
function formatCard(c) {
return (
"<li>" +
c.spokenName +
" " +
c.age +
" " +
("FEMALE" === c.gender ? "F" : "M") +
"\t" +
formatTel(c.individualPhone || c.phone) +
"\t" +
formatEmail(c.spokenName, c.email) +
"</li>\n"
);
}
function helloMinisters(p, c) {
var lead = `Hi ${c.spokenName.split(" ").shift()}, \n` + assigneeMessage;
return lead;
}
function helloAssignments(p, c) {
var lead = `Hey ${c.spokenName.split(" ").shift()}, \n` + ministerMessage;
return lead;
}
async function createMessages() {
CJCD.organize();
await CJCD.getCards();
return Object.keys(CJCD.ids)
.filter(function (id) {
var p = CJCD.getPerson(id);
var c = CJCD.getCachedCard(id);
if (!Object.keys(p.assignments)) {
console.warn("no assignments", c.spokenName, `(${id})`);
return;
}
if (!c.email) {
console.warn("no email for", c.spokenName, `(${id})`);
return;
}
if (0 === Object.keys(p.ministers).length) {
console.warn("no ministers for", c.spokenName, `(${id})`);
// pass
}
if (0 === Object.keys(p.companions).length) {
console.warn("no companions for", c.spokenName, `(${id})`);
// pass
}
if (0 === Object.keys(p.assignments).length) {
console.warn("no assignment for", c.spokenName, `(${id})`);
// pass
}
return id;
})
.map(function (id) {
var msgs = ['']; // start with a new paragraph
var p = CJCD.getPerson(id);
var c = CJCD.getCachedCard(id);
var isMinister = false;
if (Object.keys(p.assignments).length) {
isMinister = true;
let msg = `Who you minister to:<br><ul>\n`;
Object.keys(p.assignments).forEach(function (m) {
var assignment = cards[m];
msg += formatCard(assignment);
});
msg += "</ul>";
msgs.push(msg);
}
if (Object.keys(p.companions).length > 0) {
isMinister = true;
let msg = "";
if (1 === Object.keys(p.companions).length > 1) {
msg += `Your companion:<br><ul>\n`;
} else {
msg += `Your companions:<br><ul>\n`;
}
Object.keys(p.companions).forEach(function (id) {
var companion = cards[id];
msg += formatCard(companion);
});
msg += "</ul>";
msgs.push(msg);
}
if (Object.keys(p.ministers).length > 0) {
let msg = `Your ministers:<br><ul>\n`;
Object.keys(p.ministers).forEach(function (id) {
var minister = cards[id];
msg += formatCard(minister);
});
msg += "</ul>";
msgs.push(msg);
}
if (!msgs.length) {
console.error("[SANITY] Impossible!", id, "does not exist!");
return;
}
// recipients: [ id ]
var lead;
if (
"MALE" !== c.gender ||
(0 === Object.keys(p.assignments).length &&
0 === Object.keys(p.companions).length)
) {
lead = helloMinisters(p, c);
} else {
lead = helloAssignments(p, c);
}
var body = lead + msgs.join("\n<p>") + "\n\n";
var data = {
lang: "eng",
allowReplyAll: false,
recipients: [parseInt(id, 10)],
subject: isMinister && ministerSubject || assigneeSubject,
messageBody: body,
type: "EQ",
};
console.log({
body: JSON.stringify(data),
});
return data;
});
}
var messages = createMessages();
The JSON is over 1MB, so to examine it quickly I used Matt's JSON-to-Go, and I cut out translations
, as that accounted for about 75% of the entire structure.
Again, the purpose is NOT to provide the Go representation of the JSON, but rather that was the simplest tool I know of to convert a huge JSON dataset into a small overview.
type AutoGenerated struct {
Props struct {
PageProps struct {
IsServer bool `json:"isServer"`
Store struct {
} `json:"store"`
InitialState struct {
APIStatus struct {
ShowGenericError bool `json:"showGenericError"`
IsPageError bool `json:"isPageError"`
IsFileNotFound bool `json:"isFileNotFound"`
IsCaPage bool `json:"isCaPage"`
} `json:"apiStatus"`
Lang string `json:"lang"`
Version string `json:"version"`
Proxy struct {
ProxyOptions []interface{} `json:"proxyOptions"`
ProxySearchResults []interface{} `json:"proxySearchResults"`
Loading bool `json:"loading"`
} `json:"proxy"`
UserContext struct {
Environment string `json:"environment"`
Version string `json:"version"`
ContextPath string `json:"contextPath"`
Unit struct {
UnitName string `json:"unitName"`
UnitNumber int `json:"unitNumber"`
Type string `json:"type"`
TranslatedType string `json:"translatedType"`
Children interface{} `json:"children"`
ParentUnitName string `json:"parentUnitName"`
ParentUnitNumber int `json:"parentUnitNumber"`
ParentType string `json:"parentType"`
GrandparentType string `json:"grandparentType"`
MissionName string `json:"missionName"`
MissionUnitNumber int `json:"missionUnitNumber"`
AreaName string `json:"areaName"`
AreaUnitNumber int `json:"areaUnitNumber"`
MsrOfficeName string `json:"msrOfficeName"`
MsrOfficeUnitNumber int `json:"msrOfficeUnitNumber"`
} `json:"unit"`
IsLcrClassCallingHtvtEnabled bool `json:"isLcrClassCallingHtvtEnabled"`
SubOrgs []struct {
UnitNumber int `json:"unitNumber"`
SubOrgID int `json:"subOrgId"`
OrgTypeIds []int `json:"orgTypeIds"`
DefaultOrgTypeIds []int `json:"defaultOrgTypeIds"`
Name string `json:"name"`
Children []interface{} `json:"children"`
UserCanEditCallings bool `json:"userCanEditCallings"`
IsClass bool `json:"isClass"`
IsRealClass bool `json:"isRealClass"`
IsSplit bool `json:"isSplit"`
ClassGroup interface{} `json:"classGroup"`
ParentName interface{} `json:"parentName"`
FirstTypeID int `json:"firstTypeId"`
Gender interface{} `json:"gender"`
IsCombined bool `json:"isCombined"`
} `json:"subOrgs"`
UnitOrgs []struct {
Children interface{} `json:"children"`
UnitOrgUUID string `json:"unitOrgUuid"`
UnitUUID string `json:"unitUuid"`
UnitNumber int `json:"unitNumber"`
UnitOrgName string `json:"unitOrgName"`
UnitOrgTypeIds []int `json:"unitOrgTypeIds"`
IsClass bool `json:"isClass"`
} `json:"unitOrgs"`
FullDateFormatJs string `json:"fullDateFormatJs"`
FullDateFormatOmitYearJs string `json:"fullDateFormatOmitYearJs"`
EmptyNameGroup struct {
FormattedLocal interface{} `json:"formattedLocal"`
FormattedLatin interface{} `json:"formattedLatin"`
Name1 struct {
Family interface{} `json:"family"`
Given interface{} `json:"given"`
Suffix interface{} `json:"suffix"`
TranslitSource bool `json:"translitSource"`
WritingSystem string `json:"writingSystem"`
Label string `json:"label"`
LabelKey string `json:"labelKey"`
} `json:"name1"`
Name2 interface{} `json:"name2"`
Name3 interface{} `json:"name3"`
AutoRomanize bool `json:"autoRomanize"`
} `json:"emptyNameGroup"`
LdsAccountUsername string `json:"ldsAccountUsername"`
LdsAccountPreferredName string `json:"ldsAccountPreferredName"`
IndividualID int64 `json:"individualId"`
Roles []string `json:"roles"`
ActivePosition int `json:"activePosition"`
ActivePositionUnitNumber int `json:"activePositionUnitNumber"`
ActivePositionEnglish string `json:"activePositionEnglish"`
Positions []struct {
PositionTypeID int `json:"positionTypeId"`
PositionName string `json:"positionName"`
UnitNumber int `json:"unitNumber"`
} `json:"positions"`
Proxied bool `json:"proxied"`
BetaEnv bool `json:"betaEnv"`
BetaTerms bool `json:"betaTerms"`
NonBetaURL string `json:"nonBetaUrl"`
BetaURL string `json:"betaUrl"`
AddressURL string `json:"addressUrl"`
CdolURL string `json:"cdolUrl"`
CdolUnitURL string `json:"cdolUnitUrl"`
LuHelpURL string `json:"luHelpUrl"`
ChangeBoundaryURL string `json:"changeBoundaryUrl"`
ChangeBoundaryStakeURL string `json:"changeBoundaryStakeUrl"`
ViewBoundaryProposalStatusURL string `json:"viewBoundaryProposalStatusUrl"`
StakeConferencesURL string `json:"stakeConferencesUrl"`
SurveyURL string `json:"surveyUrl"`
NewBishopURL string `json:"newBishopUrl"`
NewBishopStatusURL string `json:"newBishopStatusUrl"`
NewStakeCounselorURL string `json:"newStakeCounselorUrl"`
NewPatriarchURL string `json:"newPatriarchUrl"`
RecommendMissionPresidentURL string `json:"recommendMissionPresidentUrl"`
WelfareServicesURL string `json:"welfareServicesUrl"`
CqaBulkWait string `json:"cqaBulkWait"`
Teasers struct {
SmallTeasers []interface{} `json:"smallTeasers"`
MediumTeasers [][]struct {
ID string `json:"id"`
Feature interface{} `json:"feature"`
URL interface{} `json:"url"`
URLInNewWindow bool `json:"urlInNewWindow"`
ImageURL string `json:"imageUrl"`
Title string `json:"title"`
Description string `json:"description"`
} `json:"mediumTeasers"`
} `json:"teasers"`
Menus []struct {
Type string `json:"type"`
HiddenMenuSearchOnly bool `json:"hiddenMenuSearchOnly"`
Name string `json:"name"`
LiClass interface{} `json:"liClass"`
UlClass interface{} `json:"ulClass"`
SubMenuID interface{} `json:"subMenuId"`
AllowEmptyMenu bool `json:"allowEmptyMenu"`
Sorted bool `json:"sorted"`
IncludeRoles [][]string `json:"includeRoles"`
ExcludeRoles interface{} `json:"excludeRoles"`
Item struct {
HiddenMenuSearchOnly bool `json:"hiddenMenuSearchOnly"`
Name interface{} `json:"name"`
MenuSearchName interface{} `json:"menuSearchName"`
Title interface{} `json:"title"`
ParentTitle interface{} `json:"parentTitle"`
URL string `json:"url"`
URLInNewWindow bool `json:"urlInNewWindow"`
UISref interface{} `json:"uiSref"`
UISrefOpts interface{} `json:"uiSrefOpts"`
LiClass interface{} `json:"liClass"`
IncludeRoles interface{} `json:"includeRoles"`
ExcludeRoles interface{} `json:"excludeRoles"`
React bool `json:"react"`
SubOrgID interface{} `json:"subOrgId"`
} `json:"item"`
Items interface{} `json:"items"`
Columns interface{} `json:"columns"`
ParentSubOrgType interface{} `json:"parentSubOrgType"`
ParentSubOrgID interface{} `json:"parentSubOrgId"`
} `json:"menus"`
ReactDomain interface{} `json:"reactDomain"`
AppUpdatedFormattedDate string `json:"appUpdatedFormattedDate"`
} `json:"userContext"`
UberSearch struct {
MemberResults []interface{} `json:"memberResults"`
PageResults []interface{} `json:"pageResults"`
Loading bool `json:"loading"`
} `json:"uberSearch"`
MemberList []interface{} `json:"memberList"`
MinisteringData struct {
Loading bool `json:"loading"`
Elders []struct {
Companionships []struct {
ID string `json:"id"`
Ministers []struct {
PersonUUID string `json:"personUuid"`
LegacyCmisID int64 `json:"legacyCmisId"`
Name string `json:"name"`
NameOrder int `json:"nameOrder"`
Email string `json:"email"`
Interviews []struct {
ID string `json:"id"`
Date string `json:"date"`
Timestamp string `json:"timestamp"`
} `json:"interviews"`
YouthBasedOnAge bool `json:"youthBasedOnAge"`
AssignType string `json:"assignType,omitempty"`
UnitOrgID string `json:"unitOrgId,omitempty"`
} `json:"ministers"`
Assignments []struct {
PersonUUID string `json:"personUuid"`
LegacyCmisID int `json:"legacyCmisId"`
Name string `json:"name"`
NameOrder int `json:"nameOrder"`
YouthBasedOnAge bool `json:"youthBasedOnAge"`
} `json:"assignments"`
RecentlyChangedDate string `json:"recentlyChangedDate"`
RecentlyChangedDateInMilliseconds int64 `json:"recentlyChangedDateInMilliseconds"`
} `json:"companionships"`
DistrictName string `json:"districtName"`
DistrictUUID string `json:"districtUuid"`
SupervisorName string `json:"supervisorName"`
SupervisorLegacyCmisID int64 `json:"supervisorLegacyCmisId"`
SupervisorPersonUUID string `json:"supervisorPersonUuid"`
} `json:"elders"`
EldersQuorumSupervisors []struct {
PersonUUID string `json:"personUuid"`
LegacyCmisID int64 `json:"legacyCmisId"`
Name string `json:"name"`
NameOrder int `json:"nameOrder"`
YouthBasedOnAge bool `json:"youthBasedOnAge"`
} `json:"eldersQuorumSupervisors"`
InterviewViewAccess bool `json:"interviewViewAccess"`
Error bool `json:"error"`
ReliefSociety interface{} `json:"reliefSociety"`
ReliefSocietySupervisors interface{} `json:"reliefSocietySupervisors"`
CurrentReliefSociety interface{} `json:"currentReliefSociety"`
EligibleMinistersAndAssignments struct {
EligibleMinisters []struct {
PersonUUID string `json:"personUuid"`
LegacyCmisID int `json:"legacyCmisId"`
Name string `json:"name"`
NameOrder int `json:"nameOrder"`
PriesthoodOffice string `json:"priesthoodOffice"`
Age int `json:"age"`
UnitOrgs []string `json:"unitOrgs"`
Email interface{} `json:"email"`
NotGenerallyAssigned bool `json:"notGenerallyAssigned"`
Spouse struct {
EligibleMinister bool `json:"eligibleMinister"`
NameFormats struct {
ListPreferredLocal string `json:"listPreferredLocal"`
GivenPreferredLocal string `json:"givenPreferredLocal"`
FamilyPreferredLocal string `json:"familyPreferredLocal"`
ListPreferred interface{} `json:"listPreferred"`
ListOfficial interface{} `json:"listOfficial"`
SpokenPreferredLocal interface{} `json:"spokenPreferredLocal"`
} `json:"nameFormats"`
UUID string `json:"uuid"`
NameOrder int `json:"nameOrder"`
Age int `json:"age"`
Emails []struct {
Email string `json:"email"`
OwnerType interface{} `json:"ownerType"`
UseType interface{} `json:"useType"`
} `json:"emails"`
Phones []struct {
Number string `json:"number"`
OwnerType interface{} `json:"ownerType"`
UseType interface{} `json:"useType"`
} `json:"phones"`
PhoneNumber string `json:"phoneNumber"`
PriesthoodOffice string `json:"priesthoodOffice"`
MembershipUnit interface{} `json:"membershipUnit"`
LegacyCmisID int `json:"legacyCmisId"`
Sex string `json:"sex"`
UnitOrgsCombined []string `json:"unitOrgsCombined"`
Positions interface{} `json:"positions"`
HouseholdMember struct {
HouseholdRole string `json:"householdRole"`
Household struct {
AnchorPerson struct {
LegacyCmisID int `json:"legacyCmisId"`
UUID string `json:"uuid"`
} `json:"anchorPerson"`
UUID string `json:"uuid"`
FamilyNameLocal string `json:"familyNameLocal"`
DirectoryPreferredLocal string `json:"directoryPreferredLocal"`
Address struct {
FormattedLine1 string `json:"formattedLine1"`
FormattedLine2 string `json:"formattedLine2"`
FormattedLine3 string `json:"formattedLine3"`
FormattedLine4 interface{} `json:"formattedLine4"`
Formatted1 interface{} `json:"formatted1"`
Formatted2 interface{} `json:"formatted2"`
Formatted3 interface{} `json:"formatted3"`
Formatted4 interface{} `json:"formatted4"`
AddressLines []string `json:"addressLines"`
FormattedAll []interface{} `json:"formattedAll"`
} `json:"address"`
Emails []struct {
Email string `json:"email"`
OwnerType interface{} `json:"ownerType"`
UseType interface{} `json:"useType"`
} `json:"emails"`
Phones []struct {
Number string `json:"number"`
OwnerType interface{} `json:"ownerType"`
UseType interface{} `json:"useType"`
} `json:"phones"`
Unit struct {
ParentUnit interface{} `json:"parentUnit"`
UUID interface{} `json:"uuid"`
UnitNumber int `json:"unitNumber"`
NameLocal string `json:"nameLocal"`
UnitType interface{} `json:"unitType"`
Children interface{} `json:"children"`
Positions interface{} `json:"positions"`
CdolLink interface{} `json:"cdolLink"`
AdminUnit interface{} `json:"adminUnit"`
AddressUnknown interface{} `json:"addressUnknown"`
} `json:"unit"`
} `json:"household"`
MembershipUnitFlag bool `json:"membershipUnitFlag"`
} `json:"householdMember"`
Member bool `json:"member"`
PriesthoodTeacherOrAbove bool `json:"priesthoodTeacherOrAbove"`
HouseholdEmail string `json:"householdEmail"`
HouseholdAnchorPersonUUID string `json:"householdAnchorPersonUuid"`
HouseholdNameFamilyLocal string `json:"householdNameFamilyLocal"`
HouseholdRole string `json:"householdRole"`
Convert bool `json:"convert"`
Email string `json:"email"`
UnitName string `json:"unitName"`
YouthBasedOnAge bool `json:"youthBasedOnAge"`
IsSpouse bool `json:"isSpouse"`
HouseholdUUID string `json:"householdUuid"`
IsProspectiveElder bool `json:"isProspectiveElder"`
IsSingleAdult bool `json:"isSingleAdult"`
IsYoungSingleAdult bool `json:"isYoungSingleAdult"`
HouseholdPhoneNumber string `json:"householdPhoneNumber"`
IsHead bool `json:"isHead"`
PersonUUID string `json:"personUuid"`
NameListPreferredLocal string `json:"nameListPreferredLocal"`
HouseholdNameDirectoryLocal string `json:"householdNameDirectoryLocal"`
FormattedAddress string `json:"formattedAddress"`
UnitNumber int `json:"unitNumber"`
NameGivenPreferredLocal string `json:"nameGivenPreferredLocal"`
IsMember bool `json:"isMember"`
HouseHoldMemberNameForList string `json:"houseHoldMemberNameForList"`
IsOutOfUnitMember bool `json:"isOutOfUnitMember"`
IsAdult bool `json:"isAdult"`
NameFamilyPreferredLocal string `json:"nameFamilyPreferredLocal"`
OutOfUnitMember bool `json:"outOfUnitMember"`
Address struct {
FormattedLine1 string `json:"formattedLine1"`
FormattedLine2 string `json:"formattedLine2"`
FormattedLine3 string `json:"formattedLine3"`
FormattedLine4 interface{} `json:"formattedLine4"`
Formatted1 interface{} `json:"formatted1"`
Formatted2 interface{} `json:"formatted2"`
Formatted3 interface{} `json:"formatted3"`
Formatted4 interface{} `json:"formatted4"`
AddressLines []string `json:"addressLines"`
FormattedAll []interface{} `json:"formattedAll"`
} `json:"address"`
Birth struct {
Date struct {
Date string `json:"date"`
Calc string `json:"calc"`
Display string `json:"display"`
} `json:"date"`
MonthDay struct {
Date string `json:"date"`
Calc string `json:"calc"`
Display string `json:"display"`
} `json:"monthDay"`
Place interface{} `json:"place"`
Country interface{} `json:"country"`
} `json:"birth"`
PersonStatusFlags struct {
Member bool `json:"member"`
Convert bool `json:"convert"`
Adult bool `json:"adult"`
SingleAdult bool `json:"singleAdult"`
YoungSingleAdult bool `json:"youngSingleAdult"`
ProspectiveElder bool `json:"prospectiveElder"`
Deceased bool `json:"deceased"`
} `json:"personStatusFlags"`
Name string `json:"name"`
} `json:"spouse,omitempty"`
} `json:"eligibleMinisters"`
EligibleAssignments []struct {
PersonUUID string `json:"personUuid"`
LegacyCmisID int `json:"legacyCmisId"`
Name string `json:"name"`
NameOrder int `json:"nameOrder"`
Email interface{} `json:"email"`
} `json:"eligibleAssignments"`
} `json:"eligibleMinistersAndAssignments"`
} `json:"ministeringData"`
MinisteringStats struct {
} `json:"ministeringStats"`
MinisteringAssignmentsReport struct {
} `json:"ministeringAssignmentsReport"`
MemberCard struct {
Loading bool `json:"loading"`
Error interface{} `json:"error"`
MemberCardList struct {
} `json:"memberCardList"`
} `json:"memberCard"`
HouseholdCard struct {
Loading bool `json:"loading"`
Error interface{} `json:"error"`
HouseholdCardList struct {
} `json:"householdCardList"`
} `json:"householdCard"`
MinisteringFilter struct {
SearchText string `json:"searchText"`
SelectedDistricts struct {
All bool `json:"all"`
} `json:"selectedDistricts"`
SelectedOptionFilters struct {
} `json:"selectedOptionFilters"`
SelectedOrg string `json:"selectedOrg"`
SelectedQuarter string `json:"selectedQuarter"`
UnassignedSearchText string `json:"unassignedSearchText"`
ShowNotGenerallyAssigned bool `json:"showNotGenerallyAssigned"`
} `json:"ministeringFilter"`
UnitPicker struct {
SelectedUnit struct {
} `json:"selectedUnit"`
SearchValue interface{} `json:"searchValue"`
} `json:"unitPicker"`
MinisteringAssignmentsFilter struct {
HouseholdsSearchText string `json:"householdsSearchText"`
HouseholdsAssignedValue string `json:"householdsAssignedValue"`
HouseholdsOrganizationsValue string `json:"householdsOrganizationsValue"`
IndividualsSearchText string `json:"individualsSearchText"`
IndividualsOrgFilter string `json:"individualsOrgFilter"`
IndividualsMissingFilter string `json:"individualsMissingFilter"`
} `json:"ministeringAssignmentsFilter"`
MinisteringPhotos struct {
} `json:"ministeringPhotos"`
NewMemberReport struct {
Data []interface{} `json:"data"`
SearchText string `json:"searchText"`
Term string `json:"term"`
ShowAge bool `json:"showAge"`
IsLoading bool `json:"isLoading"`
} `json:"newMemberReport"`
BirthdayList struct {
Data []interface{} `json:"data"`
MonthsShown string `json:"monthsShown"`
UnitOrg string `json:"unitOrg"`
Month int `json:"month"`
ShowAge bool `json:"showAge"`
IsLoading bool `json:"isLoading"`
} `json:"birthdayList"`
SacramentAttendanceReport struct {
Data []interface{} `json:"data"`
Years []interface{} `json:"years"`
Year interface{} `json:"year"`
EditMonth interface{} `json:"editMonth"`
IsSaving bool `json:"isSaving"`
IsLoading bool `json:"isLoading"`
IsInitialLoad bool `json:"isInitialLoad"`
TempWeeks []interface{} `json:"tempWeeks"`
} `json:"sacramentAttendanceReport"`
} `json:"initialState"`
InitialProps struct {
Type string `json:"type"`
UnitNumber int `json:"unitNumber"`
Tab string `json:"tab"`
} `json:"initialProps"`
} `json:"pageProps"`
} `json:"props"`
Page string `json:"page"`
Query struct {
Lang string `json:"lang"`
Tab string `json:"tab"`
Type string `json:"type"`
} `json:"query"`
BuildID string `json:"buildId"`
IsFallback bool `json:"isFallback"`
CustomServer bool `json:"customServer"`
Gip bool `json:"gip"`
Head [][]interface{} `json:"head"`
}