# Configuration Structure
### Team Configuration:
```
{
Id: "Guid",
ConfigurationOrigin: "String",
TeamMemberTypes: "ICollection<TeamMemberType>",
CustomFields: "ICollection<CustomField>",
LogCategories: "ICollection<LogCategory>",
Abilities: "ICollection<Ability>",
FormMemberships: "ICollection<FormMembership>",
AppointmentTypes: "ICollection<AppointmentType",
}
```
### Team Member Type:
```
{
Id: "Guid",
ConfigurationOrigin: "String",
Name: "String",
Description: "String",
LabelSingular: "String",
LabelPlural: "String",
LogConfigId: "Guid FK=LogConfigs.Id",
AppointmentsConfigId: "Guid FK=AppointmentsConfig.Id"
}
```
### Log Config:
```
{
Enabled: "Bool",
ViewAbilityId: "Guid FK=Abilities.Id",
ARAdd: "Guid FK=Abilities.Id",
AREditAny: "Guid FK=Abilities.Id",
AREditOwn: "Guid FK=Abilities.Id"
},
```
### Appointments Config:
```
{
Enabled: "Bool"
},
```
### Custom Field:
```
{
Id: "Guid",
TeamMemberTypeId: "Guid FK=TeamMemberTypes.Id",
ChartType: "String, one of: TeamMember, TeamMemberTeamEnrollment, or Service"
ConfigurationOrigin: "String",
Label: "String",
DataType: "String", //One of: Int (aka 4), Bool, String, DateTime, Date, Double (aka 4.5), *future: something nested?*
FieldType: "String", //One of: Text, DatePicker, DateTimePicker, Options, Checkboxes, Switch, Select
DataLevel: "String", //Nominal, Ordinal, Interval, Ratio
ChapterLocation: "String",
SectionLocation: "String",
OrderWeight: "Int",
Required: "String",
ARView: "Guid? FK=Abilities.Id",
AREdit: "Guid? FK=Abilities.Id"
}
```
### Log Category:
```
{
Id: "Guid",
TeamMemberTypeId: "Guid FK=TeamMemberType.Id",
ConfigurationOrigin: "String",
GroupingLabel: "String",
CategoryLabel: "String",
}
```
### Ability:
```
{
Id: "Guid",
TeamMemberTypeId: "Guid FK=TeamMemberType.Id",
ConfigurationOrigin: "String",
AbilitySource: "String: one of: System, User"
Name: "String"
}
```
### Security Role:
```
{
Id: "Guid",
TeamMemberTypeId: "Guid FK=TeamMemberType.Id",
ConfigurationOrigin: "String",
Name: "String",
Abilities: "Array of AbilityId"
}
```
### Forms Membership:
```
{
Id: "Guid",
TeamMemberTypeId: "Guid FK=TeamMemberType.Id",
ConfigurationOrigin: "String",
FormId: "String (from DMS)",
StartDate: "Date",
EndDate: "Date",
CreateDocumentAbilityId: "Guid FK=Abilities.Id",
EditAnyDocumentAbilityId: "Guid FK=Abilities.Id",
EditOwnDocumentAbilityId: "Guid FK=Abilities.Id",
ViewAnyDocumentAbilityId: "Guid FK=Abilities.Id",
ViewOwnDocumentAbilityId: "Guid FK=Abilities.Id",
}
```
### Appointment Type:
```
{
Id: "Guid",
TeamMemberTypeId: "Guid FK=TeamMemberType.Id",
ConfigurationOrigin: "String",
StartDate: "Date",
EndDate: "Date",
Label: "String",
OccurredFormMembershipId: "Guid FK=FormMemberships.Id",
CancelledFormMembershipId: "Guid FK=FormMemberships.Id",
ARCreateAppointment: "Guid FK=Abilities.Id",
AREditAnyAppointment: "Guid FK=Abilities.Id",
AREditOwnAppointment: "Guid FK=Abilities.Id",
ARViewAnyAppointment: "Guid FK=Abilities.Id",
ARViewOwnAppointment: "Guid FK=Abilities.Id",
}
```
### Custom Field Value:
```
{
Id: "Guid",
TeamMemberId: "Guid?",
TeamId: "Guid?",
ServiceId: "Guid?",
CustomFieldId: "Guid",
CustomField: {
Id: "Guid",
TeamMemberTypeId: "Guid FK=TeamMemberType.Id",
ConfigurationOrigin: "String",
Label: "String",
DataType: "String", //One of: Int (aka 4), Bool, String, DateTime, Date, Double (aka 4.5), *future: something nested?*
FieldType: "String", //One of: Text, DatePicker, DateTimePicker, Options, Checkboxes, Switch, Select
DataLevel: "String", //Nominal, Ordinal, Interval, Ratio
ChapterLocation: "String",
SectionLocation: "String",
OrderWeight: "Int",
Required: "String",
ViewAbilityId: "Guid FK=Abilities.Id",
EditAbilityId: "Guid FK=Abilities.Id"
}
FieldValue: "String" //always stored in a standard parsable way as a string for example for a date: yyyy-MM-dd Monday
}
```
### Team Member:
```
{
Id: "Guid",
TeamMemberTypeId: "Guid",
...
CustomFieldValues: "ICollection<CustomFieldValue>"
}
```
### Reporting Example:
#### Filtering:
```
//If we want to apply the following filters:
// DOB > 2015-01-01 (custom field id = "123")
// FirstName != "TEST" (custom field id = "555")
IQueryable<TeamMember> teamMembersFilteredQuery = dbContext.TeamMembers
.Where(tm => tm.Id == {teamId});
//Filter by Team Members who have a DOB After 2015-01-01
teamMembersFilteredQuery = TeamMembersFilteredQuery
.Where(tm => tm.CustomFieldValues
.Where(cfv =>
cfv.CustomFieldId == "123" &&
cfv.FieldValue > '2015-01-01')
.Count() > 0);
//Filter by Team Members who have First Name != 'Test'
teamMembersFilteredQuery = TeamMembersFilteredQuery
.Where(tm => tm.CustomFieldValues
.Where(cfv =>
cfv.CustomFieldId == "555" &&
cfv.FieldValue != 'TEST')
.Count() > 0);
//Get the Data:
TeamMember[] teamMembers = teamMembersFilterQuery
.Include(tm => tm.CustomFieldValues).ThenInclude(cfv => cfv.CustomField)
.ToArray();
CustomFieldValue[] customFieldValuesFlat = teamMembers
.SelectMany(tm => tm.CustomFieldValues)
.ToArray();
```
#### Aggregating:
```
//Split By (AKA Group By) by Last Name (CFId = 333) and First Name (CFId = 555) with the following columns:
//Height (Mean) (CFId = 999) (FieldType = int AKA height in inches)
//DOB (Min) (CFId = 123)
string[] uniqueLastNames = customFieldValuesFlat
.Where(cfv => cfv.CustomFieldId == 333)
.Select(cfv => cfv.FieldValue)
.Distinct();
string[] uniqueFirstNames = customFieldValuesFlat
.Where(cfv => cfv.CustomFieldId == 555)
.Select(cfv => cfv.FieldValue)
.Distinct();
// uniqueLastNames should look like this: ['Smith',Stickney','Hranek', 'Niu', 'Vergilio']
foreach(string lastName in uniqueLastNames)
{
foreach(string firstName in uniqueFirstNames)
{
var meanHeight = teamMembers
.Where(tm =>
tm.CustomFieldValues
.Where(cfv =>
cfv.CustomFieldId == 333 &&
cfv.FieldValue == lastName)
.Count() > 0 &&
tm.CustomFieldValues
.Where(cfv =>
cfv.CustomFieldId == 333 &&
cfv.FieldValue == firstName)
.Count() > 0)
.SelectMany(tm => tm.CustomFieldValues)
.Where(cfv => cfv.CustomFieldId == 999)
.Average(cfv => int.Parse(cfv.FieldValue));
}
}
//or like this for dynamic:
IQueryable<TeamMember> teamMemberQueryForAggregate = teamMembers
.Where(tm => tm.Id != null);
foreach(string lastName in uniqueLastNames)
{
teamMemberQueryForAggregate = teamMemberQueryForAggregate
.Where(tm =>
tm.CustomFieldValues
.Where(cfv =>
cfv.CustomFieldId == 333 &&
cfv.FieldValue == lastName)
.Count() > 0);
foreach(string firstName in uniqueFirstNames)
{
teamMemberQueryForAggregate = teamMemberQueryForAggregate
.Where(tm =>
tm.CustomFieldValues
.Where(cfv =>
cfv.CustomFieldId == 999 &&
cfv.FieldValue == firstName)
.Count() > 0);
var meanHeight = teamMemberQueryForAggregate
.SelectMany(tm => tm.CustomFieldValues)
.Where(cfv => cfv.CustomFieldId == 999)
.Average(cfv => int.Parse(cfv.FieldValue));
}
}
```