source: Scheduling/trunk/cs/bsdx0200GUISourceCode/CGSchedLib.cs@ 1095

Last change on this file since 1095 was 1095, checked in by Sam Habiel, 13 years ago

Remove CGSchedLib.OutputArray everywhere. Used to be needed in VS 2003; not anymore as you can see tables now.
CalendarGrid:

  • Make MinSince80 and TimesOverlap static functions for use by other classes since they are done over and over again.

CGAppointments:

  • Documentation updates

CGAVDocument:

CGDocument:

CGDocumentManager:

CGSchedLib: Lots of Changes

CGView:

  • OnUpdateScheduleCallback now has try catch when it this.Invokes the original form so that it won't crash if the form has been closed before it gets called.
File size: 33.9 KB
Line 
1using System;
2using System.Data;
3//using System.Data.OleDb;
4using System.Collections;
5using System.Diagnostics;
6using System.Drawing;
7using IndianHealthService.BMXNet;
8
9namespace IndianHealthService.ClinicalScheduling
10{
11 public enum ScheduleType
12 {
13 Resource,
14 Clinic
15 }
16
17 /// <summary>
18 /// CGSchedLib contains static functions that are called from throughout the
19 /// scheduling application.
20 /// </summary>
21 public static class CGSchedLib
22 {
23 /// <summary>
24 /// Gets appointments from VISTA to display in Grid
25 /// </summary>
26 /// <param name="docManager"></param>
27 /// <param name="saryResNames"></param>
28 /// <param name="StartTime"></param>
29 /// <param name="EndTime"></param>
30 /// <returns></returns>
31 public static DataTable CreateAppointmentSchedule(CGDocumentManager docManager, ArrayList saryResNames, DateTime StartTime, DateTime EndTime)
32 {
33 string sResName = "";
34 for (int i = 0; i < saryResNames.Count; i++)
35 {
36 sResName += saryResNames[i];
37 if ((i+1) < saryResNames.Count)
38 sResName += "|";
39 }
40
41
42 string sStart = FMDateTime.Create(StartTime).DateOnly.FMDateString;
43 string sEnd = FMDateTime.Create(EndTime).FMDateString;
44 string sSql = "BSDX CREATE APPT SCHEDULE^" + sResName + "^" + sStart + "^" + sEnd ;
45 DataTable dtRet = docManager.RPMSDataTable(sSql, "AppointmentSchedule");
46 return dtRet;
47
48 }
49
50
51 /// <summary>
52 /// Gets all Availabilities and Appointments, then substracts Appointments from availabilities.
53 /// </summary>
54 /// <param name="docManager">God Class</param>
55 /// <param name="saryResourceNames">Resource Array (ArrayList)</param>
56 /// <param name="StartTime">Self-Explanatory</param>
57 /// <param name="EndTime">Self-Explanatory</param>
58 /// <param name="saryApptTypes">Array of Access Type IDs</param>
59 /// <param name="stType"></param>
60 /// <param name="sSearchInfo"></param>
61 /// <returns></returns>
62 public static DataTable CreateAvailabilitySchedule(CGDocumentManager docManager,
63 ArrayList saryResourceNames, DateTime StartTime, DateTime EndTime,
64 ArrayList saryApptTypes,/**/ ScheduleType stType, string sSearchInfo)
65 {
66 DataTable rsOut;
67 rsOut = new DataTable("AvailabilitySchedule");
68
69 DataTable rsSlotSchedule;
70 DataTable rsApptSchedule;
71 DataTable rsTemp1;
72
73 int nSize = saryResourceNames.Count;
74 if (nSize == 0)
75 {
76 return rsOut;
77 }
78
79 string sResName;
80 //TODO: Optimize: no need to keep looping through resources.
81 // for each resource
82 for (int i = 0; i < nSize; i++)
83 {
84 sResName = saryResourceNames[i].ToString();
85 //Gets all the slots (or Availabities, or AV Blocks if you like)
86 rsSlotSchedule = CGSchedLib.CreateAssignedSlotSchedule(docManager, sResName, StartTime, EndTime, saryApptTypes,/**/ stType, sSearchInfo);
87
88 //if we have slots
89 if (rsSlotSchedule.Rows.Count > 0 )
90 {
91 // Get appointment count to substract from the slots
92 rsApptSchedule = CGSchedLib.CreateAppointmentSlotSchedule(docManager, sResName, StartTime, EndTime, stType);
93
94 // Perform the substraction
95 rsTemp1 = CGSchedLib.SubtractSlotsRS2(rsSlotSchedule, rsApptSchedule, sResName);
96
97 }
98 //otherwise, just return the slot schedule we have.
99 else
100 {
101 rsTemp1 = rsSlotSchedule;
102
103 }
104
105 // if only one resource was passed in, its availablility is what we want
106 if (i == 0)
107 {
108 rsOut = rsTemp1;
109
110 }
111 // if more than one resource, merge them together
112 else
113 {
114 rsOut = CGSchedLib.UnionBlocks(rsTemp1, rsOut);
115 }
116 }
117 return rsOut;
118 }
119
120
121 /* NOT USED ANYMORE!!!
122 public static DataTable CreateAssignedTypeSchedule(CGDocumentManager docManager, string sResourceName, DateTime StartTime, DateTime EndTime, ScheduleType stType)
123 {
124
125 string sStart = FMDateTime.Create(StartTime).DateOnly.FMDateString;
126 string sEnd = FMDateTime.Create(EndTime).DateOnly.FMDateString;
127 string sSql = "BSDX TYPE BLOCKS OVERLAP^" + sStart + "^" + sEnd + "^" + sResourceName;
128
129 DataTable rs = docManager.RPMSDataTable(sSql, "AssignedTypeSchedule");
130
131 if (rs.Rows.Count == 0)
132 return rs;
133
134 DataTable rsCopy = new DataTable("rsCopy");
135 DataColumn dCol = new DataColumn();
136 dCol.DataType = Type.GetType("System.DateTime");
137 dCol.ColumnName = "StartTime";
138 dCol.ReadOnly = true;
139 dCol.AllowDBNull = false;
140 dCol.Unique = false;
141 rsCopy.Columns.Add(dCol);
142
143 dCol = new DataColumn();
144 dCol.DataType = Type.GetType("System.DateTime");
145 dCol.ColumnName = "EndTime";
146 dCol.ReadOnly = true;
147 dCol.AllowDBNull = false;
148 dCol.Unique = false;
149 rsCopy.Columns.Add(dCol);
150
151 dCol = new DataColumn();
152 dCol.DataType = Type.GetType("System.Int16");
153 dCol.ColumnName = "AppointmentTypeID";
154 dCol.ReadOnly = true;
155 dCol.AllowDBNull = false;
156 dCol.Unique = false;
157 rsCopy.Columns.Add(dCol);
158
159 dCol = new DataColumn();
160 dCol.DataType = Type.GetType("System.Int32");
161 dCol.ColumnName = "AvailabilityID";
162 dCol.ReadOnly = true;
163 dCol.AllowDBNull = false;
164 dCol.Unique = false;
165 rsCopy.Columns.Add(dCol);
166
167 dCol = new DataColumn();
168 dCol.DataType = Type.GetType("System.String");
169 dCol.ColumnName = "ResourceName";
170 dCol.ReadOnly = true;
171 dCol.AllowDBNull = false;
172 dCol.Unique = false;
173 rsCopy.Columns.Add(dCol);
174
175
176 DateTime dLastEnd;
177 DateTime dStart;
178 DateTime dEnd;
179 DataRow rNew; //Temporary Holding place for first or last row
180
181 // Get Last Date from final row
182 rNew = rs.Rows[rs.Rows.Count - 1];
183 dLastEnd = (DateTime) rNew["EndTime"];
184 // Get First Date from first row
185 rNew = rs.Rows[0];
186 dStart = (DateTime) rNew["StartTime"];
187
188 long UNSPECIFIED_TYPE = 10;
189 long UNSPECIFIED_ID = 0;
190
191 //if first block in vgetrows starts later than StartTime,
192 // then pad with a new block
193
194 // if First Row time is later than the StartTime param (should always be true)
195 // then make a new row whose time starts from StartTime and ends with dStart
196 if (dStart > StartTime)
197 {
198 dEnd = dStart;
199 rNew = rsCopy.NewRow();
200 rNew["StartTime"] = StartTime;
201 rNew["EndTime"] = dEnd;
202 rNew["AppointmentTypeID"] = UNSPECIFIED_TYPE;
203 rNew["AvailabilityID"] = UNSPECIFIED_ID;
204 rNew["ResourceName"] = sResourceName;
205 rsCopy.Rows.Add(rNew);
206 }
207
208 //if first block start time is < StartTime then trim (shouldn't happen)
209 if (dStart < StartTime)
210 {
211 rNew = rs.Rows[0];
212 rNew["StartTime"] = StartTime;
213 dStart = StartTime;
214 }
215
216 int nAppointmentTypeID;
217 int nAvailabilityID;
218
219 //dStart holds the first date for the availabilities returned from RPMS
220 dEnd = dStart;
221 foreach (DataRow rEach in rs.Rows)
222 {
223 dStart = (DateTime) rEach["StartTime"];
224 if (dStart > dEnd)
225 {
226 rNew = rsCopy.NewRow();
227 rNew["StartTime"] = dEnd;
228 rNew["EndTime"] = dStart;
229 rNew["AppointmentTypeID"] = 0;
230 rNew["AvailabilityID"] = UNSPECIFIED_ID;
231 rNew["ResourceName"] = sResourceName;
232 rsCopy.Rows.Add(rNew);
233 }
234
235 //dEnd now EndTime for AV Block
236 dEnd = (DateTime) rEach["EndTime"];
237
238 // if dEnd is greater than endime, set dEnd to be the same as EndTime.
239 if (dEnd > EndTime) { dEnd = EndTime; }
240
241
242 nAppointmentTypeID = (int) rEach["AppointmentTypeID"];
243 nAvailabilityID = (int) rEach["AvailabilityID"];
244
245 rNew = rsCopy.NewRow();
246 rNew["StartTime"] = dStart;
247 rNew["EndTime"] = dEnd;
248 rNew["AppointmentTypeID"] = nAppointmentTypeID;
249 rNew["AvailabilityID"] = nAvailabilityID;
250 rNew["ResourceName"] = sResourceName;
251 rsCopy.Rows.Add(rNew);
252 }
253
254 //Pad the end if necessary
255 if (dLastEnd < EndTime)
256 {
257 rNew = rsCopy.NewRow();
258 rNew["StartTime"] = dLastEnd;
259 rNew["EndTime"] = EndTime;
260 rNew["AppointmentTypeID"] = UNSPECIFIED_TYPE;
261 rNew["AvailabilityID"] = UNSPECIFIED_ID;
262 rNew["ResourceName"] = sResourceName;
263 rsCopy.Rows.Add(rNew);
264 }
265
266 return rsCopy;
267 }
268 */
269
270 /// <summary>
271 /// Gets the Availability Slots from the Server
272 /// </summary>
273 /// <param name="docManager">God Class</param>
274 /// <param name="sResourceName">Resource for which to get slots</param>
275 /// <param name="StartTime"></param>
276 /// <param name="EndTime"></param>
277 /// <param name="rsaryApptTypeIDs">Access Type IDs to retrieve</param>
278 /// <param name="stType">Not used</param>
279 /// <param name="sSearchInfo">If performing a slot search (i.e. for empty appointments), has search info here. Used by Find Appointments</param>
280 /// <returns>DataTable with the following Columns:
281 /// D00030START_TIME^D00030END_TIME^I00010SLOTS^T00030RESOURCE^T00010ACCESS_TYPE^T00250NOTE^I00030AVAILABILITYID
282 /// </returns>
283 public static DataTable CreateAssignedSlotSchedule(CGDocumentManager docManager, string sResourceName, DateTime StartTime,
284 DateTime EndTime, ArrayList rsaryApptTypeIDs, /**/ ScheduleType stType, string sSearchInfo)
285 {
286 //Appointment type ids is now always "" so that all appointment types are returned.
287 string sApptTypeIDs = "";
288
289 //flatten types by '|'
290 int nSize = rsaryApptTypeIDs.Count; //nSize is used to decide where to put the '|' sent in the RPC as we flatten sApptTypeIDs
291 for (int i=0; i < nSize; i++)
292 {
293 sApptTypeIDs += rsaryApptTypeIDs[i];
294 if (i < (nSize-1))
295 sApptTypeIDs += "|";
296 }
297
298 string sStart = FMDateTime.Create(StartTime).DateOnly.FMDateString;
299 string sEnd = FMDateTime.Create(EndTime).FMDateString;
300 string sSql = "BSDX CREATE ASGND SLOT SCHED^" + sResourceName + "^" + sStart + "^" + sEnd + "^" + sApptTypeIDs + "^" + sSearchInfo; //+ "^" + sSTType ;
301 DataTable dtRet = docManager.RPMSDataTable(sSql, "AssignedSlotSchedule");
302
303 return dtRet;
304 }
305
306 public static DataTable CreateCopyTable()
307 {
308 DataTable dtCopy = new DataTable("dtCopy");
309 DataColumn dCol = new DataColumn();
310 dCol.DataType = Type.GetType("System.DateTime");
311 dCol.ColumnName = "START_TIME";
312 dCol.ReadOnly = true;
313 dCol.AllowDBNull = false;
314 dCol.Unique = false;
315 dtCopy.Columns.Add(dCol);
316
317 dCol = new DataColumn();
318 dCol.DataType = Type.GetType("System.DateTime");
319 dCol.ColumnName = "END_TIME";
320 dCol.ReadOnly = true;
321 dCol.AllowDBNull = false;
322 dCol.Unique = false;
323 dtCopy.Columns.Add(dCol);
324
325 dCol = new DataColumn();
326 dCol.DataType = Type.GetType("System.Int16");
327 dCol.ColumnName = "SLOTS";
328 dCol.ReadOnly = true;
329 dCol.AllowDBNull = false;
330 dCol.Unique = false;
331 dtCopy.Columns.Add(dCol);
332
333 dCol = new DataColumn();
334 dCol.DataType = Type.GetType("System.String");
335 dCol.ColumnName = "RESOURCE";
336 dCol.ReadOnly = true;
337 dCol.AllowDBNull = true;
338 dCol.Unique = false;
339 dtCopy.Columns.Add(dCol);
340
341 dCol = new DataColumn();
342 dCol.DataType = Type.GetType("System.String");
343 dCol.ColumnName = "ACCESS_TYPE";
344 dCol.ReadOnly = true;
345 dCol.AllowDBNull = true;
346 dCol.Unique = false;
347 dtCopy.Columns.Add(dCol);
348
349 dCol = new DataColumn();
350 dCol.DataType = Type.GetType("System.String");
351 dCol.ColumnName = "NOTE";
352 dCol.ReadOnly = true;
353 dCol.AllowDBNull = true;
354 dCol.Unique = false;
355 dtCopy.Columns.Add(dCol);
356
357
358 return dtCopy;
359 }
360
361 /// <summary>
362 /// This gets a datatable which shows the appointments between start and end time, one row per appointment
363 /// </summary>
364 /// <param name="docManager"></param>
365 /// <param name="sResourceName"></param>
366 /// <param name="StartTime"></param>
367 /// <param name="EndTime"></param>
368 /// <param name="stType"></param>
369 /// <returns>DataTable with 4 columns: START_TIME, END_TIME, SLOTS, RESOURCE </returns>
370 public static DataTable CreateAppointmentSlotSchedule(CGDocumentManager docManager, string sResourceName, DateTime StartTime, DateTime EndTime, ScheduleType stType)
371 {
372 //Change Dates to FM Format
373 string sStart = FMDateTime.Create(StartTime).DateOnly.FMDateString;
374 string sEnd = FMDateTime.Create(EndTime).DateOnly.FMDateString;
375
376 string sSTType = (stType == ScheduleType.Resource ? "ST_RESOURCE" : "ST_CLINIC");
377 string sSql = "BSDX APPT BLOCKS OVERLAP^" + sStart + "^" + sEnd + "^" + sResourceName ;//+ "^" + sSTType;
378
379 //This gets you a table with 2 columns containing start and end time for each appt
380 //Each appt gets its own row
381 DataTable dtRet = docManager.RPMSDataTable(sSql, "AppointmentSlotSchedule");
382
383 if (dtRet.Rows.Count < 1)
384 return dtRet;
385
386 //Create CDateTimeArray & load records from rsOut
387 int nRC = dtRet.Rows.Count; // nRC is row count from Appointments table just retrieved
388 ArrayList cdtArray = new ArrayList();
389 cdtArray.Capacity = (nRC * 2); //new ArrayList has capacity double that of appointment table
390 DateTime v;
391 int i = 0;
392
393 //for each row in the Appointment Table, ArrayList cdtArray gets 2 entries: Start and End times
394 foreach (DataRow r in dtRet.Rows)
395 {
396 v = (DateTime) r[dtRet.Columns["START_TIME"]];
397 cdtArray.Add(v);
398 v = (DateTime) r[dtRet.Columns["END_TIME"]];
399 cdtArray.Add(v);
400 }
401
402 //Sort start and end times (for use in ScheduleFromArray method)
403 cdtArray.Sort();
404
405 //Create a CTimeBlockArray and load it from rsOut
406
407 //Now, create a new ArrayList with the size of the appointment table to hold availabilities
408 ArrayList ctbAppointments = new ArrayList(nRC);
409 CGAvailability cTB;
410 i = 0;
411 //For each appointment, create an availability
412 foreach (DataRow r in dtRet.Rows)
413 {
414 cTB = new CGAvailability();
415 cTB.StartTime = (DateTime) r[dtRet.Columns["START_TIME"]];
416 cTB.EndTime = (DateTime) r[dtRet.Columns["END_TIME"]];
417 ctbAppointments.Add(cTB);
418 }
419
420 //Create a TimeBlock Array from the data in the DateTime array
421 ArrayList ctbApptSchedule = new ArrayList();
422
423 //Convert Appointments to Availabilities, where all appointments become squeezed together.
424 ScheduleFromArray(cdtArray, StartTime, EndTime, ref ctbApptSchedule);
425
426 /*So far, we have the following:
427 * dtRet -> List of Appointments Start and End times, one row per appointment
428 * cdtArray -> Linear 1 dimensional Array of dtRet Start and End times, sorted
429 * ctbAppointments -> Arraylist of dtRet as Availabilities
430 * ctbApptSchedule -> Arraylist of dtRet as Availabilities with no overlapping boundaries
431 * (overlaps in appointments get converted into availabilties themselves: so
432 * 2 appts as 10:10-10:30 and 10:20-10:40 get converted into 10:10-10:20,
433 * 10:20-10:30, 10:30-10:40).
434 */
435
436 //Find number of TimeBlocks in ctbApptSchedule that overlap the TimeBlocks in ctbAppointments
437 ArrayList ctbApptSchedule2 = new ArrayList();
438 CGAvailability cTB2;
439 int nSlots = 0;
440 for (i=0; i< ctbApptSchedule.Count; i++) //for each non-overlapping appts as availabilities
441 {
442 cTB = (CGAvailability) ctbApptSchedule[i];
443 //How many times does the non-overlapping part show up in an appointment slot?
444 nSlots = BlocksOverlap(cTB, ctbAppointments);
445 cTB2 = new CGAvailability();
446 cTB2.Create(cTB.StartTime, cTB.EndTime, nSlots);
447 ctbApptSchedule2.Add(cTB2);
448 }
449
450 ConsolidateBlocks(ctbApptSchedule2);
451
452 DataTable dtCopy = new DataTable("dtCopy");
453 DataColumn dCol = new DataColumn();
454 dCol.DataType = Type.GetType("System.DateTime");
455 dCol.ColumnName = "START_TIME";
456 dCol.ReadOnly = true;
457 dCol.AllowDBNull = false;
458 dCol.Unique = false;
459 dtCopy.Columns.Add(dCol);
460
461 dCol = new DataColumn();
462 dCol.DataType = Type.GetType("System.DateTime");
463 dCol.ColumnName = "END_TIME";
464 dCol.ReadOnly = true;
465 dCol.AllowDBNull = false;
466 dCol.Unique = false;
467 dtCopy.Columns.Add(dCol);
468
469 dCol = new DataColumn();
470 dCol.DataType = Type.GetType("System.Int16");
471 dCol.ColumnName = "SLOTS";
472 dCol.ReadOnly = true;
473 dCol.AllowDBNull = false;
474 dCol.Unique = false;
475 dtCopy.Columns.Add(dCol);
476
477 dCol = new DataColumn();
478 dCol.DataType = Type.GetType("System.String");
479 dCol.ColumnName = "RESOURCE";
480 dCol.ReadOnly = true;
481 dCol.AllowDBNull = false;
482 dCol.Unique = false;
483 dtCopy.Columns.Add(dCol);
484
485 for (int k=0; k < ctbApptSchedule2.Count; k++)
486 {
487 cTB = (CGAvailability) ctbApptSchedule2[k];
488 DataRow newRow;
489 newRow = dtCopy.NewRow();
490 newRow["START_TIME"] = cTB.StartTime;
491 newRow["END_TIME"] = cTB.EndTime;
492 newRow["SLOTS"] = cTB.Slots;
493 newRow["RESOURCE"] = sResourceName;
494 dtCopy.Rows.Add(newRow);
495 }
496
497 return dtCopy;
498
499 }
500
501 public static int BlocksOverlap(CGAvailability rBlock, ArrayList rTBArray)
502 {
503 //this overload implements default false for bCountSlots
504 return CGSchedLib.BlocksOverlap(rBlock, rTBArray, false);
505 }
506 public static int BlocksOverlap(CGAvailability rBlock, ArrayList rTBArray, bool bCountSlots)
507 {
508 //If bCountSlots == true, then returns
509 //sum of Slots in overlapping blocks
510 //instead of the count of overlapping blocks
511
512 DateTime dStart1;
513 DateTime dStart2;
514 DateTime dEnd1;
515 DateTime dEnd2;
516 int nSlots;
517 int nCount = 0;
518 CGAvailability cBlock;
519
520 dStart1 = rBlock.StartTime;
521 dEnd1 = rBlock.EndTime;
522
523 for (int j=0; j< rTBArray.Count; j++)
524 {
525 cBlock = (CGAvailability) rTBArray[j];
526 dStart2 = cBlock.StartTime;
527 dEnd2 = cBlock.EndTime;
528 nSlots = cBlock.Slots;
529 if (TimesOverlap(dStart1, dEnd1, dStart2, dEnd2))
530 {
531 if (bCountSlots == true)
532 {
533 nCount += nSlots;
534 }
535 else
536 {
537 nCount++;
538 }
539 }
540 }
541
542 return nCount;
543 }
544
545 /// <summary>
546 /// Does an appointment overlap with another appointment in the same day?
547 /// </summary>
548 /// <param name="dStart1">Start Time of First Appt</param>
549 /// <param name="dEnd1">End Time of First Appt</param>
550 /// <param name="dStart2">Start Time of Second Appt</param>
551 /// <param name="dEnd2">End Time of Second Appt</param>
552 /// <returns>true or false</returns>
553 /// <remarks>Draws 2 rectangles and sees if they overlap using minutes from 1980 as the start point</remarks>
554 public static bool TimesOverlap(DateTime dStart1, DateTime dEnd1, DateTime dStart2, DateTime dEnd2)
555 {
556 Rectangle rect1 = new Rectangle();
557 Rectangle rect2 = new Rectangle();
558 rect1.X = 0;
559 rect2.X = 0;
560 rect1.Width = 1;
561 rect2.Width = 1;
562
563 rect1.Y = CGSchedLib.MinSince80(dStart1);
564 rect1.Height = CGSchedLib.MinSince80(dEnd1) - rect1.Y;
565 rect2.Y = CGSchedLib.MinSince80(dStart2);
566 rect2.Height = CGSchedLib.MinSince80(dEnd2) - rect2.Y;
567 bool bRet = rect2.IntersectsWith(rect1);
568 return bRet;
569 }
570
571 public static void ConsolidateBlocks(ArrayList rTBArray)
572 {
573 //TODO: Test this function
574
575 int j = 0;
576 bool bDirty = false;
577 CGAvailability cBlockA;
578 CGAvailability cBlockB;
579 CGAvailability cTemp1;
580 if (rTBArray.Count < 2)
581 return;
582 do
583 {
584 bDirty = false;
585 for (j = 0; j < (rTBArray.Count - 1); j++) //TODO: why minus 1?
586 {
587 cBlockA = (CGAvailability) rTBArray[j];
588 cBlockB = (CGAvailability) rTBArray[j+1];
589 if ((cBlockA.EndTime == cBlockB.StartTime)
590 && (cBlockA.Slots == cBlockB.Slots)
591 && (cBlockA.ResourceList == cBlockB.ResourceList)
592 && (cBlockA.AccessRuleList == cBlockB.AccessRuleList)
593 )
594 {
595 cTemp1 = new CGAvailability();
596 cTemp1.StartTime = cBlockA.StartTime;
597 cTemp1.EndTime = cBlockB.EndTime;
598 cTemp1.Slots = cBlockA.Slots;
599 cTemp1.AccessRuleList = cBlockA.AccessRuleList;
600 cTemp1.ResourceList = cBlockA.ResourceList;
601 rTBArray.Insert(j, cTemp1);
602 rTBArray.RemoveRange(j+1, 2);
603 bDirty = true;
604 break;
605 }
606 }
607 }
608 while (!((bDirty == false) || (rTBArray.Count == 1)));
609 }
610
611 /// <summary>
612 /// My guess is that this calculates remaining slots
613 /// </summary>
614 /// <param name="rsBlocks1"></param>
615 /// <param name="rsBlocks2"></param>
616 /// <param name="sResource"></param>
617 /// <returns></returns>
618 public static DataTable SubtractSlotsRS2(DataTable rsBlocks1, DataTable rsBlocks2, string sResource)
619 {
620 //Subtract slots in rsBlocks2 from rsBlocks1
621 //7-16-01 SUBCLINIC data field in rsBlocks1 persists thru routine.
622 //7-18-01 RESOURCE and ACCESS_TYPE fields presisted
623
624 if ((rsBlocks2.Rows.Count == 0) || (rsBlocks1.Rows.Count == 0))
625 return rsBlocks1;
626
627
628 //Create an array of the start and end times of blocks2
629 ArrayList cdtArray = new ArrayList(2*(rsBlocks1.Rows.Count + rsBlocks2.Rows.Count));
630
631 foreach (DataRow r in rsBlocks1.Rows)
632 {
633 cdtArray.Add(r[rsBlocks1.Columns["START_TIME"]]);
634 cdtArray.Add(r[rsBlocks1.Columns["END_TIME"]]);
635 }
636
637 foreach (DataRow r in rsBlocks2.Rows)
638 {
639 cdtArray.Add(r[rsBlocks2.Columns["START_TIME"]]);
640 cdtArray.Add(r[rsBlocks2.Columns["END_TIME"]]);
641 }
642
643 cdtArray.Sort();
644
645 ArrayList ctbReturn = new ArrayList();
646 DateTime cDate = new DateTime();
647 ScheduleFromArray(cdtArray, cDate, cDate, ref ctbReturn);
648
649 //Set up return table
650 DataTable rsCopy = CGSchedLib.CreateCopyTable(); //TODO: There's a datatable method that does this.
651 long nSlots = 0;
652 CGAvailability cTB;
653
654 for (int j=0; j < (ctbReturn.Count -1); j++) //TODO: why minus 1?
655 {
656 cTB = (CGAvailability) ctbReturn[j];
657 nSlots = SlotsInBlock(cTB, rsBlocks1) - SlotsInBlock(cTB, rsBlocks2);
658 string sResourceList = "";
659 string sAccessRuleList = "";
660 string sNote = "";
661
662 if (nSlots > 0)
663 {
664 bool bRet = ResourceRulesInBlock(cTB, rsBlocks1, ref sResourceList, ref sAccessRuleList, ref sNote);
665 }
666 DataRow newRow;
667 newRow = rsCopy.NewRow();
668 newRow["START_TIME"] = cTB.StartTime;
669 newRow["END_TIME"] = cTB.EndTime;
670 newRow["SLOTS"] = nSlots;
671 //Subclinic, Access Rule and Resource are null in subtractedSlot sets
672 newRow["RESOURCE"] = sResource;
673 newRow["ACCESS_TYPE"] = sAccessRuleList;
674
675 newRow["NOTE"] = sNote;
676
677 rsCopy.Rows.Add(newRow);
678 }
679 return rsCopy;
680
681 }
682
683 public static DataTable UnionBlocks(DataTable rs1, DataTable rs2)
684 {
685 //Test input tables
686 Debug.Assert(rs1 != null);
687 Debug.Assert(rs2 != null);
688
689 DataTable rsCopy;
690 DataRow dr;
691 if (rs1.Columns.Count >= rs2.Columns.Count)
692 {
693 rsCopy = rs1.Copy();
694 foreach (DataRow dr2 in rs2.Rows)
695 {
696 dr = rsCopy.NewRow();
697 dr.ItemArray = dr2.ItemArray;
698 //dr["START_TIME"] = dr2["START_TIME"];
699 //dr["END_TIME"] = dr2["END_TIME"];
700 //dr["SLOTS"] = dr2["SLOTS"];
701 rsCopy.Rows.Add(dr);
702 }
703 }
704 else
705 {
706 rsCopy = rs2.Copy();
707 foreach (DataRow dr2 in rs1.Rows)
708 {
709 dr = rsCopy.NewRow();
710 dr.ItemArray = dr2.ItemArray;
711 rsCopy.Rows.Add(dr);
712 }
713 }
714 return rsCopy;
715 }
716
717 public static DataTable IntersectBlocks(DataTable rs1, DataTable rs2)
718 {
719 DataTable rsCopy = CGSchedLib.CreateCopyTable();
720
721 //Test input tables
722
723 if ((rs1 == null) || (rs2 == null))
724 return rsCopy;
725
726 if ((rs1.Rows.Count == 0) || (rs2.Rows.Count == 0))
727 return rsCopy;
728
729 int nSlots1 = 0;
730 int nSlots2 = 0;
731 int nSlots3 = 0;
732 DateTime dStart1;
733 DateTime dStart2;
734 DateTime dStart3;
735 DateTime dEnd1;
736 DateTime dEnd2;
737 DateTime dEnd3;
738 string sClinic;
739 string sClinic2;
740// Rectangle rect1 = new Rectangle();
741// Rectangle rect2 = new Rectangle();
742// rect1.X = 0;
743// rect2.X = 0;
744// rect1.Width = 1;
745// rect2.Width = 1;
746
747 // DataColumn cSlots = rs1.Columns["SLOTS"];
748 foreach (System.Data.DataRow r1 in rs1.Rows)
749 {
750 nSlots1 = (int) r1[rs1.Columns["SLOTS"]];
751 if (nSlots1 > 0)
752 {
753 dStart1 = (DateTime) r1[rs1.Columns["START_TIME"]];
754 dEnd1 = (DateTime) r1[rs1.Columns["END_TIME"]];
755 sClinic = r1[rs1.Columns["SUBCLINIC"]].ToString();
756 if (sClinic == "NULL")
757 sClinic = "";
758// rect1.Y = CGSchedLib.MinSince80(dStart1);
759// rect1.Height = CGSchedLib.MinSince80(dEnd1) - rect1.Y;
760 foreach (System.Data.DataRow r2 in rs2.Rows)
761 {
762 nSlots2 = (int) r2[rs2.Columns["SLOTS"]];
763
764 if (nSlots2 > 0)
765 {
766 dStart2 = (DateTime) r2[rs2.Columns["START_TIME"]];
767 dEnd2 = (DateTime) r2[rs2.Columns["END_TIME"]];
768 sClinic2 = r2[rs2.Columns["SUBCLINIC"]].ToString();
769// rect2.Y = CGSchedLib.MinSince80(dStart2);
770// rect2.Height = CGSchedLib.MinSince80(dEnd2) - rect2.Y;
771 if (
772 /*(rect2.IntersectsWith(rect1) == true)*/
773 (CGSchedLib.TimesOverlap(dStart1, dEnd1, dStart2, dEnd2) == true)
774 &&
775 ((sClinic == sClinic2) || (sClinic == "NONE") || (sClinic2 == "NONE"))
776 )
777 {
778 dStart3 = (dStart1 >= dStart2) ? dStart1 : dStart2 ;
779 dEnd3 = (dEnd1 >= dEnd2) ? dEnd2 : dEnd1;
780 nSlots3 = (nSlots1 >= nSlots2) ? nSlots2 : nSlots1 ;
781
782 DataRow newRow;
783 newRow = rsCopy.NewRow();
784 newRow["START_TIME"] = dStart3;
785 newRow["END_TIME"] = dEnd3;
786 newRow["SLOTS"] = nSlots3;
787 newRow["SUBCLINIC"] = (sClinic == "NONE") ? sClinic2 : sClinic;
788 //Access Rule and Resource are null in interesected sets
789 newRow["ACCESS_TYPE"] = "";
790 newRow["RESOURCE"] = "";
791 rsCopy.Rows.Add(newRow);
792 }
793 }//nSlots2 > 0
794 }//foreach r2 in rs2.rows
795 }//nSlots1 > 0
796 }//foreach r1 in rs1.rows
797 return rsCopy;
798 }//end IntersectBlocks
799
800 /// <summary>
801 /// Number of minutes since Jan 1 1980
802 /// </summary>
803 /// <param name="d">Date to compare</param>
804 /// <returns>Minutes as integer</returns>
805 public static int MinSince80(DateTime d)
806 {
807 //Returns the total minutes between d and 1 Jan 1980
808 DateTime y = new DateTime(1980,1,1,0,0,0);
809 Debug.Assert(d > y);
810 TimeSpan ts = d - y;
811 //Assure ts.TotalMinutes within int range so that cast on next line works
812 Debug.Assert(ts.TotalMinutes < 2147483646);
813 int nMinutes = (int) ts.TotalMinutes;
814 return nMinutes;
815 }
816
817 /// <summary>
818 /// Converts an Array of Times like this:
819 /// 10:00 10:00 10:20 10:20 10:30 10:30 10:30 10:40
820 /// To an array of availabilities like this:
821 /// 12:00-10:00 10:00-10:20 10:20-10:30 10:30-10:40
822 /// Where the 12:00 comes from the start time
823 /// </summary>
824 /// <param name="cdtArray">ArrayList containing start and end times of Appointments combmined and sorted</param>
825 /// <param name="dStartTime">Start Time for Schedule</param>
826 /// <param name="dEndTime">End Time for Schedule</param>
827 /// <param name="rTBArray">Output of Availabilities: Pass Empty</param>
828 public static void ScheduleFromArray(ArrayList cdtArray, DateTime dStartTime, DateTime dEndTime,
829 ref ArrayList rTBArray)
830 {
831 int j = 0;
832 CGAvailability cTB;
833
834 if (cdtArray.Count == 0)
835 return;
836
837 Debug.Assert(cdtArray.Count > 0);
838 Debug.Assert(cdtArray[0].GetType() == typeof(DateTime));
839
840 //If StartTime passed in, then adjust for it
841 if (dStartTime.Ticks > 0)
842 {
843 // if first Array Entry greater than start time, then create an availability
844 // with the start time as dStartTime, and end time as the first array entry, and 0 slots
845 // then add this to the output array
846 if ((DateTime) cdtArray[0] > dStartTime)
847 {
848 cTB = new CGAvailability();
849 cTB.Create(dStartTime, (DateTime) cdtArray[0], 0);
850 rTBArray.Add(cTB);
851 }
852
853 // if first Array Entry less than start time (shouldn't happen),
854 // convert all input array's times less than the start time to all be the start time
855 if ((DateTime) cdtArray[0] < dStartTime)
856 {
857 for (j = 0; j < cdtArray.Count; j++)
858 {
859 if ((DateTime) cdtArray[j] < dStartTime)
860 cdtArray[j] = dStartTime;
861 }
862 }
863 }
864
865 //Trim the end if necessary
866 //If end time is passed, set all the times in the original array greater
867 //than the end time to be the end time (Shouldn't happen).
868 if (dEndTime.Ticks > 0)
869 {
870 for (j = 0; j < cdtArray.Count; j++)
871 {
872 if ((DateTime) cdtArray[j] > dEndTime)
873 cdtArray[j] = dEndTime;
874 }
875 }
876
877 //build the schedule in rTBArray
878 DateTime dTemp = new DateTime(); //hold previous appt time.
879 DateTime dStart;
880 DateTime dEnd;
881 int k = 0;
882 //for each time in appointment array
883 for (j = 0; j < (cdtArray.Count -1); j++) // -1 b/c k below starts with j+1.
884 {
885 if ((DateTime) cdtArray[j] != dTemp)
886 {
887 dStart =(DateTime) cdtArray[j];
888 dTemp = dStart;
889
890 //for each time in appointment array, starting with the next one
891 for (k = j+1; k < cdtArray.Count; k++)
892 {
893 dEnd = new DateTime();
894 if ((DateTime) cdtArray[k] != dStart) //if the next time isn't the same
895 {
896 dEnd = (DateTime) cdtArray[k]; // set the end to be the next time
897 }
898 if (dEnd.Ticks > 0) //make a new availability and add it to the output array.
899 {
900 cTB = new CGAvailability();
901 cTB.Create(dStart, dEnd, 0);
902 rTBArray.Add(cTB);
903 break;
904 }
905 // if the end time is still empty, loop
906 }
907 }
908 }
909
910 }//end ScheduleFromArray
911
912 //long CResourceLink::SlotsInBlock(CTimeBlock &rTimeBlock, _RecordsetPtr rsBlock)
913 public static int SlotsInBlock(CGAvailability rTimeBlock, DataTable rsBlock)
914 {
915 DateTime dStart1;
916 DateTime dStart2;
917 DateTime dEnd1;
918 DateTime dEnd2;
919 int nSlots = 0;
920
921 if (rsBlock.Rows.Count == 0)
922 return nSlots;
923
924 Rectangle rect1 = new Rectangle();
925 Rectangle rect2 = new Rectangle();
926 rect1.X = 0;
927 rect2.X = 0;
928 rect1.Width = 1;
929 rect2.Width = 1;
930
931 dStart1 = rTimeBlock.StartTime;
932 dEnd1 = rTimeBlock.EndTime;
933 rect1.Y = CGSchedLib.MinSince80(dStart1);
934 rect1.Height = CGSchedLib.MinSince80(dEnd1) - rect1.Y;
935
936 //for each row in the availability schedule
937 foreach (DataRow r in rsBlock.Rows)
938 {
939 dStart2 = (DateTime) r[rsBlock.Columns["START_TIME"]];
940 dEnd2 = (DateTime) r[rsBlock.Columns["END_TIME"]];
941
942 rect2.Y = CGSchedLib.MinSince80(dStart2);
943 rect2.Height = CGSchedLib.MinSince80(dEnd2) - rect2.Y;
944 if (rect2.IntersectsWith(rect1) == true)
945 {
946 string sSlots = r[rsBlock.Columns["SLOTS"]].ToString();
947 nSlots = System.Convert.ToInt16(sSlots);
948// nSlots = (int) r[rsBlock.Columns["SLOTS"]];
949 break;
950 }
951 }
952 return nSlots;
953 }//end SlotsInBlock
954
955 public static string ClinicInBlock(CGAvailability rTimeBlock, DataTable rsBlock)
956 {
957 DateTime dStart1;
958 DateTime dStart2;
959 DateTime dEnd1;
960 DateTime dEnd2;
961 string sClinic = "";
962
963 if (rsBlock.Rows.Count == 0)
964 return sClinic;
965
966 Rectangle rect1 = new Rectangle();
967 Rectangle rect2 = new Rectangle();
968 rect1.X = 0;
969 rect2.X = 0;
970 rect1.Width = 1;
971 rect2.Width = 1;
972
973 dStart1 = rTimeBlock.StartTime;
974 dEnd1 = rTimeBlock.EndTime;
975 rect1.Y = CGSchedLib.MinSince80(dStart1);
976 rect1.Height = CGSchedLib.MinSince80(dEnd1) - rect1.Y;
977
978 foreach (DataRow r in rsBlock.Rows)
979 {
980 dStart2 = (DateTime) r[rsBlock.Columns["START_TIME"]];
981 dEnd2 = (DateTime) r[rsBlock.Columns["END_TIME"]];
982
983 rect2.Y = CGSchedLib.MinSince80(dStart2);
984 rect2.Height = CGSchedLib.MinSince80(dEnd2) - rect2.Y;
985 if (rect2.IntersectsWith(rect1) == true)
986 {
987 sClinic = r[rsBlock.Columns["SUBCLINIC"]].ToString();
988 break;
989 }
990 }
991 return sClinic;
992 }//end ClinicInBlock
993
994 public static bool ResourceRulesInBlock(CGAvailability rTimeBlock, DataTable rsBlock, ref string sResourceList, ref string sAccessRuleList, ref string sNote)
995 {
996 DateTime dStart1;
997 DateTime dStart2;
998 DateTime dEnd1;
999 DateTime dEnd2;
1000 string sResource;
1001 string sAccessRule;
1002
1003 if (rsBlock.Rows.Count == 0)
1004 return true;
1005
1006 Rectangle rect1 = new Rectangle();
1007 Rectangle rect2 = new Rectangle();
1008 rect1.X = 0;
1009 rect2.X = 0;
1010 rect1.Width = 1;
1011 rect2.Width = 1;
1012
1013 dStart1 = rTimeBlock.StartTime;
1014 dEnd1 = rTimeBlock.EndTime;
1015 rect1.Y = CGSchedLib.MinSince80(dStart1);
1016 rect1.Height = CGSchedLib.MinSince80(dEnd1) - rect1.Y;
1017
1018 foreach (DataRow r in rsBlock.Rows)
1019 {
1020 dStart2 = (DateTime) r[rsBlock.Columns["START_TIME"]];
1021 dEnd2 = (DateTime) r[rsBlock.Columns["END_TIME"]];
1022
1023 rect2.Y = CGSchedLib.MinSince80(dStart2);
1024 rect2.Height = CGSchedLib.MinSince80(dEnd2) - rect2.Y;
1025 if (rect2.IntersectsWith(rect1) == true)
1026 {
1027 sResource = r[rsBlock.Columns["RESOURCE"]].ToString();
1028 if (sResource == "NULL")
1029 sResource = "";
1030 if (sResource != "")
1031 {
1032 if (sResourceList == "")
1033 {
1034 sResourceList += sResource;
1035 }
1036 else
1037 {
1038 sResourceList += "^" + sResource;
1039 }
1040 }
1041 sAccessRule = r[rsBlock.Columns["ACCESS_TYPE"]].ToString();
1042 if (sAccessRule == "0")
1043 sAccessRule = "";
1044 if (sAccessRule != "")
1045 {
1046 if (sAccessRuleList == "")
1047 {
1048 sAccessRuleList += sAccessRule;
1049 }
1050 else
1051 {
1052 sAccessRuleList += "^" + sAccessRule;
1053 }
1054 }
1055 sNote = r[rsBlock.Columns["NOTE"]].ToString();
1056
1057 }
1058 }
1059 return true;
1060 }//End ResourceRulesInBlock
1061
1062
1063 }
1064}
Note: See TracBrowser for help on using the repository browser.