Skip to content

Commit 4a11edc

Browse files
committed
Update to newer Wild Apricot API
1 parent d0f8b28 commit 4a11edc

13 files changed

+23664
-10362
lines changed
Lines changed: 293 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,308 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics.Metrics;
4+
using System.Reflection.Emit;
25
using System.Threading;
36
using System.Threading.Tasks;
7+
using System.Xml.Linq;
48
using CronScheduler.Extensions.Scheduler;
9+
using Mms.Database;
10+
using PetaPoco;
11+
using static SQLite.SQLite3;
12+
using WildApricot;
13+
using static System.Runtime.InteropServices.JavaScript.JSType;
14+
using System.Security.Policy;
15+
using Serilog;
516

617
namespace Mms.Api.Jobs
718
{
819
public class PullMembersFromWildApricot : IScheduledJob
920
{
1021
public string Name { get; } = nameof(PullMembersFromWildApricot);
1122

12-
public Task ExecuteAsync(CancellationToken cancellationToken)
23+
public async Task ExecuteAsync(CancellationToken cancellationToken)
1324
{
14-
throw new NotImplementedException();
25+
Log.Information("Beginning Wild Apricot Sync");
26+
27+
var wildApricot = new WildApricotClient();
28+
29+
var contactsCount = await wildApricot.GetContactsListAsync(
30+
accountId: wildApricot.accountId,
31+
//count: true,
32+
filter: "Archived eq false"
33+
);
34+
35+
ContactsResponse contacts = null;
36+
37+
var attempts = 0;
38+
39+
while (attempts < 10 && string.IsNullOrWhiteSpace(contacts?.Processed) && contacts?.ProcessingState != ContactsAsyncResponseProcessingState.Complete) {
40+
await Task.Delay(5000);
41+
42+
contacts = await wildApricot.GetContactsListAsync(
43+
accountId: wildApricot.accountId,
44+
resultId: contactsCount.ResultId
45+
);
46+
47+
attempts += 1;
48+
}
49+
50+
if ((string.IsNullOrWhiteSpace(contacts?.Processed) && contacts?.ProcessingState != ContactsAsyncResponseProcessingState.Complete) || contacts?.Contacts?.Count < 101)
51+
throw new Exception("Wild Apricot did not return complete members list!");
52+
53+
using var accessDb = new AccessControlDatabase();
54+
using var fundingDb = new AreaFundingDatabase();
55+
56+
Log.Information($"Contacts Returned: {contacts.Contacts.Count}");
57+
58+
var totalFunding = new fund();
59+
60+
foreach (var contact in contacts.Contacts) {
61+
// For Keys
62+
string key1 = null;
63+
string key2 = null;
64+
string type = null;
65+
DateTime? joined = null;
66+
DateTime? renewal = null;
67+
var id = contact.Id ?? -1;
68+
var key3 = $"{id}#";
69+
var fullname = $"{contact.FirstName} {contact.LastName}";
70+
var apricot_admin = contact.IsAccountAdministrator ?? false;
71+
72+
// For Funding
73+
var active = false;
74+
var specialPurpose = false;
75+
var amount = 0m;
76+
77+
// If a member is currently pending renewal (during last 10 days of month, for instance), we count them as active
78+
switch (contact.Status ?? ContactStatus.Lapsed) {
79+
case ContactStatus.Active:
80+
case ContactStatus.PendingRenewal:
81+
active = true;
82+
break;
83+
}
84+
85+
// If an account is active, and set to the type "Special Purpose Account"
86+
// we do not count it towards the population or area funding.
87+
// We do put a key in the access control system for it.
88+
// Expiration date is overridden below.
89+
if (contact.MembershipLevel?.Name == "Special Purpose Account") {
90+
active = false;
91+
specialPurpose = true;
92+
}
93+
94+
foreach (var field in contact.FieldValues) {
95+
switch (field.FieldName) {
96+
case "Key Fob Code":
97+
key1 = field.Value?.ToString().ToUpperInvariant().Trim();
98+
break;
99+
case "PIN Code":
100+
key2 = field.Value?.ToString().ToUpperInvariant().Trim();
101+
break;
102+
case "Member since":
103+
joined = DateTime.Parse(field.Value?.ToString() ?? "2000-01-01");
104+
break;
105+
case "Renewal due":
106+
renewal = DateTime.Parse(field.Value?.ToString() ?? "2000-01-01");
107+
break;
108+
case "Member role":
109+
switch (field.Value?.ToString() ?? "") {
110+
case "Bundle Administrator":
111+
case "Bundle administrator":
112+
case "Individual":
113+
case "":
114+
type = "General";
115+
116+
if (active) {
117+
totalFunding.general += 1;
118+
amount = 1.5m;
119+
}
120+
break;
121+
case "Bundle Member":
122+
case "Bundle member":
123+
type = "Family";
124+
125+
if (active) {
126+
totalFunding.family += 1;
127+
amount = 0.38m;
128+
}
129+
break;
130+
}
131+
break;
132+
case "Suspended member":
133+
case "Archived":
134+
// If the BOD suspends someone before they expire, we immediately disable their key
135+
if (field.Value?.ToString() == "1") {
136+
active = false;
137+
var sql = @"
138+
UPDATE
139+
member
140+
SET
141+
expires = '2000-01-01'
142+
WHERE
143+
member_id = @0;";
144+
145+
accessDb.Execute(sql, id);
146+
}
147+
break;
148+
case "$1.50/Month Area #1":
149+
case "$1.50/Month Area #2":
150+
case "$1.50/Month Area #3":
151+
case "$1.50/Month Area #4":
152+
case "$1.50/Month Area #5":
153+
if (active) {
154+
if (field.Value == null)
155+
Log.Error($"Null Area Selection! - {fullname} - {field.FieldName} - {field.Value}");
156+
else {
157+
TabulateFunds(totalFunding, field.FieldName, amount);
158+
totalFunding.total += amount;
159+
}
160+
}
161+
break;
162+
}
163+
}
164+
165+
if (active)
166+
totalFunding.members += 1;
167+
168+
if (specialPurpose) {
169+
// Always set special purpose accounts to expire one month into the future.
170+
renewal = (new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day)).AddMonths(1);
171+
}
172+
173+
// Record keys into access database
174+
if (active || specialPurpose) {
175+
if (!string.IsNullOrEmpty(key1)) {
176+
var sql2 = @$"
177+
REPLACE INTO
178+
keycode(keycode_id, member_id, updated)
179+
VALUES
180+
(@0, @1, NOW()); ";
181+
182+
accessDb.Execute(sql2, key1, id);
183+
}
184+
185+
if (!string.IsNullOrEmpty(key2)) {
186+
var sql3 = @$"
187+
REPLACE INTO
188+
keycode(keycode_id, member_id, updated)
189+
VALUES
190+
(@0, @1, NOW()); ";
191+
192+
accessDb.Execute(sql3, key2, id);
193+
}
194+
195+
var sql4 = @$"
196+
REPLACE INTO
197+
keycode(keycode_id, member_id, updated)
198+
VALUES
199+
(@0, @1, NOW()); ";
200+
201+
accessDb.Execute(sql4, key3, id);
202+
203+
var sql5 = @"
204+
REPLACE INTO
205+
member(member_id, name, type, apricot_admin, joined, expires, updated)
206+
VALUES
207+
(@0, @1, @2, @3, @4, @5, NOW()); ";
208+
209+
accessDb.Execute(sql5, id, fullname, type, apricot_admin, joined, renewal);
210+
}
211+
}
212+
213+
totalFunding.month = DateTime.Now;
214+
215+
fundingDb.Insert(totalFunding);
216+
}
217+
218+
private void TabulateFunds(fund total, string area, decimal amount)
219+
{
220+
switch (area) {
221+
case "* Not Allocated *":
222+
total.building_purchase += amount;
223+
break;
224+
case "3D Printers":
225+
total.threed_printer += amount;
226+
break;
227+
case "Bicycle Repair":
228+
total.bicycle_repair += amount;
229+
break;
230+
case "Blacksmith and Forge":
231+
total.forge += amount;
232+
break;
233+
case "Casting":
234+
total.casting += amount;
235+
break;
236+
case "Ceramic Studio":
237+
total.ceramic += amount;
238+
break;
239+
case "CNC Room":
240+
total.cnc += amount;
241+
break;
242+
case "Cosplay":
243+
total.cosplay += amount;
244+
break;
245+
case "Craft Lab":
246+
total.craft += amount;
247+
break;
248+
case "Dalek Asylum":
249+
total.dalek += amount;
250+
break;
251+
case "Electronic Lab":
252+
total.electronic += amount;
253+
break;
254+
case "Fine Art Printing":
255+
total.print += amount;
256+
break;
257+
case "Finishing":
258+
total.finishing += amount;
259+
break;
260+
case "Ham Radio":
261+
total.ham_radio += amount;
262+
break;
263+
case "Jewelry and Non Ferrous Metals":
264+
total.jewelry += amount;
265+
break;
266+
case "Lampworking":
267+
total.lampworking += amount;
268+
break;
269+
case "Laser Cutters":
270+
total.laser += amount;
271+
break;
272+
case "Leather Working":
273+
total.leather += amount;
274+
break;
275+
case "Long Arm Quilting":
276+
total.long_arm += amount;
277+
break;
278+
case "Community Outreach":
279+
total.makerfaire += amount;
280+
break;
281+
case "Metal Shop":
282+
total.metal += amount;
283+
break;
284+
case "Neon":
285+
total.neon += amount;
286+
break;
287+
case "Paint Room":
288+
total.paint += amount;
289+
break;
290+
case "Stained Glass":
291+
total.stained_glass += amount;
292+
break;
293+
case "Tiger Lily Sculpture Gang":
294+
total.tiger_lily += amount;
295+
break;
296+
case "Vacuum Former":
297+
total.vacuum += amount;
298+
break;
299+
case "Welders":
300+
total.welding += amount;
301+
break;
302+
case "Wood Shop":
303+
total.wood += amount;
304+
break;
305+
}
15306
}
16307
}
17308
}

MilwaukeeMakerspaceApi/MilwaukeeMakerspaceApi.csproj

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
<Project Sdk="Microsoft.NET.Sdk.Web">
22
<PropertyGroup>
33
<OutputType>Exe</OutputType>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
66
<WarningsAsErrors />
77
<WarningLevel>4</WarningLevel>
88
<RootNamespace>Mms.Api</RootNamespace>
99
<NoWarn>1701;1702;NU1605</NoWarn>
1010
</PropertyGroup>
1111
<ItemGroup>
12-
<PackageReference Include="BlazorDateRangePicker" Version="5.2.0" />
13-
<PackageReference Include="CronScheduler.AspNetCore" Version="3.1.0" />
14-
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.1" />
12+
<PackageReference Include="BlazorDateRangePicker" Version="5.4.0" />
13+
<PackageReference Include="CronScheduler.AspNetCore" Version="3.2.0" />
14+
<PackageReference Include="IppDotNetSdkForQuickBooksApiV3" Version="14.7.0" />
15+
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
1516
<PackageReference Include="Rssdp" Version="4.0.4" />
16-
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
17-
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
18-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
17+
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
18+
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
19+
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
1920
</ItemGroup>
2021
<ItemGroup>
2122
<ProjectReference Include="..\Mms.Database\Mms.Database.csproj" />

0 commit comments

Comments
 (0)